summaryrefslogtreecommitdiffstats
path: root/sound/pci/mixart
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:49:45 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:49:45 +0000
commit2c3c1048746a4622d8c89a29670120dc8fab93c4 (patch)
tree848558de17fb3008cdf4d861b01ac7781903ce39 /sound/pci/mixart
parentInitial commit. (diff)
downloadlinux-2c3c1048746a4622d8c89a29670120dc8fab93c4.tar.xz
linux-2c3c1048746a4622d8c89a29670120dc8fab93c4.zip
Adding upstream version 6.1.76.upstream/6.1.76
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sound/pci/mixart')
-rw-r--r--sound/pci/mixart/Makefile9
-rw-r--r--sound/pci/mixart/mixart.c1408
-rw-r--r--sound/pci/mixart/mixart.h207
-rw-r--r--sound/pci/mixart/mixart_core.c589
-rw-r--r--sound/pci/mixart/mixart_core.h566
-rw-r--r--sound/pci/mixart/mixart_hwdep.c586
-rw-r--r--sound/pci/mixart/mixart_hwdep.h142
-rw-r--r--sound/pci/mixart/mixart_mixer.c1192
-rw-r--r--sound/pci/mixart/mixart_mixer.h18
9 files changed, 4717 insertions, 0 deletions
diff --git a/sound/pci/mixart/Makefile b/sound/pci/mixart/Makefile
new file mode 100644
index 000000000..16cfeb78a
--- /dev/null
+++ b/sound/pci/mixart/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
+#
+
+snd-mixart-objs := mixart.o mixart_core.o mixart_hwdep.o mixart_mixer.o
+
+obj-$(CONFIG_SND_MIXART) += snd-mixart.o
diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c
new file mode 100644
index 000000000..1b078b789
--- /dev/null
+++ b/sound/pci/mixart/mixart.c
@@ -0,0 +1,1408 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Digigram miXart soundcards
+ *
+ * main file with alsa callbacks
+ *
+ * Copyright (c) 2003 by Digigram <alsa@digigram.com>
+ */
+
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "mixart.h"
+#include "mixart_hwdep.h"
+#include "mixart_core.h"
+#include "mixart_mixer.h"
+
+#define CARD_NAME "miXart"
+
+MODULE_AUTHOR("Digigram <alsa@digigram.com>");
+MODULE_DESCRIPTION("Digigram " CARD_NAME);
+MODULE_LICENSE("GPL");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for Digigram " CARD_NAME " soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for Digigram " CARD_NAME " soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Digigram " CARD_NAME " soundcard.");
+
+/*
+ */
+
+static const struct pci_device_id snd_mixart_ids[] = {
+ { PCI_VDEVICE(MOTOROLA, 0x0003), 0, }, /* MC8240 */
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_mixart_ids);
+
+
+static int mixart_set_pipe_state(struct mixart_mgr *mgr,
+ struct mixart_pipe *pipe, int start)
+{
+ struct mixart_group_state_req group_state;
+ struct mixart_group_state_resp group_state_resp;
+ struct mixart_msg request;
+ int err;
+ u32 system_msg_uid;
+
+ switch(pipe->status) {
+ case PIPE_RUNNING:
+ case PIPE_CLOCK_SET:
+ if(start) return 0; /* already started */
+ break;
+ case PIPE_STOPPED:
+ if(!start) return 0; /* already stopped */
+ break;
+ default:
+ dev_err(&mgr->pci->dev,
+ "error mixart_set_pipe_state called with wrong pipe->status!\n");
+ return -EINVAL; /* function called with wrong pipe status */
+ }
+
+ system_msg_uid = 0x12345678; /* the event ! (take care: the MSB and two LSB's have to be 0) */
+
+ /* wait on the last MSG_SYSTEM_SEND_SYNCHRO_CMD command to be really finished */
+
+ request.message_id = MSG_SYSTEM_WAIT_SYNCHRO_CMD;
+ request.uid = (struct mixart_uid){0,0};
+ request.data = &system_msg_uid;
+ request.size = sizeof(system_msg_uid);
+
+ err = snd_mixart_send_msg_wait_notif(mgr, &request, system_msg_uid);
+ if(err) {
+ dev_err(&mgr->pci->dev,
+ "error : MSG_SYSTEM_WAIT_SYNCHRO_CMD was not notified !\n");
+ return err;
+ }
+
+ /* start or stop the pipe (1 pipe) */
+
+ memset(&group_state, 0, sizeof(group_state));
+ group_state.pipe_count = 1;
+ group_state.pipe_uid[0] = pipe->group_uid;
+
+ if(start)
+ request.message_id = MSG_STREAM_START_STREAM_GRP_PACKET;
+ else
+ request.message_id = MSG_STREAM_STOP_STREAM_GRP_PACKET;
+
+ request.uid = pipe->group_uid; /*(struct mixart_uid){0,0};*/
+ request.data = &group_state;
+ request.size = sizeof(group_state);
+
+ err = snd_mixart_send_msg(mgr, &request, sizeof(group_state_resp), &group_state_resp);
+ if (err < 0 || group_state_resp.txx_status != 0) {
+ dev_err(&mgr->pci->dev,
+ "error MSG_STREAM_ST***_STREAM_GRP_PACKET err=%x stat=%x !\n",
+ err, group_state_resp.txx_status);
+ return -EINVAL;
+ }
+
+ if(start) {
+ u32 stat = 0;
+
+ group_state.pipe_count = 0; /* in case of start same command once again with pipe_count=0 */
+
+ err = snd_mixart_send_msg(mgr, &request, sizeof(group_state_resp), &group_state_resp);
+ if (err < 0 || group_state_resp.txx_status != 0) {
+ dev_err(&mgr->pci->dev,
+ "error MSG_STREAM_START_STREAM_GRP_PACKET err=%x stat=%x !\n",
+ err, group_state_resp.txx_status);
+ return -EINVAL;
+ }
+
+ /* in case of start send a synchro top */
+
+ request.message_id = MSG_SYSTEM_SEND_SYNCHRO_CMD;
+ request.uid = (struct mixart_uid){0,0};
+ request.data = NULL;
+ request.size = 0;
+
+ err = snd_mixart_send_msg(mgr, &request, sizeof(stat), &stat);
+ if (err < 0 || stat != 0) {
+ dev_err(&mgr->pci->dev,
+ "error MSG_SYSTEM_SEND_SYNCHRO_CMD err=%x stat=%x !\n",
+ err, stat);
+ return -EINVAL;
+ }
+
+ pipe->status = PIPE_RUNNING;
+ }
+ else /* !start */
+ pipe->status = PIPE_STOPPED;
+
+ return 0;
+}
+
+
+static int mixart_set_clock(struct mixart_mgr *mgr,
+ struct mixart_pipe *pipe, unsigned int rate)
+{
+ struct mixart_msg request;
+ struct mixart_clock_properties clock_properties;
+ struct mixart_clock_properties_resp clock_prop_resp;
+ int err;
+
+ switch(pipe->status) {
+ case PIPE_CLOCK_SET:
+ break;
+ case PIPE_RUNNING:
+ if(rate != 0)
+ break;
+ fallthrough;
+ default:
+ if(rate == 0)
+ return 0; /* nothing to do */
+ else {
+ dev_err(&mgr->pci->dev,
+ "error mixart_set_clock(%d) called with wrong pipe->status !\n",
+ rate);
+ return -EINVAL;
+ }
+ }
+
+ memset(&clock_properties, 0, sizeof(clock_properties));
+ clock_properties.clock_generic_type = (rate != 0) ? CGT_INTERNAL_CLOCK : CGT_NO_CLOCK;
+ clock_properties.clock_mode = CM_STANDALONE;
+ clock_properties.frequency = rate;
+ clock_properties.nb_callers = 1; /* only one entry in uid_caller ! */
+ clock_properties.uid_caller[0] = pipe->group_uid;
+
+ dev_dbg(&mgr->pci->dev, "mixart_set_clock to %d kHz\n", rate);
+
+ request.message_id = MSG_CLOCK_SET_PROPERTIES;
+ request.uid = mgr->uid_console_manager;
+ request.data = &clock_properties;
+ request.size = sizeof(clock_properties);
+
+ err = snd_mixart_send_msg(mgr, &request, sizeof(clock_prop_resp), &clock_prop_resp);
+ if (err < 0 || clock_prop_resp.status != 0 || clock_prop_resp.clock_mode != CM_STANDALONE) {
+ dev_err(&mgr->pci->dev,
+ "error MSG_CLOCK_SET_PROPERTIES err=%x stat=%x mod=%x !\n",
+ err, clock_prop_resp.status, clock_prop_resp.clock_mode);
+ return -EINVAL;
+ }
+
+ if(rate) pipe->status = PIPE_CLOCK_SET;
+ else pipe->status = PIPE_RUNNING;
+
+ return 0;
+}
+
+
+/*
+ * Allocate or reference output pipe for analog IOs (pcmp0/1)
+ */
+struct mixart_pipe *
+snd_mixart_add_ref_pipe(struct snd_mixart *chip, int pcm_number, int capture,
+ int monitoring)
+{
+ int stream_count;
+ struct mixart_pipe *pipe;
+ struct mixart_msg request;
+
+ if(capture) {
+ if (pcm_number == MIXART_PCM_ANALOG) {
+ pipe = &(chip->pipe_in_ana); /* analog inputs */
+ } else {
+ pipe = &(chip->pipe_in_dig); /* digital inputs */
+ }
+ request.message_id = MSG_STREAM_ADD_OUTPUT_GROUP;
+ stream_count = MIXART_CAPTURE_STREAMS;
+ } else {
+ if (pcm_number == MIXART_PCM_ANALOG) {
+ pipe = &(chip->pipe_out_ana); /* analog outputs */
+ } else {
+ pipe = &(chip->pipe_out_dig); /* digital outputs */
+ }
+ request.message_id = MSG_STREAM_ADD_INPUT_GROUP;
+ stream_count = MIXART_PLAYBACK_STREAMS;
+ }
+
+ /* a new stream is opened and there are already all streams in use */
+ if( (monitoring == 0) && (pipe->references >= stream_count) ) {
+ return NULL;
+ }
+
+ /* pipe is not yet defined */
+ if( pipe->status == PIPE_UNDEFINED ) {
+ int err, i;
+ struct {
+ struct mixart_streaming_group_req sgroup_req;
+ struct mixart_streaming_group sgroup_resp;
+ } *buf;
+
+ dev_dbg(chip->card->dev,
+ "add_ref_pipe audio chip(%d) pcm(%d)\n",
+ chip->chip_idx, pcm_number);
+
+ buf = kmalloc(sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ return NULL;
+
+ request.uid = (struct mixart_uid){0,0}; /* should be StreamManagerUID, but zero is OK if there is only one ! */
+ request.data = &buf->sgroup_req;
+ request.size = sizeof(buf->sgroup_req);
+
+ memset(&buf->sgroup_req, 0, sizeof(buf->sgroup_req));
+
+ buf->sgroup_req.stream_count = stream_count;
+ buf->sgroup_req.channel_count = 2;
+ buf->sgroup_req.latency = 256;
+ buf->sgroup_req.connector = pipe->uid_left_connector; /* the left connector */
+
+ for (i=0; i<stream_count; i++) {
+ int j;
+ struct mixart_flowinfo *flowinfo;
+ struct mixart_bufferinfo *bufferinfo;
+
+ /* we don't yet know the format, so config 16 bit pcm audio for instance */
+ buf->sgroup_req.stream_info[i].size_max_byte_frame = 1024;
+ buf->sgroup_req.stream_info[i].size_max_sample_frame = 256;
+ buf->sgroup_req.stream_info[i].nb_bytes_max_per_sample = MIXART_FLOAT_P__4_0_TO_HEX; /* is 4.0f */
+
+ /* find the right bufferinfo_array */
+ j = (chip->chip_idx * MIXART_MAX_STREAM_PER_CARD) + (pcm_number * (MIXART_PLAYBACK_STREAMS + MIXART_CAPTURE_STREAMS)) + i;
+ if(capture) j += MIXART_PLAYBACK_STREAMS; /* in the array capture is behind playback */
+
+ buf->sgroup_req.flow_entry[i] = j;
+
+ flowinfo = (struct mixart_flowinfo *)chip->mgr->flowinfo.area;
+ flowinfo[j].bufferinfo_array_phy_address = (u32)chip->mgr->bufferinfo.addr + (j * sizeof(struct mixart_bufferinfo));
+ flowinfo[j].bufferinfo_count = 1; /* 1 will set the miXart to ring-buffer mode ! */
+
+ bufferinfo = (struct mixart_bufferinfo *)chip->mgr->bufferinfo.area;
+ bufferinfo[j].buffer_address = 0; /* buffer is not yet allocated */
+ bufferinfo[j].available_length = 0; /* buffer is not yet allocated */
+
+ /* construct the identifier of the stream buffer received in the interrupts ! */
+ bufferinfo[j].buffer_id = (chip->chip_idx << MIXART_NOTIFY_CARD_OFFSET) + (pcm_number << MIXART_NOTIFY_PCM_OFFSET ) + i;
+ if(capture) {
+ bufferinfo[j].buffer_id |= MIXART_NOTIFY_CAPT_MASK;
+ }
+ }
+
+ err = snd_mixart_send_msg(chip->mgr, &request, sizeof(buf->sgroup_resp), &buf->sgroup_resp);
+ if((err < 0) || (buf->sgroup_resp.status != 0)) {
+ dev_err(chip->card->dev,
+ "error MSG_STREAM_ADD_**PUT_GROUP err=%x stat=%x !\n",
+ err, buf->sgroup_resp.status);
+ kfree(buf);
+ return NULL;
+ }
+
+ pipe->group_uid = buf->sgroup_resp.group; /* id of the pipe, as returned by embedded */
+ pipe->stream_count = buf->sgroup_resp.stream_count;
+ /* pipe->stream_uid[i] = buf->sgroup_resp.stream[i].stream_uid; */
+
+ pipe->status = PIPE_STOPPED;
+ kfree(buf);
+ }
+
+ if(monitoring) pipe->monitoring = 1;
+ else pipe->references++;
+
+ return pipe;
+}
+
+
+int snd_mixart_kill_ref_pipe(struct mixart_mgr *mgr,
+ struct mixart_pipe *pipe, int monitoring)
+{
+ int err = 0;
+
+ if(pipe->status == PIPE_UNDEFINED)
+ return 0;
+
+ if(monitoring)
+ pipe->monitoring = 0;
+ else
+ pipe->references--;
+
+ if((pipe->references <= 0) && (pipe->monitoring == 0)) {
+
+ struct mixart_msg request;
+ struct mixart_delete_group_resp delete_resp;
+
+ /* release the clock */
+ err = mixart_set_clock( mgr, pipe, 0);
+ if( err < 0 ) {
+ dev_err(&mgr->pci->dev,
+ "mixart_set_clock(0) return error!\n");
+ }
+
+ /* stop the pipe */
+ err = mixart_set_pipe_state(mgr, pipe, 0);
+ if( err < 0 ) {
+ dev_err(&mgr->pci->dev, "error stopping pipe!\n");
+ }
+
+ request.message_id = MSG_STREAM_DELETE_GROUP;
+ request.uid = (struct mixart_uid){0,0};
+ request.data = &pipe->group_uid; /* the streaming group ! */
+ request.size = sizeof(pipe->group_uid);
+
+ /* delete the pipe */
+ err = snd_mixart_send_msg(mgr, &request, sizeof(delete_resp), &delete_resp);
+ if ((err < 0) || (delete_resp.status != 0)) {
+ dev_err(&mgr->pci->dev,
+ "error MSG_STREAM_DELETE_GROUP err(%x), status(%x)\n",
+ err, delete_resp.status);
+ }
+
+ pipe->group_uid = (struct mixart_uid){0,0};
+ pipe->stream_count = 0;
+ pipe->status = PIPE_UNDEFINED;
+ }
+
+ return err;
+}
+
+static int mixart_set_stream_state(struct mixart_stream *stream, int start)
+{
+ struct snd_mixart *chip;
+ struct mixart_stream_state_req stream_state_req;
+ struct mixart_msg request;
+
+ if(!stream->substream)
+ return -EINVAL;
+
+ memset(&stream_state_req, 0, sizeof(stream_state_req));
+ stream_state_req.stream_count = 1;
+ stream_state_req.stream_info.stream_desc.uid_pipe = stream->pipe->group_uid;
+ stream_state_req.stream_info.stream_desc.stream_idx = stream->substream->number;
+
+ if (stream->substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ request.message_id = start ? MSG_STREAM_START_INPUT_STAGE_PACKET : MSG_STREAM_STOP_INPUT_STAGE_PACKET;
+ else
+ request.message_id = start ? MSG_STREAM_START_OUTPUT_STAGE_PACKET : MSG_STREAM_STOP_OUTPUT_STAGE_PACKET;
+
+ request.uid = (struct mixart_uid){0,0};
+ request.data = &stream_state_req;
+ request.size = sizeof(stream_state_req);
+
+ stream->abs_period_elapsed = 0; /* reset stream pos */
+ stream->buf_periods = 0;
+ stream->buf_period_frag = 0;
+
+ chip = snd_pcm_substream_chip(stream->substream);
+
+ return snd_mixart_send_msg_nonblock(chip->mgr, &request);
+}
+
+/*
+ * Trigger callback
+ */
+
+static int snd_mixart_trigger(struct snd_pcm_substream *subs, int cmd)
+{
+ struct mixart_stream *stream = subs->runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+
+ dev_dbg(subs->pcm->card->dev, "SNDRV_PCM_TRIGGER_START\n");
+
+ /* START_STREAM */
+ if( mixart_set_stream_state(stream, 1) )
+ return -EINVAL;
+
+ stream->status = MIXART_STREAM_STATUS_RUNNING;
+
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+
+ /* STOP_STREAM */
+ if( mixart_set_stream_state(stream, 0) )
+ return -EINVAL;
+
+ stream->status = MIXART_STREAM_STATUS_OPEN;
+
+ dev_dbg(subs->pcm->card->dev, "SNDRV_PCM_TRIGGER_STOP\n");
+
+ break;
+
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ /* TODO */
+ stream->status = MIXART_STREAM_STATUS_PAUSE;
+ dev_dbg(subs->pcm->card->dev, "SNDRV_PCM_PAUSE_PUSH\n");
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ /* TODO */
+ stream->status = MIXART_STREAM_STATUS_RUNNING;
+ dev_dbg(subs->pcm->card->dev, "SNDRV_PCM_PAUSE_RELEASE\n");
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int mixart_sync_nonblock_events(struct mixart_mgr *mgr)
+{
+ unsigned long timeout = jiffies + HZ;
+ while (atomic_read(&mgr->msg_processed) > 0) {
+ if (time_after(jiffies, timeout)) {
+ dev_err(&mgr->pci->dev,
+ "mixart: cannot process nonblock events!\n");
+ return -EBUSY;
+ }
+ schedule_timeout_uninterruptible(1);
+ }
+ return 0;
+}
+
+/*
+ * prepare callback for all pcms
+ */
+static int snd_mixart_prepare(struct snd_pcm_substream *subs)
+{
+ struct snd_mixart *chip = snd_pcm_substream_chip(subs);
+ struct mixart_stream *stream = subs->runtime->private_data;
+
+ /* TODO de façon non bloquante, réappliquer les hw_params (rate, bits, codec) */
+
+ dev_dbg(chip->card->dev, "snd_mixart_prepare\n");
+
+ mixart_sync_nonblock_events(chip->mgr);
+
+ /* only the first stream can choose the sample rate */
+ /* the further opened streams will be limited to its frequency (see open) */
+ if(chip->mgr->ref_count_rate == 1)
+ chip->mgr->sample_rate = subs->runtime->rate;
+
+ /* set the clock only once (first stream) on the same pipe */
+ if(stream->pipe->references == 1) {
+ if( mixart_set_clock(chip->mgr, stream->pipe, subs->runtime->rate) )
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+static int mixart_set_format(struct mixart_stream *stream, snd_pcm_format_t format)
+{
+ int err;
+ struct snd_mixart *chip;
+ struct mixart_msg request;
+ struct mixart_stream_param_desc stream_param;
+ struct mixart_return_uid resp;
+
+ chip = snd_pcm_substream_chip(stream->substream);
+
+ memset(&stream_param, 0, sizeof(stream_param));
+
+ stream_param.coding_type = CT_LINEAR;
+ stream_param.number_of_channel = stream->channels;
+
+ stream_param.sampling_freq = chip->mgr->sample_rate;
+ if(stream_param.sampling_freq == 0)
+ stream_param.sampling_freq = 44100; /* if frequency not yet defined, use some default */
+
+ switch(format){
+ case SNDRV_PCM_FORMAT_U8:
+ stream_param.sample_type = ST_INTEGER_8;
+ stream_param.sample_size = 8;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ stream_param.sample_type = ST_INTEGER_16LE;
+ stream_param.sample_size = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S16_BE:
+ stream_param.sample_type = ST_INTEGER_16BE;
+ stream_param.sample_size = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ stream_param.sample_type = ST_INTEGER_24LE;
+ stream_param.sample_size = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S24_3BE:
+ stream_param.sample_type = ST_INTEGER_24BE;
+ stream_param.sample_size = 24;
+ break;
+ case SNDRV_PCM_FORMAT_FLOAT_LE:
+ stream_param.sample_type = ST_FLOATING_POINT_32LE;
+ stream_param.sample_size = 32;
+ break;
+ case SNDRV_PCM_FORMAT_FLOAT_BE:
+ stream_param.sample_type = ST_FLOATING_POINT_32BE;
+ stream_param.sample_size = 32;
+ break;
+ default:
+ dev_err(chip->card->dev,
+ "error mixart_set_format() : unknown format\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(chip->card->dev,
+ "set SNDRV_PCM_FORMAT sample_type(%d) sample_size(%d) freq(%d) channels(%d)\n",
+ stream_param.sample_type, stream_param.sample_size, stream_param.sampling_freq, stream->channels);
+
+ /* TODO: what else to configure ? */
+ /* stream_param.samples_per_frame = 2; */
+ /* stream_param.bytes_per_frame = 4; */
+ /* stream_param.bytes_per_sample = 2; */
+
+ stream_param.pipe_count = 1; /* set to 1 */
+ stream_param.stream_count = 1; /* set to 1 */
+ stream_param.stream_desc[0].uid_pipe = stream->pipe->group_uid;
+ stream_param.stream_desc[0].stream_idx = stream->substream->number;
+
+ request.message_id = MSG_STREAM_SET_INPUT_STAGE_PARAM;
+ request.uid = (struct mixart_uid){0,0};
+ request.data = &stream_param;
+ request.size = sizeof(stream_param);
+
+ err = snd_mixart_send_msg(chip->mgr, &request, sizeof(resp), &resp);
+ if((err < 0) || resp.error_code) {
+ dev_err(chip->card->dev,
+ "MSG_STREAM_SET_INPUT_STAGE_PARAM err=%x; resp=%x\n",
+ err, resp.error_code);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+/*
+ * HW_PARAMS callback for all pcms
+ */
+static int snd_mixart_hw_params(struct snd_pcm_substream *subs,
+ struct snd_pcm_hw_params *hw)
+{
+ struct snd_mixart *chip = snd_pcm_substream_chip(subs);
+ struct mixart_mgr *mgr = chip->mgr;
+ struct mixart_stream *stream = subs->runtime->private_data;
+ snd_pcm_format_t format;
+ int err;
+ int channels;
+
+ /* set up channels */
+ channels = params_channels(hw);
+
+ /* set up format for the stream */
+ format = params_format(hw);
+
+ mutex_lock(&mgr->setup_mutex);
+
+ /* update the stream levels */
+ if( stream->pcm_number <= MIXART_PCM_DIGITAL ) {
+ int is_aes = stream->pcm_number > MIXART_PCM_ANALOG;
+ if( subs->stream == SNDRV_PCM_STREAM_PLAYBACK )
+ mixart_update_playback_stream_level(chip, is_aes, subs->number);
+ else
+ mixart_update_capture_stream_level( chip, is_aes);
+ }
+
+ stream->channels = channels;
+
+ /* set the format to the board */
+ err = mixart_set_format(stream, format);
+ if(err < 0) {
+ mutex_unlock(&mgr->setup_mutex);
+ return err;
+ }
+
+ if (subs->runtime->buffer_changed) {
+ struct mixart_bufferinfo *bufferinfo;
+ int i = (chip->chip_idx * MIXART_MAX_STREAM_PER_CARD) + (stream->pcm_number * (MIXART_PLAYBACK_STREAMS+MIXART_CAPTURE_STREAMS)) + subs->number;
+ if( subs->stream == SNDRV_PCM_STREAM_CAPTURE ) {
+ i += MIXART_PLAYBACK_STREAMS; /* in array capture is behind playback */
+ }
+
+ bufferinfo = (struct mixart_bufferinfo *)chip->mgr->bufferinfo.area;
+ bufferinfo[i].buffer_address = subs->runtime->dma_addr;
+ bufferinfo[i].available_length = subs->runtime->dma_bytes;
+ /* bufferinfo[i].buffer_id is already defined */
+
+ dev_dbg(chip->card->dev,
+ "snd_mixart_hw_params(pcm %d) : dma_addr(%x) dma_bytes(%x) subs-number(%d)\n",
+ i, bufferinfo[i].buffer_address,
+ bufferinfo[i].available_length,
+ subs->number);
+ }
+ mutex_unlock(&mgr->setup_mutex);
+
+ return 0;
+}
+
+static int snd_mixart_hw_free(struct snd_pcm_substream *subs)
+{
+ struct snd_mixart *chip = snd_pcm_substream_chip(subs);
+ mixart_sync_nonblock_events(chip->mgr);
+ return 0;
+}
+
+
+
+/*
+ * TODO CONFIGURATION SPACE for all pcms, mono pcm must update channels_max
+ */
+static const struct snd_pcm_hardware snd_mixart_analog_caps =
+{
+ .info = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE),
+ .formats = ( SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |
+ SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE ),
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (32*1024),
+ .period_bytes_min = 256, /* 256 frames U8 mono*/
+ .period_bytes_max = (16*1024),
+ .periods_min = 2,
+ .periods_max = (32*1024/256),
+};
+
+static const struct snd_pcm_hardware snd_mixart_digital_caps =
+{
+ .info = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE),
+ .formats = ( SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |
+ SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE ),
+ .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+ .rate_min = 32000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (32*1024),
+ .period_bytes_min = 256, /* 256 frames U8 mono*/
+ .period_bytes_max = (16*1024),
+ .periods_min = 2,
+ .periods_max = (32*1024/256),
+};
+
+
+static int snd_mixart_playback_open(struct snd_pcm_substream *subs)
+{
+ struct snd_mixart *chip = snd_pcm_substream_chip(subs);
+ struct mixart_mgr *mgr = chip->mgr;
+ struct snd_pcm_runtime *runtime = subs->runtime;
+ struct snd_pcm *pcm = subs->pcm;
+ struct mixart_stream *stream;
+ struct mixart_pipe *pipe;
+ int err = 0;
+ int pcm_number;
+
+ mutex_lock(&mgr->setup_mutex);
+
+ if ( pcm == chip->pcm ) {
+ pcm_number = MIXART_PCM_ANALOG;
+ runtime->hw = snd_mixart_analog_caps;
+ } else {
+ snd_BUG_ON(pcm != chip->pcm_dig);
+ pcm_number = MIXART_PCM_DIGITAL;
+ runtime->hw = snd_mixart_digital_caps;
+ }
+ dev_dbg(chip->card->dev,
+ "snd_mixart_playback_open C%d/P%d/Sub%d\n",
+ chip->chip_idx, pcm_number, subs->number);
+
+ /* get stream info */
+ stream = &(chip->playback_stream[pcm_number][subs->number]);
+
+ if (stream->status != MIXART_STREAM_STATUS_FREE){
+ /* streams in use */
+ dev_err(chip->card->dev,
+ "snd_mixart_playback_open C%d/P%d/Sub%d in use\n",
+ chip->chip_idx, pcm_number, subs->number);
+ err = -EBUSY;
+ goto _exit_open;
+ }
+
+ /* get pipe pointer (out pipe) */
+ pipe = snd_mixart_add_ref_pipe(chip, pcm_number, 0, 0);
+
+ if (pipe == NULL) {
+ err = -EINVAL;
+ goto _exit_open;
+ }
+
+ /* start the pipe if necessary */
+ err = mixart_set_pipe_state(chip->mgr, pipe, 1);
+ if( err < 0 ) {
+ dev_err(chip->card->dev, "error starting pipe!\n");
+ snd_mixart_kill_ref_pipe(chip->mgr, pipe, 0);
+ err = -EINVAL;
+ goto _exit_open;
+ }
+
+ stream->pipe = pipe;
+ stream->pcm_number = pcm_number;
+ stream->status = MIXART_STREAM_STATUS_OPEN;
+ stream->substream = subs;
+ stream->channels = 0; /* not configured yet */
+
+ runtime->private_data = stream;
+
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 64);
+
+ /* if a sample rate is already used, another stream cannot change */
+ if(mgr->ref_count_rate++) {
+ if(mgr->sample_rate) {
+ runtime->hw.rate_min = runtime->hw.rate_max = mgr->sample_rate;
+ }
+ }
+
+ _exit_open:
+ mutex_unlock(&mgr->setup_mutex);
+
+ return err;
+}
+
+
+static int snd_mixart_capture_open(struct snd_pcm_substream *subs)
+{
+ struct snd_mixart *chip = snd_pcm_substream_chip(subs);
+ struct mixart_mgr *mgr = chip->mgr;
+ struct snd_pcm_runtime *runtime = subs->runtime;
+ struct snd_pcm *pcm = subs->pcm;
+ struct mixart_stream *stream;
+ struct mixart_pipe *pipe;
+ int err = 0;
+ int pcm_number;
+
+ mutex_lock(&mgr->setup_mutex);
+
+ if ( pcm == chip->pcm ) {
+ pcm_number = MIXART_PCM_ANALOG;
+ runtime->hw = snd_mixart_analog_caps;
+ } else {
+ snd_BUG_ON(pcm != chip->pcm_dig);
+ pcm_number = MIXART_PCM_DIGITAL;
+ runtime->hw = snd_mixart_digital_caps;
+ }
+
+ runtime->hw.channels_min = 2; /* for instance, no mono */
+
+ dev_dbg(chip->card->dev, "snd_mixart_capture_open C%d/P%d/Sub%d\n",
+ chip->chip_idx, pcm_number, subs->number);
+
+ /* get stream info */
+ stream = &(chip->capture_stream[pcm_number]);
+
+ if (stream->status != MIXART_STREAM_STATUS_FREE){
+ /* streams in use */
+ dev_err(chip->card->dev,
+ "snd_mixart_capture_open C%d/P%d/Sub%d in use\n",
+ chip->chip_idx, pcm_number, subs->number);
+ err = -EBUSY;
+ goto _exit_open;
+ }
+
+ /* get pipe pointer (in pipe) */
+ pipe = snd_mixart_add_ref_pipe(chip, pcm_number, 1, 0);
+
+ if (pipe == NULL) {
+ err = -EINVAL;
+ goto _exit_open;
+ }
+
+ /* start the pipe if necessary */
+ err = mixart_set_pipe_state(chip->mgr, pipe, 1);
+ if( err < 0 ) {
+ dev_err(chip->card->dev, "error starting pipe!\n");
+ snd_mixart_kill_ref_pipe(chip->mgr, pipe, 0);
+ err = -EINVAL;
+ goto _exit_open;
+ }
+
+ stream->pipe = pipe;
+ stream->pcm_number = pcm_number;
+ stream->status = MIXART_STREAM_STATUS_OPEN;
+ stream->substream = subs;
+ stream->channels = 0; /* not configured yet */
+
+ runtime->private_data = stream;
+
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 64);
+
+ /* if a sample rate is already used, another stream cannot change */
+ if(mgr->ref_count_rate++) {
+ if(mgr->sample_rate) {
+ runtime->hw.rate_min = runtime->hw.rate_max = mgr->sample_rate;
+ }
+ }
+
+ _exit_open:
+ mutex_unlock(&mgr->setup_mutex);
+
+ return err;
+}
+
+
+
+static int snd_mixart_close(struct snd_pcm_substream *subs)
+{
+ struct snd_mixart *chip = snd_pcm_substream_chip(subs);
+ struct mixart_mgr *mgr = chip->mgr;
+ struct mixart_stream *stream = subs->runtime->private_data;
+
+ mutex_lock(&mgr->setup_mutex);
+
+ dev_dbg(chip->card->dev, "snd_mixart_close C%d/P%d/Sub%d\n",
+ chip->chip_idx, stream->pcm_number, subs->number);
+
+ /* sample rate released */
+ if(--mgr->ref_count_rate == 0) {
+ mgr->sample_rate = 0;
+ }
+
+ /* delete pipe */
+ if (snd_mixart_kill_ref_pipe(mgr, stream->pipe, 0 ) < 0) {
+
+ dev_err(chip->card->dev,
+ "error snd_mixart_kill_ref_pipe C%dP%d\n",
+ chip->chip_idx, stream->pcm_number);
+ }
+
+ stream->pipe = NULL;
+ stream->status = MIXART_STREAM_STATUS_FREE;
+ stream->substream = NULL;
+
+ mutex_unlock(&mgr->setup_mutex);
+ return 0;
+}
+
+
+static snd_pcm_uframes_t snd_mixart_stream_pointer(struct snd_pcm_substream *subs)
+{
+ struct snd_pcm_runtime *runtime = subs->runtime;
+ struct mixart_stream *stream = runtime->private_data;
+
+ return (snd_pcm_uframes_t)((stream->buf_periods * runtime->period_size) + stream->buf_period_frag);
+}
+
+
+
+static const struct snd_pcm_ops snd_mixart_playback_ops = {
+ .open = snd_mixart_playback_open,
+ .close = snd_mixart_close,
+ .prepare = snd_mixart_prepare,
+ .hw_params = snd_mixart_hw_params,
+ .hw_free = snd_mixart_hw_free,
+ .trigger = snd_mixart_trigger,
+ .pointer = snd_mixart_stream_pointer,
+};
+
+static const struct snd_pcm_ops snd_mixart_capture_ops = {
+ .open = snd_mixart_capture_open,
+ .close = snd_mixart_close,
+ .prepare = snd_mixart_prepare,
+ .hw_params = snd_mixart_hw_params,
+ .hw_free = snd_mixart_hw_free,
+ .trigger = snd_mixart_trigger,
+ .pointer = snd_mixart_stream_pointer,
+};
+
+static void preallocate_buffers(struct snd_mixart *chip, struct snd_pcm *pcm)
+{
+#if 0
+ struct snd_pcm_substream *subs;
+ int stream;
+
+ for (stream = 0; stream < 2; stream++) {
+ int idx = 0;
+ for (subs = pcm->streams[stream].substream; subs; subs = subs->next, idx++)
+ /* set up the unique device id with the chip index */
+ subs->dma_device.id = subs->pcm->device << 16 |
+ subs->stream << 8 | (subs->number + 1) |
+ (chip->chip_idx + 1) << 24;
+ }
+#endif
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &chip->mgr->pci->dev,
+ 32*1024, 32*1024);
+}
+
+/*
+ */
+static int snd_mixart_pcm_analog(struct snd_mixart *chip)
+{
+ int err;
+ struct snd_pcm *pcm;
+ char name[32];
+
+ sprintf(name, "miXart analog %d", chip->chip_idx);
+ err = snd_pcm_new(chip->card, name, MIXART_PCM_ANALOG,
+ MIXART_PLAYBACK_STREAMS,
+ MIXART_CAPTURE_STREAMS, &pcm);
+ if (err < 0) {
+ dev_err(chip->card->dev,
+ "cannot create the analog pcm %d\n", chip->chip_idx);
+ return err;
+ }
+
+ pcm->private_data = chip;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_mixart_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mixart_capture_ops);
+
+ pcm->info_flags = 0;
+ pcm->nonatomic = true;
+ strcpy(pcm->name, name);
+
+ preallocate_buffers(chip, pcm);
+
+ chip->pcm = pcm;
+ return 0;
+}
+
+
+/*
+ */
+static int snd_mixart_pcm_digital(struct snd_mixart *chip)
+{
+ int err;
+ struct snd_pcm *pcm;
+ char name[32];
+
+ sprintf(name, "miXart AES/EBU %d", chip->chip_idx);
+ err = snd_pcm_new(chip->card, name, MIXART_PCM_DIGITAL,
+ MIXART_PLAYBACK_STREAMS,
+ MIXART_CAPTURE_STREAMS, &pcm);
+ if (err < 0) {
+ dev_err(chip->card->dev,
+ "cannot create the digital pcm %d\n", chip->chip_idx);
+ return err;
+ }
+
+ pcm->private_data = chip;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_mixart_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mixart_capture_ops);
+
+ pcm->info_flags = 0;
+ pcm->nonatomic = true;
+ strcpy(pcm->name, name);
+
+ preallocate_buffers(chip, pcm);
+
+ chip->pcm_dig = pcm;
+ return 0;
+}
+
+static int snd_mixart_chip_free(struct snd_mixart *chip)
+{
+ kfree(chip);
+ return 0;
+}
+
+static int snd_mixart_chip_dev_free(struct snd_device *device)
+{
+ struct snd_mixart *chip = device->device_data;
+ return snd_mixart_chip_free(chip);
+}
+
+
+/*
+ */
+static int snd_mixart_create(struct mixart_mgr *mgr, struct snd_card *card, int idx)
+{
+ int err;
+ struct snd_mixart *chip;
+ static const struct snd_device_ops ops = {
+ .dev_free = snd_mixart_chip_dev_free,
+ };
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->card = card;
+ chip->chip_idx = idx;
+ chip->mgr = mgr;
+ card->sync_irq = mgr->irq;
+
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (err < 0) {
+ snd_mixart_chip_free(chip);
+ return err;
+ }
+
+ mgr->chip[idx] = chip;
+ return 0;
+}
+
+int snd_mixart_create_pcm(struct snd_mixart* chip)
+{
+ int err;
+
+ err = snd_mixart_pcm_analog(chip);
+ if (err < 0)
+ return err;
+
+ if(chip->mgr->board_type == MIXART_DAUGHTER_TYPE_AES) {
+
+ err = snd_mixart_pcm_digital(chip);
+ if (err < 0)
+ return err;
+ }
+ return err;
+}
+
+
+/*
+ * release all the cards assigned to a manager instance
+ */
+static int snd_mixart_free(struct mixart_mgr *mgr)
+{
+ unsigned int i;
+
+ for (i = 0; i < mgr->num_cards; i++) {
+ if (mgr->chip[i])
+ snd_card_free(mgr->chip[i]->card);
+ }
+
+ /* stop mailbox */
+ snd_mixart_exit_mailbox(mgr);
+
+ /* release irq */
+ if (mgr->irq >= 0)
+ free_irq(mgr->irq, mgr);
+
+ /* reset board if some firmware was loaded */
+ if(mgr->dsp_loaded) {
+ snd_mixart_reset_board(mgr);
+ dev_dbg(&mgr->pci->dev, "reset miXart !\n");
+ }
+
+ /* release the i/o ports */
+ for (i = 0; i < 2; ++i)
+ iounmap(mgr->mem[i].virt);
+
+ pci_release_regions(mgr->pci);
+
+ /* free flowarray */
+ if(mgr->flowinfo.area) {
+ snd_dma_free_pages(&mgr->flowinfo);
+ mgr->flowinfo.area = NULL;
+ }
+ /* free bufferarray */
+ if(mgr->bufferinfo.area) {
+ snd_dma_free_pages(&mgr->bufferinfo);
+ mgr->bufferinfo.area = NULL;
+ }
+
+ pci_disable_device(mgr->pci);
+ kfree(mgr);
+ return 0;
+}
+
+/*
+ * proc interface
+ */
+
+/*
+ mixart_BA0 proc interface for BAR 0 - read callback
+ */
+static ssize_t snd_mixart_BA0_read(struct snd_info_entry *entry,
+ void *file_private_data,
+ struct file *file, char __user *buf,
+ size_t count, loff_t pos)
+{
+ struct mixart_mgr *mgr = entry->private_data;
+
+ count = count & ~3; /* make sure the read size is a multiple of 4 bytes */
+ if (copy_to_user_fromio(buf, MIXART_MEM(mgr, pos), count))
+ return -EFAULT;
+ return count;
+}
+
+/*
+ mixart_BA1 proc interface for BAR 1 - read callback
+ */
+static ssize_t snd_mixart_BA1_read(struct snd_info_entry *entry,
+ void *file_private_data,
+ struct file *file, char __user *buf,
+ size_t count, loff_t pos)
+{
+ struct mixart_mgr *mgr = entry->private_data;
+
+ count = count & ~3; /* make sure the read size is a multiple of 4 bytes */
+ if (copy_to_user_fromio(buf, MIXART_REG(mgr, pos), count))
+ return -EFAULT;
+ return count;
+}
+
+static const struct snd_info_entry_ops snd_mixart_proc_ops_BA0 = {
+ .read = snd_mixart_BA0_read,
+};
+
+static const struct snd_info_entry_ops snd_mixart_proc_ops_BA1 = {
+ .read = snd_mixart_BA1_read,
+};
+
+
+static void snd_mixart_proc_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_mixart *chip = entry->private_data;
+ u32 ref;
+
+ snd_iprintf(buffer, "Digigram miXart (alsa card %d)\n\n", chip->chip_idx);
+
+ /* stats available when embedded OS is running */
+ if (chip->mgr->dsp_loaded & ( 1 << MIXART_MOTHERBOARD_ELF_INDEX)) {
+ snd_iprintf(buffer, "- hardware -\n");
+ switch (chip->mgr->board_type ) {
+ case MIXART_DAUGHTER_TYPE_NONE : snd_iprintf(buffer, "\tmiXart8 (no daughter board)\n\n"); break;
+ case MIXART_DAUGHTER_TYPE_AES : snd_iprintf(buffer, "\tmiXart8 AES/EBU\n\n"); break;
+ case MIXART_DAUGHTER_TYPE_COBRANET : snd_iprintf(buffer, "\tmiXart8 Cobranet\n\n"); break;
+ default: snd_iprintf(buffer, "\tUNKNOWN!\n\n"); break;
+ }
+
+ snd_iprintf(buffer, "- system load -\n");
+
+ /* get perf reference */
+
+ ref = readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_SYSTEM_LOAD_OFFSET));
+
+ if (ref) {
+ u32 mailbox = 100 * readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_MAILBX_LOAD_OFFSET)) / ref;
+ u32 streaming = 100 * readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_STREAM_LOAD_OFFSET)) / ref;
+ u32 interr = 100 * readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_INTERR_LOAD_OFFSET)) / ref;
+
+ snd_iprintf(buffer, "\tstreaming : %d\n", streaming);
+ snd_iprintf(buffer, "\tmailbox : %d\n", mailbox);
+ snd_iprintf(buffer, "\tinterrupts handling : %d\n\n", interr);
+ }
+ } /* endif elf loaded */
+}
+
+static void snd_mixart_proc_init(struct snd_mixart *chip)
+{
+ struct snd_info_entry *entry;
+
+ /* text interface to read perf and temp meters */
+ snd_card_ro_proc_new(chip->card, "board_info", chip,
+ snd_mixart_proc_read);
+
+ if (! snd_card_proc_new(chip->card, "mixart_BA0", &entry)) {
+ entry->content = SNDRV_INFO_CONTENT_DATA;
+ entry->private_data = chip->mgr;
+ entry->c.ops = &snd_mixart_proc_ops_BA0;
+ entry->size = MIXART_BA0_SIZE;
+ }
+ if (! snd_card_proc_new(chip->card, "mixart_BA1", &entry)) {
+ entry->content = SNDRV_INFO_CONTENT_DATA;
+ entry->private_data = chip->mgr;
+ entry->c.ops = &snd_mixart_proc_ops_BA1;
+ entry->size = MIXART_BA1_SIZE;
+ }
+}
+/* end of proc interface */
+
+
+/*
+ * probe function - creates the card manager
+ */
+static int snd_mixart_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ struct mixart_mgr *mgr;
+ unsigned int i;
+ int err;
+ size_t size;
+
+ /*
+ */
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (! enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ /* enable PCI device */
+ err = pci_enable_device(pci);
+ if (err < 0)
+ return err;
+ pci_set_master(pci);
+
+ /* check if we can restrict PCI DMA transfers to 32 bits */
+ if (dma_set_mask(&pci->dev, DMA_BIT_MASK(32)) < 0) {
+ dev_err(&pci->dev,
+ "architecture does not support 32bit PCI busmaster DMA\n");
+ pci_disable_device(pci);
+ return -ENXIO;
+ }
+
+ /*
+ */
+ mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
+ if (! mgr) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+
+ mgr->pci = pci;
+ mgr->irq = -1;
+
+ /* resource assignment */
+ err = pci_request_regions(pci, CARD_NAME);
+ if (err < 0) {
+ kfree(mgr);
+ pci_disable_device(pci);
+ return err;
+ }
+ for (i = 0; i < 2; i++) {
+ mgr->mem[i].phys = pci_resource_start(pci, i);
+ mgr->mem[i].virt = pci_ioremap_bar(pci, i);
+ if (!mgr->mem[i].virt) {
+ dev_err(&pci->dev, "unable to remap resource 0x%lx\n",
+ mgr->mem[i].phys);
+ snd_mixart_free(mgr);
+ return -EBUSY;
+ }
+ }
+
+ if (request_threaded_irq(pci->irq, snd_mixart_interrupt,
+ snd_mixart_threaded_irq, IRQF_SHARED,
+ KBUILD_MODNAME, mgr)) {
+ dev_err(&pci->dev, "unable to grab IRQ %d\n", pci->irq);
+ snd_mixart_free(mgr);
+ return -EBUSY;
+ }
+ mgr->irq = pci->irq;
+
+ /* init mailbox */
+ mgr->msg_fifo_readptr = 0;
+ mgr->msg_fifo_writeptr = 0;
+
+ mutex_init(&mgr->lock);
+ mutex_init(&mgr->msg_lock);
+ init_waitqueue_head(&mgr->msg_sleep);
+ atomic_set(&mgr->msg_processed, 0);
+
+ /* init setup mutex*/
+ mutex_init(&mgr->setup_mutex);
+
+ /* card assignment */
+ mgr->num_cards = MIXART_MAX_CARDS; /* 4 FIXME: configurable? */
+ for (i = 0; i < mgr->num_cards; i++) {
+ struct snd_card *card;
+ char tmpid[16];
+ int idx;
+
+ if (index[dev] < 0)
+ idx = index[dev];
+ else
+ idx = index[dev] + i;
+ snprintf(tmpid, sizeof(tmpid), "%s-%d", id[dev] ? id[dev] : "MIXART", i);
+ err = snd_card_new(&pci->dev, idx, tmpid, THIS_MODULE,
+ 0, &card);
+
+ if (err < 0) {
+ dev_err(&pci->dev, "cannot allocate the card %d\n", i);
+ snd_mixart_free(mgr);
+ return err;
+ }
+
+ strcpy(card->driver, CARD_NAME);
+ snprintf(card->shortname, sizeof(card->shortname),
+ "Digigram miXart [PCM #%d]", i);
+ snprintf(card->longname, sizeof(card->longname),
+ "Digigram miXart at 0x%lx & 0x%lx, irq %i [PCM #%d]",
+ mgr->mem[0].phys, mgr->mem[1].phys, mgr->irq, i);
+
+ err = snd_mixart_create(mgr, card, i);
+ if (err < 0) {
+ snd_card_free(card);
+ snd_mixart_free(mgr);
+ return err;
+ }
+
+ if(i==0) {
+ /* init proc interface only for chip0 */
+ snd_mixart_proc_init(mgr->chip[i]);
+ }
+
+ err = snd_card_register(card);
+ if (err < 0) {
+ snd_mixart_free(mgr);
+ return err;
+ }
+ }
+
+ /* init firmware status (mgr->dsp_loaded reset in hwdep_new) */
+ mgr->board_type = MIXART_DAUGHTER_TYPE_NONE;
+
+ /* create array of streaminfo */
+ size = PAGE_ALIGN( (MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS *
+ sizeof(struct mixart_flowinfo)) );
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev,
+ size, &mgr->flowinfo) < 0) {
+ snd_mixart_free(mgr);
+ return -ENOMEM;
+ }
+ /* init streaminfo_array */
+ memset(mgr->flowinfo.area, 0, size);
+
+ /* create array of bufferinfo */
+ size = PAGE_ALIGN( (MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS *
+ sizeof(struct mixart_bufferinfo)) );
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev,
+ size, &mgr->bufferinfo) < 0) {
+ snd_mixart_free(mgr);
+ return -ENOMEM;
+ }
+ /* init bufferinfo_array */
+ memset(mgr->bufferinfo.area, 0, size);
+
+ /* set up firmware */
+ err = snd_mixart_setup_firmware(mgr);
+ if (err < 0) {
+ snd_mixart_free(mgr);
+ return err;
+ }
+
+ pci_set_drvdata(pci, mgr);
+ dev++;
+ return 0;
+}
+
+static void snd_mixart_remove(struct pci_dev *pci)
+{
+ snd_mixart_free(pci_get_drvdata(pci));
+}
+
+static struct pci_driver mixart_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = snd_mixart_ids,
+ .probe = snd_mixart_probe,
+ .remove = snd_mixart_remove,
+};
+
+module_pci_driver(mixart_driver);
diff --git a/sound/pci/mixart/mixart.h b/sound/pci/mixart/mixart.h
new file mode 100644
index 000000000..cbed6d9a9
--- /dev/null
+++ b/sound/pci/mixart/mixart.h
@@ -0,0 +1,207 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Driver for Digigram miXart soundcards
+ *
+ * main header file
+ *
+ * Copyright (c) 2003 by Digigram <alsa@digigram.com>
+ */
+
+#ifndef __SOUND_MIXART_H
+#define __SOUND_MIXART_H
+
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <sound/pcm.h>
+
+#define MIXART_DRIVER_VERSION 0x000100 /* 0.1.0 */
+
+
+/*
+ */
+
+struct mixart_uid {
+ u32 object_id;
+ u32 desc;
+};
+
+struct mem_area {
+ unsigned long phys;
+ void __iomem *virt;
+ struct resource *res;
+};
+
+
+struct mixart_route {
+ unsigned char connected;
+ unsigned char phase_inv;
+ int volume;
+};
+
+
+/* firmware status codes */
+#define MIXART_MOTHERBOARD_XLX_INDEX 0
+#define MIXART_MOTHERBOARD_ELF_INDEX 1
+#define MIXART_AESEBUBOARD_XLX_INDEX 2
+#define MIXART_HARDW_FILES_MAX_INDEX 3 /* xilinx, elf, AESEBU xilinx */
+
+#define MIXART_MAX_CARDS 4
+#define MSG_FIFO_SIZE 16
+
+#define MIXART_MAX_PHYS_CONNECTORS (MIXART_MAX_CARDS * 2 * 2) /* 4 * stereo * (analog+digital) */
+
+struct mixart_mgr {
+ unsigned int num_cards;
+ struct snd_mixart *chip[MIXART_MAX_CARDS];
+
+ struct pci_dev *pci;
+
+ int irq;
+
+ /* memory-maps */
+ struct mem_area mem[2];
+
+ /* one and only blocking message or notification may be pending */
+ u32 pending_event;
+ wait_queue_head_t msg_sleep;
+
+ /* messages fifo */
+ u32 msg_fifo[MSG_FIFO_SIZE];
+ int msg_fifo_readptr;
+ int msg_fifo_writeptr;
+ atomic_t msg_processed; /* number of messages to be processed in irq thread */
+
+ struct mutex lock; /* interrupt lock */
+ struct mutex msg_lock; /* mailbox lock */
+
+ struct mutex setup_mutex; /* mutex used in hw_params, open and close */
+
+ /* hardware interface */
+ unsigned int dsp_loaded; /* bit flags of loaded dsp indices */
+ unsigned int board_type; /* read from embedded once elf file is loaded, 250 = miXart8, 251 = with AES, 252 = with Cobranet */
+
+ struct snd_dma_buffer flowinfo;
+ struct snd_dma_buffer bufferinfo;
+
+ struct mixart_uid uid_console_manager;
+ int sample_rate;
+ int ref_count_rate;
+
+ struct mutex mixer_mutex; /* mutex for mixer */
+
+};
+
+
+#define MIXART_STREAM_STATUS_FREE 0
+#define MIXART_STREAM_STATUS_OPEN 1
+#define MIXART_STREAM_STATUS_RUNNING 2
+#define MIXART_STREAM_STATUS_DRAINING 3
+#define MIXART_STREAM_STATUS_PAUSE 4
+
+#define MIXART_PLAYBACK_STREAMS 4
+#define MIXART_CAPTURE_STREAMS 1
+
+#define MIXART_PCM_ANALOG 0
+#define MIXART_PCM_DIGITAL 1
+#define MIXART_PCM_TOTAL 2
+
+#define MIXART_MAX_STREAM_PER_CARD (MIXART_PCM_TOTAL * (MIXART_PLAYBACK_STREAMS + MIXART_CAPTURE_STREAMS) )
+
+
+#define MIXART_NOTIFY_CARD_MASK 0xF000
+#define MIXART_NOTIFY_CARD_OFFSET 12
+#define MIXART_NOTIFY_PCM_MASK 0x0F00
+#define MIXART_NOTIFY_PCM_OFFSET 8
+#define MIXART_NOTIFY_CAPT_MASK 0x0080
+#define MIXART_NOTIFY_SUBS_MASK 0x007F
+
+
+struct mixart_stream {
+ struct snd_pcm_substream *substream;
+ struct mixart_pipe *pipe;
+ int pcm_number;
+
+ int status; /* nothing, running, draining */
+
+ u64 abs_period_elapsed; /* last absolute stream position where period_elapsed was called (multiple of runtime->period_size) */
+ u32 buf_periods; /* periods counter in the buffer (< runtime->periods) */
+ u32 buf_period_frag; /* defines with buf_period_pos the exact position in the buffer (< runtime->period_size) */
+
+ int channels;
+};
+
+
+enum mixart_pipe_status {
+ PIPE_UNDEFINED,
+ PIPE_STOPPED,
+ PIPE_RUNNING,
+ PIPE_CLOCK_SET
+};
+
+struct mixart_pipe {
+ struct mixart_uid group_uid; /* id of the pipe, as returned by embedded */
+ int stream_count;
+ struct mixart_uid uid_left_connector; /* UID's for the audio connectors */
+ struct mixart_uid uid_right_connector;
+ enum mixart_pipe_status status;
+ int references; /* number of subs openned */
+ int monitoring; /* pipe used for monitoring issue */
+};
+
+
+struct snd_mixart {
+ struct snd_card *card;
+ struct mixart_mgr *mgr;
+ int chip_idx; /* zero based */
+ struct snd_hwdep *hwdep; /* DSP loader, only for the first card */
+
+ struct snd_pcm *pcm; /* PCM analog i/o */
+ struct snd_pcm *pcm_dig; /* PCM digital i/o */
+
+ /* allocate stereo pipe for instance */
+ struct mixart_pipe pipe_in_ana;
+ struct mixart_pipe pipe_out_ana;
+
+ /* if AES/EBU daughter board is available, additional pipes possible on pcm_dig */
+ struct mixart_pipe pipe_in_dig;
+ struct mixart_pipe pipe_out_dig;
+
+ struct mixart_stream playback_stream[MIXART_PCM_TOTAL][MIXART_PLAYBACK_STREAMS]; /* 0 = pcm, 1 = pcm_dig */
+ struct mixart_stream capture_stream[MIXART_PCM_TOTAL]; /* 0 = pcm, 1 = pcm_dig */
+
+ /* UID's for the physical io's */
+ struct mixart_uid uid_out_analog_physio;
+ struct mixart_uid uid_in_analog_physio;
+
+ int analog_playback_active[2]; /* Mixer : Master Playback active (!mute) */
+ int analog_playback_volume[2]; /* Mixer : Master Playback Volume */
+ int analog_capture_volume[2]; /* Mixer : Master Capture Volume */
+ int digital_playback_active[2*MIXART_PLAYBACK_STREAMS][2]; /* Mixer : Digital Playback Active [(analog+AES output)*streams][stereo]*/
+ int digital_playback_volume[2*MIXART_PLAYBACK_STREAMS][2]; /* Mixer : Digital Playback Volume [(analog+AES output)*streams][stereo]*/
+ int digital_capture_volume[2][2]; /* Mixer : Digital Capture Volume [analog+AES output][stereo] */
+ int monitoring_active[2]; /* Mixer : Monitoring Active */
+ int monitoring_volume[2]; /* Mixer : Monitoring Volume */
+};
+
+struct mixart_bufferinfo
+{
+ u32 buffer_address;
+ u32 reserved[5];
+ u32 available_length;
+ u32 buffer_id;
+};
+
+struct mixart_flowinfo
+{
+ u32 bufferinfo_array_phy_address;
+ u32 reserved[11];
+ u32 bufferinfo_count;
+ u32 capture;
+};
+
+/* exported */
+int snd_mixart_create_pcm(struct snd_mixart * chip);
+struct mixart_pipe *snd_mixart_add_ref_pipe(struct snd_mixart *chip, int pcm_number, int capture, int monitoring);
+int snd_mixart_kill_ref_pipe(struct mixart_mgr *mgr, struct mixart_pipe *pipe, int monitoring);
+
+#endif /* __SOUND_MIXART_H */
diff --git a/sound/pci/mixart/mixart_core.c b/sound/pci/mixart/mixart_core.c
new file mode 100644
index 000000000..a047ed0f8
--- /dev/null
+++ b/sound/pci/mixart/mixart_core.c
@@ -0,0 +1,589 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Digigram miXart soundcards
+ *
+ * low level interface with interrupt handling and mail box implementation
+ *
+ * Copyright (c) 2003 by Digigram <alsa@digigram.com>
+ */
+
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+
+#include <sound/core.h>
+#include "mixart.h"
+#include "mixart_hwdep.h"
+#include "mixart_core.h"
+
+
+#define MSG_TIMEOUT_JIFFIES (400 * HZ) / 1000 /* 400 ms */
+
+#define MSG_DESCRIPTOR_SIZE 0x24
+#define MSG_HEADER_SIZE (MSG_DESCRIPTOR_SIZE + 4)
+
+#define MSG_TYPE_MASK 0x00000003 /* mask for following types */
+#define MSG_TYPE_NOTIFY 0 /* embedded -> driver (only notification, do not get_msg() !) */
+#define MSG_TYPE_COMMAND 1 /* driver <-> embedded (a command has no answer) */
+#define MSG_TYPE_REQUEST 2 /* driver -> embedded (request will get an answer back) */
+#define MSG_TYPE_ANSWER 3 /* embedded -> driver */
+#define MSG_CANCEL_NOTIFY_MASK 0x80000000 /* this bit is set for a notification that has been canceled */
+
+
+static int retrieve_msg_frame(struct mixart_mgr *mgr, u32 *msg_frame)
+{
+ /* read the message frame fifo */
+ u32 headptr, tailptr;
+
+ tailptr = readl_be(MIXART_MEM(mgr, MSG_OUTBOUND_POST_TAIL));
+ headptr = readl_be(MIXART_MEM(mgr, MSG_OUTBOUND_POST_HEAD));
+
+ if (tailptr == headptr)
+ return 0; /* no message posted */
+
+ if (tailptr < MSG_OUTBOUND_POST_STACK)
+ return 0; /* error */
+ if (tailptr >= MSG_OUTBOUND_POST_STACK + MSG_BOUND_STACK_SIZE)
+ return 0; /* error */
+
+ *msg_frame = readl_be(MIXART_MEM(mgr, tailptr));
+
+ /* increment the tail index */
+ tailptr += 4;
+ if( tailptr >= (MSG_OUTBOUND_POST_STACK+MSG_BOUND_STACK_SIZE) )
+ tailptr = MSG_OUTBOUND_POST_STACK;
+ writel_be(tailptr, MIXART_MEM(mgr, MSG_OUTBOUND_POST_TAIL));
+
+ return 1;
+}
+
+static int get_msg(struct mixart_mgr *mgr, struct mixart_msg *resp,
+ u32 msg_frame_address )
+{
+ u32 headptr;
+ u32 size;
+ int err;
+#ifndef __BIG_ENDIAN
+ unsigned int i;
+#endif
+
+ err = 0;
+
+ /* copy message descriptor from miXart to driver */
+ size = readl_be(MIXART_MEM(mgr, msg_frame_address)); /* size of descriptor + response */
+ resp->message_id = readl_be(MIXART_MEM(mgr, msg_frame_address + 4)); /* dwMessageID */
+ resp->uid.object_id = readl_be(MIXART_MEM(mgr, msg_frame_address + 8)); /* uidDest */
+ resp->uid.desc = readl_be(MIXART_MEM(mgr, msg_frame_address + 12)); /* */
+
+ if( (size < MSG_DESCRIPTOR_SIZE) || (resp->size < (size - MSG_DESCRIPTOR_SIZE))) {
+ err = -EINVAL;
+ dev_err(&mgr->pci->dev,
+ "problem with response size = %d\n", size);
+ goto _clean_exit;
+ }
+ size -= MSG_DESCRIPTOR_SIZE;
+
+ memcpy_fromio(resp->data, MIXART_MEM(mgr, msg_frame_address + MSG_HEADER_SIZE ), size);
+ resp->size = size;
+
+ /* swap if necessary */
+#ifndef __BIG_ENDIAN
+ size /= 4; /* u32 size */
+ for(i=0; i < size; i++) {
+ ((u32*)resp->data)[i] = be32_to_cpu(((__be32*)resp->data)[i]);
+ }
+#endif
+
+ /*
+ * free message frame address
+ */
+ headptr = readl_be(MIXART_MEM(mgr, MSG_OUTBOUND_FREE_HEAD));
+
+ if( (headptr < MSG_OUTBOUND_FREE_STACK) || ( headptr >= (MSG_OUTBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE))) {
+ err = -EINVAL;
+ goto _clean_exit;
+ }
+
+ /* give address back to outbound fifo */
+ writel_be(msg_frame_address, MIXART_MEM(mgr, headptr));
+
+ /* increment the outbound free head */
+ headptr += 4;
+ if( headptr >= (MSG_OUTBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE) )
+ headptr = MSG_OUTBOUND_FREE_STACK;
+
+ writel_be(headptr, MIXART_MEM(mgr, MSG_OUTBOUND_FREE_HEAD));
+
+ _clean_exit:
+ return err;
+}
+
+
+/*
+ * send a message to miXart. return: the msg_frame used for this message
+ */
+/* call with mgr->msg_lock held! */
+static int send_msg( struct mixart_mgr *mgr,
+ struct mixart_msg *msg,
+ int max_answersize,
+ int mark_pending,
+ u32 *msg_event)
+{
+ u32 headptr, tailptr;
+ u32 msg_frame_address;
+ int i;
+
+ if (snd_BUG_ON(msg->size % 4))
+ return -EINVAL;
+
+ /* get message frame address */
+ tailptr = readl_be(MIXART_MEM(mgr, MSG_INBOUND_FREE_TAIL));
+ headptr = readl_be(MIXART_MEM(mgr, MSG_INBOUND_FREE_HEAD));
+
+ if (tailptr == headptr) {
+ dev_err(&mgr->pci->dev, "error: no message frame available\n");
+ return -EBUSY;
+ }
+
+ if( (tailptr < MSG_INBOUND_FREE_STACK) || (tailptr >= (MSG_INBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE))) {
+ return -EINVAL;
+ }
+
+ msg_frame_address = readl_be(MIXART_MEM(mgr, tailptr));
+ writel(0, MIXART_MEM(mgr, tailptr)); /* set address to zero on this fifo position */
+
+ /* increment the inbound free tail */
+ tailptr += 4;
+ if( tailptr >= (MSG_INBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE) )
+ tailptr = MSG_INBOUND_FREE_STACK;
+
+ writel_be(tailptr, MIXART_MEM(mgr, MSG_INBOUND_FREE_TAIL));
+
+ /* TODO : use memcpy_toio() with intermediate buffer to copy the message */
+
+ /* copy message descriptor to card memory */
+ writel_be( msg->size + MSG_DESCRIPTOR_SIZE, MIXART_MEM(mgr, msg_frame_address) ); /* size of descriptor + request */
+ writel_be( msg->message_id , MIXART_MEM(mgr, msg_frame_address + 4) ); /* dwMessageID */
+ writel_be( msg->uid.object_id, MIXART_MEM(mgr, msg_frame_address + 8) ); /* uidDest */
+ writel_be( msg->uid.desc, MIXART_MEM(mgr, msg_frame_address + 12) ); /* */
+ writel_be( MSG_DESCRIPTOR_SIZE, MIXART_MEM(mgr, msg_frame_address + 16) ); /* SizeHeader */
+ writel_be( MSG_DESCRIPTOR_SIZE, MIXART_MEM(mgr, msg_frame_address + 20) ); /* OffsetDLL_T16 */
+ writel_be( msg->size, MIXART_MEM(mgr, msg_frame_address + 24) ); /* SizeDLL_T16 */
+ writel_be( MSG_DESCRIPTOR_SIZE, MIXART_MEM(mgr, msg_frame_address + 28) ); /* OffsetDLL_DRV */
+ writel_be( 0, MIXART_MEM(mgr, msg_frame_address + 32) ); /* SizeDLL_DRV */
+ writel_be( MSG_DESCRIPTOR_SIZE + max_answersize, MIXART_MEM(mgr, msg_frame_address + 36) ); /* dwExpectedAnswerSize */
+
+ /* copy message data to card memory */
+ for( i=0; i < msg->size; i+=4 ) {
+ writel_be( *(u32*)(msg->data + i), MIXART_MEM(mgr, MSG_HEADER_SIZE + msg_frame_address + i) );
+ }
+
+ if( mark_pending ) {
+ if( *msg_event ) {
+ /* the pending event is the notification we wait for ! */
+ mgr->pending_event = *msg_event;
+ }
+ else {
+ /* the pending event is the answer we wait for (same address than the request)! */
+ mgr->pending_event = msg_frame_address;
+
+ /* copy address back to caller */
+ *msg_event = msg_frame_address;
+ }
+ }
+
+ /* mark the frame as a request (will have an answer) */
+ msg_frame_address |= MSG_TYPE_REQUEST;
+
+ /* post the frame */
+ headptr = readl_be(MIXART_MEM(mgr, MSG_INBOUND_POST_HEAD));
+
+ if( (headptr < MSG_INBOUND_POST_STACK) || (headptr >= (MSG_INBOUND_POST_STACK+MSG_BOUND_STACK_SIZE))) {
+ return -EINVAL;
+ }
+
+ writel_be(msg_frame_address, MIXART_MEM(mgr, headptr));
+
+ /* increment the inbound post head */
+ headptr += 4;
+ if( headptr >= (MSG_INBOUND_POST_STACK+MSG_BOUND_STACK_SIZE) )
+ headptr = MSG_INBOUND_POST_STACK;
+
+ writel_be(headptr, MIXART_MEM(mgr, MSG_INBOUND_POST_HEAD));
+
+ return 0;
+}
+
+
+int snd_mixart_send_msg(struct mixart_mgr *mgr, struct mixart_msg *request, int max_resp_size, void *resp_data)
+{
+ struct mixart_msg resp;
+ u32 msg_frame = 0; /* set to 0, so it's no notification to wait for, but the answer */
+ int err;
+ wait_queue_entry_t wait;
+ long timeout;
+
+ init_waitqueue_entry(&wait, current);
+
+ mutex_lock(&mgr->msg_lock);
+ /* send the message */
+ err = send_msg(mgr, request, max_resp_size, 1, &msg_frame); /* send and mark the answer pending */
+ if (err) {
+ mutex_unlock(&mgr->msg_lock);
+ return err;
+ }
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ add_wait_queue(&mgr->msg_sleep, &wait);
+ mutex_unlock(&mgr->msg_lock);
+ timeout = schedule_timeout(MSG_TIMEOUT_JIFFIES);
+ remove_wait_queue(&mgr->msg_sleep, &wait);
+
+ if (! timeout) {
+ /* error - no ack */
+ dev_err(&mgr->pci->dev,
+ "error: no response on msg %x\n", msg_frame);
+ return -EIO;
+ }
+
+ /* retrieve the answer into the same struct mixart_msg */
+ resp.message_id = 0;
+ resp.uid = (struct mixart_uid){0,0};
+ resp.data = resp_data;
+ resp.size = max_resp_size;
+
+ mutex_lock(&mgr->msg_lock);
+ err = get_msg(mgr, &resp, msg_frame);
+ mutex_unlock(&mgr->msg_lock);
+
+ if( request->message_id != resp.message_id )
+ dev_err(&mgr->pci->dev, "RESPONSE ERROR!\n");
+
+ return err;
+}
+
+
+int snd_mixart_send_msg_wait_notif(struct mixart_mgr *mgr,
+ struct mixart_msg *request, u32 notif_event)
+{
+ int err;
+ wait_queue_entry_t wait;
+ long timeout;
+
+ if (snd_BUG_ON(!notif_event))
+ return -EINVAL;
+ if (snd_BUG_ON((notif_event & MSG_TYPE_MASK) != MSG_TYPE_NOTIFY))
+ return -EINVAL;
+ if (snd_BUG_ON(notif_event & MSG_CANCEL_NOTIFY_MASK))
+ return -EINVAL;
+
+ init_waitqueue_entry(&wait, current);
+
+ mutex_lock(&mgr->msg_lock);
+ /* send the message */
+ err = send_msg(mgr, request, MSG_DEFAULT_SIZE, 1, &notif_event); /* send and mark the notification event pending */
+ if(err) {
+ mutex_unlock(&mgr->msg_lock);
+ return err;
+ }
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ add_wait_queue(&mgr->msg_sleep, &wait);
+ mutex_unlock(&mgr->msg_lock);
+ timeout = schedule_timeout(MSG_TIMEOUT_JIFFIES);
+ remove_wait_queue(&mgr->msg_sleep, &wait);
+
+ if (! timeout) {
+ /* error - no ack */
+ dev_err(&mgr->pci->dev,
+ "error: notification %x not received\n", notif_event);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+
+int snd_mixart_send_msg_nonblock(struct mixart_mgr *mgr, struct mixart_msg *request)
+{
+ u32 message_frame;
+ int err;
+
+ /* just send the message (do not mark it as a pending one) */
+ mutex_lock(&mgr->msg_lock);
+ err = send_msg(mgr, request, MSG_DEFAULT_SIZE, 0, &message_frame);
+ mutex_unlock(&mgr->msg_lock);
+
+ /* the answer will be handled by snd_struct mixart_msgasklet() */
+ atomic_inc(&mgr->msg_processed);
+
+ return err;
+}
+
+
+/* common buffer of interrupt to send/receive messages */
+static u32 mixart_msg_data[MSG_DEFAULT_SIZE / 4];
+
+
+static void snd_mixart_process_msg(struct mixart_mgr *mgr)
+{
+ struct mixart_msg resp;
+ u32 msg, addr, type;
+ int err;
+
+ while (mgr->msg_fifo_readptr != mgr->msg_fifo_writeptr) {
+ msg = mgr->msg_fifo[mgr->msg_fifo_readptr];
+ mgr->msg_fifo_readptr++;
+ mgr->msg_fifo_readptr %= MSG_FIFO_SIZE;
+
+ /* process the message ... */
+ addr = msg & ~MSG_TYPE_MASK;
+ type = msg & MSG_TYPE_MASK;
+
+ switch (type) {
+ case MSG_TYPE_ANSWER:
+ /* answer to a message on that we did not wait for (send_msg_nonblock) */
+ resp.message_id = 0;
+ resp.data = mixart_msg_data;
+ resp.size = sizeof(mixart_msg_data);
+ err = get_msg(mgr, &resp, addr);
+ if( err < 0 ) {
+ dev_err(&mgr->pci->dev,
+ "error(%d) reading mf %x\n",
+ err, msg);
+ break;
+ }
+
+ switch(resp.message_id) {
+ case MSG_STREAM_START_INPUT_STAGE_PACKET:
+ case MSG_STREAM_START_OUTPUT_STAGE_PACKET:
+ case MSG_STREAM_STOP_INPUT_STAGE_PACKET:
+ case MSG_STREAM_STOP_OUTPUT_STAGE_PACKET:
+ if(mixart_msg_data[0])
+ dev_err(&mgr->pci->dev,
+ "error MSG_STREAM_ST***_***PUT_STAGE_PACKET status=%x\n",
+ mixart_msg_data[0]);
+ break;
+ default:
+ dev_dbg(&mgr->pci->dev,
+ "received mf(%x) : msg_id(%x) uid(%x, %x) size(%zd)\n",
+ msg, resp.message_id, resp.uid.object_id, resp.uid.desc, resp.size);
+ break;
+ }
+ break;
+ case MSG_TYPE_NOTIFY:
+ /* msg contains no address ! do not get_msg() ! */
+ case MSG_TYPE_COMMAND:
+ /* get_msg() necessary */
+ default:
+ dev_err(&mgr->pci->dev,
+ "doesn't know what to do with message %x\n",
+ msg);
+ } /* switch type */
+
+ /* decrement counter */
+ atomic_dec(&mgr->msg_processed);
+
+ } /* while there is a msg in fifo */
+}
+
+
+irqreturn_t snd_mixart_interrupt(int irq, void *dev_id)
+{
+ struct mixart_mgr *mgr = dev_id;
+ u32 it_reg;
+
+ it_reg = readl_le(MIXART_REG(mgr, MIXART_PCI_OMISR_OFFSET));
+ if( !(it_reg & MIXART_OIDI) ) {
+ /* this device did not cause the interrupt */
+ return IRQ_NONE;
+ }
+
+ /* mask all interrupts */
+ writel_le(MIXART_HOST_ALL_INTERRUPT_MASKED, MIXART_REG(mgr, MIXART_PCI_OMIMR_OFFSET));
+
+ /* outdoorbell register clear */
+ it_reg = readl(MIXART_REG(mgr, MIXART_PCI_ODBR_OFFSET));
+ writel(it_reg, MIXART_REG(mgr, MIXART_PCI_ODBR_OFFSET));
+
+ /* clear interrupt */
+ writel_le( MIXART_OIDI, MIXART_REG(mgr, MIXART_PCI_OMISR_OFFSET) );
+
+ return IRQ_WAKE_THREAD;
+}
+
+irqreturn_t snd_mixart_threaded_irq(int irq, void *dev_id)
+{
+ struct mixart_mgr *mgr = dev_id;
+ int err;
+ struct mixart_msg resp;
+ u32 msg;
+
+ mutex_lock(&mgr->lock);
+ /* process interrupt */
+ while (retrieve_msg_frame(mgr, &msg)) {
+
+ switch (msg & MSG_TYPE_MASK) {
+ case MSG_TYPE_COMMAND:
+ resp.message_id = 0;
+ resp.data = mixart_msg_data;
+ resp.size = sizeof(mixart_msg_data);
+ err = get_msg(mgr, &resp, msg & ~MSG_TYPE_MASK);
+ if( err < 0 ) {
+ dev_err(&mgr->pci->dev,
+ "interrupt: error(%d) reading mf %x\n",
+ err, msg);
+ break;
+ }
+
+ if(resp.message_id == MSG_SERVICES_TIMER_NOTIFY) {
+ int i;
+ struct mixart_timer_notify *notify;
+ notify = (struct mixart_timer_notify *)mixart_msg_data;
+
+ BUILD_BUG_ON(sizeof(notify) > sizeof(mixart_msg_data));
+ if (snd_BUG_ON(notify->stream_count > ARRAY_SIZE(notify->streams)))
+ break;
+ for(i=0; i<notify->stream_count; i++) {
+
+ u32 buffer_id = notify->streams[i].buffer_id;
+ unsigned int chip_number = (buffer_id & MIXART_NOTIFY_CARD_MASK) >> MIXART_NOTIFY_CARD_OFFSET; /* card0 to 3 */
+ unsigned int pcm_number = (buffer_id & MIXART_NOTIFY_PCM_MASK ) >> MIXART_NOTIFY_PCM_OFFSET; /* pcm0 to 3 */
+ unsigned int sub_number = buffer_id & MIXART_NOTIFY_SUBS_MASK; /* 0 to MIXART_PLAYBACK_STREAMS */
+ unsigned int is_capture = ((buffer_id & MIXART_NOTIFY_CAPT_MASK) != 0); /* playback == 0 / capture == 1 */
+
+ struct snd_mixart *chip = mgr->chip[chip_number];
+ struct mixart_stream *stream;
+
+ if ((chip_number >= mgr->num_cards) || (pcm_number >= MIXART_PCM_TOTAL) || (sub_number >= MIXART_PLAYBACK_STREAMS)) {
+ dev_err(&mgr->pci->dev,
+ "error MSG_SERVICES_TIMER_NOTIFY buffer_id (%x) pos(%d)\n",
+ buffer_id, notify->streams[i].sample_pos_low_part);
+ break;
+ }
+
+ if (is_capture)
+ stream = &chip->capture_stream[pcm_number];
+ else
+ stream = &chip->playback_stream[pcm_number][sub_number];
+
+ if (stream->substream && (stream->status == MIXART_STREAM_STATUS_RUNNING)) {
+ struct snd_pcm_runtime *runtime = stream->substream->runtime;
+ int elapsed = 0;
+ u64 sample_count = ((u64)notify->streams[i].sample_pos_high_part) << 32;
+ sample_count |= notify->streams[i].sample_pos_low_part;
+
+ while (1) {
+ u64 new_elapse_pos = stream->abs_period_elapsed + runtime->period_size;
+
+ if (new_elapse_pos > sample_count) {
+ break; /* while */
+ }
+ else {
+ elapsed = 1;
+ stream->buf_periods++;
+ if (stream->buf_periods >= runtime->periods)
+ stream->buf_periods = 0;
+
+ stream->abs_period_elapsed = new_elapse_pos;
+ }
+ }
+ stream->buf_period_frag = (u32)( sample_count - stream->abs_period_elapsed );
+
+ if(elapsed) {
+ mutex_unlock(&mgr->lock);
+ snd_pcm_period_elapsed(stream->substream);
+ mutex_lock(&mgr->lock);
+ }
+ }
+ }
+ break;
+ }
+ if(resp.message_id == MSG_SERVICES_REPORT_TRACES) {
+ if(resp.size > 1) {
+#ifndef __BIG_ENDIAN
+ /* Traces are text: the swapped msg_data has to be swapped back ! */
+ int i;
+ for(i=0; i<(resp.size/4); i++) {
+ ((__be32*)mixart_msg_data)[i] = cpu_to_be32((mixart_msg_data)[i]);
+ }
+#endif
+ ((char*)mixart_msg_data)[resp.size - 1] = 0;
+ dev_dbg(&mgr->pci->dev,
+ "MIXART TRACE : %s\n",
+ (char *)mixart_msg_data);
+ }
+ break;
+ }
+
+ dev_dbg(&mgr->pci->dev, "command %x not handled\n",
+ resp.message_id);
+ break;
+
+ case MSG_TYPE_NOTIFY:
+ if(msg & MSG_CANCEL_NOTIFY_MASK) {
+ msg &= ~MSG_CANCEL_NOTIFY_MASK;
+ dev_err(&mgr->pci->dev,
+ "canceled notification %x !\n", msg);
+ }
+ fallthrough;
+ case MSG_TYPE_ANSWER:
+ /* answer or notification to a message we are waiting for*/
+ mutex_lock(&mgr->msg_lock);
+ if( (msg & ~MSG_TYPE_MASK) == mgr->pending_event ) {
+ wake_up(&mgr->msg_sleep);
+ mgr->pending_event = 0;
+ }
+ /* answer to a message we did't want to wait for */
+ else {
+ mgr->msg_fifo[mgr->msg_fifo_writeptr] = msg;
+ mgr->msg_fifo_writeptr++;
+ mgr->msg_fifo_writeptr %= MSG_FIFO_SIZE;
+ snd_mixart_process_msg(mgr);
+ }
+ mutex_unlock(&mgr->msg_lock);
+ break;
+ case MSG_TYPE_REQUEST:
+ default:
+ dev_dbg(&mgr->pci->dev,
+ "interrupt received request %x\n", msg);
+ /* TODO : are there things to do here ? */
+ break;
+ } /* switch on msg type */
+ } /* while there are msgs */
+
+ /* allow interrupt again */
+ writel_le( MIXART_ALLOW_OUTBOUND_DOORBELL, MIXART_REG( mgr, MIXART_PCI_OMIMR_OFFSET));
+
+ mutex_unlock(&mgr->lock);
+
+ return IRQ_HANDLED;
+}
+
+
+void snd_mixart_init_mailbox(struct mixart_mgr *mgr)
+{
+ writel( 0, MIXART_MEM( mgr, MSG_HOST_RSC_PROTECTION ) );
+ writel( 0, MIXART_MEM( mgr, MSG_AGENT_RSC_PROTECTION ) );
+
+ /* allow outbound messagebox to generate interrupts */
+ if(mgr->irq >= 0) {
+ writel_le( MIXART_ALLOW_OUTBOUND_DOORBELL, MIXART_REG( mgr, MIXART_PCI_OMIMR_OFFSET));
+ }
+ return;
+}
+
+void snd_mixart_exit_mailbox(struct mixart_mgr *mgr)
+{
+ /* no more interrupts on outbound messagebox */
+ writel_le( MIXART_HOST_ALL_INTERRUPT_MASKED, MIXART_REG( mgr, MIXART_PCI_OMIMR_OFFSET));
+ return;
+}
+
+void snd_mixart_reset_board(struct mixart_mgr *mgr)
+{
+ /* reset miXart */
+ writel_be( 1, MIXART_REG(mgr, MIXART_BA1_BRUTAL_RESET_OFFSET) );
+ return;
+}
diff --git a/sound/pci/mixart/mixart_core.h b/sound/pci/mixart/mixart_core.h
new file mode 100644
index 000000000..2f0e29ed5
--- /dev/null
+++ b/sound/pci/mixart/mixart_core.h
@@ -0,0 +1,566 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Driver for Digigram miXart soundcards
+ *
+ * low level interface with interrupt handling and mail box implementation
+ *
+ * Copyright (c) 2003 by Digigram <alsa@digigram.com>
+ */
+
+#ifndef __SOUND_MIXART_CORE_H
+#define __SOUND_MIXART_CORE_H
+
+
+enum mixart_message_id {
+ MSG_CONNECTOR_GET_AUDIO_INFO = 0x050008,
+ MSG_CONNECTOR_GET_OUT_AUDIO_LEVEL = 0x050009,
+ MSG_CONNECTOR_SET_OUT_AUDIO_LEVEL = 0x05000A,
+
+ MSG_CONSOLE_MANAGER = 0x070000,
+ MSG_CONSOLE_GET_CLOCK_UID = 0x070003,
+
+ MSG_PHYSICALIO_SET_LEVEL = 0x0F0008,
+
+ MSG_STREAM_ADD_INPUT_GROUP = 0x130000,
+ MSG_STREAM_ADD_OUTPUT_GROUP = 0x130001,
+ MSG_STREAM_DELETE_GROUP = 0x130004,
+ MSG_STREAM_START_STREAM_GRP_PACKET = 0x130006,
+ MSG_STREAM_START_INPUT_STAGE_PACKET = 0x130007,
+ MSG_STREAM_START_OUTPUT_STAGE_PACKET = 0x130008,
+ MSG_STREAM_STOP_STREAM_GRP_PACKET = 0x130009,
+ MSG_STREAM_STOP_INPUT_STAGE_PACKET = 0x13000A,
+ MSG_STREAM_STOP_OUTPUT_STAGE_PACKET = 0x13000B,
+ MSG_STREAM_SET_INPUT_STAGE_PARAM = 0x13000F,
+ MSG_STREAM_SET_OUTPUT_STAGE_PARAM = 0x130010,
+ MSG_STREAM_SET_IN_AUDIO_LEVEL = 0x130015,
+ MSG_STREAM_SET_OUT_STREAM_LEVEL = 0x130017,
+
+ MSG_SYSTEM_FIRST_ID = 0x160000,
+ MSG_SYSTEM_ENUM_PHYSICAL_IO = 0x16000E,
+ MSG_SYSTEM_ENUM_PLAY_CONNECTOR = 0x160017,
+ MSG_SYSTEM_ENUM_RECORD_CONNECTOR = 0x160018,
+ MSG_SYSTEM_WAIT_SYNCHRO_CMD = 0x16002C,
+ MSG_SYSTEM_SEND_SYNCHRO_CMD = 0x16002D,
+
+ MSG_SERVICES_TIMER_NOTIFY = 0x1D0404,
+ MSG_SERVICES_REPORT_TRACES = 0x1D0700,
+
+ MSG_CLOCK_CHECK_PROPERTIES = 0x200001,
+ MSG_CLOCK_SET_PROPERTIES = 0x200002,
+};
+
+#define MSG_DEFAULT_SIZE 512
+
+struct mixart_msg
+{
+ u32 message_id;
+ struct mixart_uid uid;
+ void* data;
+ size_t size;
+};
+
+/* structs used to communicate with miXart */
+
+struct mixart_enum_connector_resp
+{
+ u32 error_code;
+ u32 first_uid_offset;
+ u32 uid_count;
+ u32 current_uid_index;
+ struct mixart_uid uid[MIXART_MAX_PHYS_CONNECTORS];
+} __attribute__((packed));
+
+
+/* used for following struct */
+#define MIXART_FLOAT_P_22_0_TO_HEX 0x41b00000 /* 22.0f */
+#define MIXART_FLOAT_M_20_0_TO_HEX 0xc1a00000 /* -20.0f */
+#define MIXART_FLOAT____0_0_TO_HEX 0x00000000 /* 0.0f */
+
+struct mixart_audio_info_req
+{
+ u32 line_max_level; /* float */
+ u32 micro_max_level; /* float */
+ u32 cd_max_level; /* float */
+} __attribute__((packed));
+
+struct mixart_analog_hw_info
+{
+ u32 is_present;
+ u32 hw_connection_type;
+ u32 max_level; /* float */
+ u32 min_var_level; /* float */
+ u32 max_var_level; /* float */
+ u32 step_var_level; /* float */
+ u32 fix_gain; /* float */
+ u32 zero_var; /* float */
+} __attribute__((packed));
+
+struct mixart_digital_hw_info
+{
+ u32 hw_connection_type;
+ u32 presence;
+ u32 clock;
+ u32 reserved;
+} __attribute__((packed));
+
+struct mixart_analog_info
+{
+ u32 type_mask;
+ struct mixart_analog_hw_info micro_info;
+ struct mixart_analog_hw_info line_info;
+ struct mixart_analog_hw_info cd_info;
+ u32 analog_level_present;
+} __attribute__((packed));
+
+struct mixart_digital_info
+{
+ u32 type_mask;
+ struct mixart_digital_hw_info aes_info;
+ struct mixart_digital_hw_info adat_info;
+} __attribute__((packed));
+
+struct mixart_audio_info
+{
+ u32 clock_type_mask;
+ struct mixart_analog_info analog_info;
+ struct mixart_digital_info digital_info;
+} __attribute__((packed));
+
+struct mixart_audio_info_resp
+{
+ u32 txx_status;
+ struct mixart_audio_info info;
+} __attribute__((packed));
+
+
+/* used for nb_bytes_max_per_sample */
+#define MIXART_FLOAT_P__4_0_TO_HEX 0x40800000 /* +4.0f */
+#define MIXART_FLOAT_P__8_0_TO_HEX 0x41000000 /* +8.0f */
+
+struct mixart_stream_info
+{
+ u32 size_max_byte_frame;
+ u32 size_max_sample_frame;
+ u32 nb_bytes_max_per_sample; /* float */
+} __attribute__((packed));
+
+/* MSG_STREAM_ADD_INPUT_GROUP */
+/* MSG_STREAM_ADD_OUTPUT_GROUP */
+
+struct mixart_streaming_group_req
+{
+ u32 stream_count;
+ u32 channel_count;
+ u32 user_grp_number;
+ u32 first_phys_audio;
+ u32 latency;
+ struct mixart_stream_info stream_info[32];
+ struct mixart_uid connector;
+ u32 flow_entry[32];
+} __attribute__((packed));
+
+struct mixart_stream_desc
+{
+ struct mixart_uid stream_uid;
+ u32 stream_desc;
+} __attribute__((packed));
+
+struct mixart_streaming_group
+{
+ u32 status;
+ struct mixart_uid group;
+ u32 pipe_desc;
+ u32 stream_count;
+ struct mixart_stream_desc stream[32];
+} __attribute__((packed));
+
+/* MSG_STREAM_DELETE_GROUP */
+
+/* request : mixart_uid_t group */
+
+struct mixart_delete_group_resp
+{
+ u32 status;
+ u32 unused[2];
+} __attribute__((packed));
+
+
+/* MSG_STREAM_START_INPUT_STAGE_PACKET = 0x130000 + 7,
+ MSG_STREAM_START_OUTPUT_STAGE_PACKET = 0x130000 + 8,
+ MSG_STREAM_STOP_INPUT_STAGE_PACKET = 0x130000 + 10,
+ MSG_STREAM_STOP_OUTPUT_STAGE_PACKET = 0x130000 + 11,
+ */
+
+struct mixart_fx_couple_uid
+{
+ struct mixart_uid uid_fx_code;
+ struct mixart_uid uid_fx_data;
+} __attribute__((packed));
+
+struct mixart_txx_stream_desc
+{
+ struct mixart_uid uid_pipe;
+ u32 stream_idx;
+ u32 fx_number;
+ struct mixart_fx_couple_uid uid_fx[4];
+} __attribute__((packed));
+
+struct mixart_flow_info
+{
+ struct mixart_txx_stream_desc stream_desc;
+ u32 flow_entry;
+ u32 flow_phy_addr;
+} __attribute__((packed));
+
+struct mixart_stream_state_req
+{
+ u32 delayed;
+ u64 scheduler;
+ u32 reserved4np[3];
+ u32 stream_count; /* set to 1 for instance */
+ struct mixart_flow_info stream_info; /* could be an array[stream_count] */
+} __attribute__((packed));
+
+/* MSG_STREAM_START_STREAM_GRP_PACKET = 0x130000 + 6
+ MSG_STREAM_STOP_STREAM_GRP_PACKET = 0x130000 + 9
+ */
+
+struct mixart_group_state_req
+{
+ u32 delayed;
+ u64 scheduler;
+ u32 reserved4np[2];
+ u32 pipe_count; /* set to 1 for instance */
+ struct mixart_uid pipe_uid[1]; /* could be an array[pipe_count] */
+} __attribute__((packed));
+
+struct mixart_group_state_resp
+{
+ u32 txx_status;
+ u64 scheduler;
+} __attribute__((packed));
+
+
+
+/* Structures used by the MSG_SERVICES_TIMER_NOTIFY command */
+
+struct mixart_sample_pos
+{
+ u32 buffer_id;
+ u32 validity;
+ u32 sample_pos_high_part;
+ u32 sample_pos_low_part;
+} __attribute__((packed));
+
+/*
+ * This structure is limited by the size of MSG_DEFAULT_SIZE. Instead of
+ * having MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS many streams,
+ * this is capped to have a total size below MSG_DEFAULT_SIZE.
+ */
+#define MIXART_MAX_TIMER_NOTIFY_STREAMS \
+ ((MSG_DEFAULT_SIZE - sizeof(u32)) / sizeof(struct mixart_sample_pos))
+struct mixart_timer_notify
+{
+ u32 stream_count;
+ struct mixart_sample_pos streams[MIXART_MAX_TIMER_NOTIFY_STREAMS];
+} __attribute__((packed));
+
+
+/* MSG_CONSOLE_GET_CLOCK_UID = 0x070003,
+ */
+
+/* request is a uid with desc = MSG_CONSOLE_MANAGER | cardindex */
+
+struct mixart_return_uid
+{
+ u32 error_code;
+ struct mixart_uid uid;
+} __attribute__((packed));
+
+/* MSG_CLOCK_CHECK_PROPERTIES = 0x200001,
+ MSG_CLOCK_SET_PROPERTIES = 0x200002,
+*/
+
+enum mixart_clock_generic_type {
+ CGT_NO_CLOCK,
+ CGT_INTERNAL_CLOCK,
+ CGT_PROGRAMMABLE_CLOCK,
+ CGT_INTERNAL_ENSLAVED_CLOCK,
+ CGT_EXTERNAL_CLOCK,
+ CGT_CURRENT_CLOCK
+};
+
+enum mixart_clock_mode {
+ CM_UNDEFINED,
+ CM_MASTER,
+ CM_SLAVE,
+ CM_STANDALONE,
+ CM_NOT_CONCERNED
+};
+
+
+struct mixart_clock_properties
+{
+ u32 error_code;
+ u32 validation_mask;
+ u32 frequency;
+ u32 reference_frequency;
+ u32 clock_generic_type;
+ u32 clock_mode;
+ struct mixart_uid uid_clock_source;
+ struct mixart_uid uid_event_source;
+ u32 event_mode;
+ u32 synchro_signal_presence;
+ u32 format;
+ u32 board_mask;
+ u32 nb_callers; /* set to 1 (see below) */
+ struct mixart_uid uid_caller[1];
+} __attribute__((packed));
+
+struct mixart_clock_properties_resp
+{
+ u32 status;
+ u32 clock_mode;
+} __attribute__((packed));
+
+
+/* MSG_STREAM_SET_INPUT_STAGE_PARAM = 0x13000F */
+/* MSG_STREAM_SET_OUTPUT_STAGE_PARAM = 0x130010 */
+
+enum mixart_coding_type {
+ CT_NOT_DEFINED,
+ CT_LINEAR,
+ CT_MPEG_L1,
+ CT_MPEG_L2,
+ CT_MPEG_L3,
+ CT_MPEG_L3_LSF,
+ CT_GSM
+};
+enum mixart_sample_type {
+ ST_NOT_DEFINED,
+ ST_FLOATING_POINT_32BE,
+ ST_FLOATING_POINT_32LE,
+ ST_FLOATING_POINT_64BE,
+ ST_FLOATING_POINT_64LE,
+ ST_FIXED_POINT_8,
+ ST_FIXED_POINT_16BE,
+ ST_FIXED_POINT_16LE,
+ ST_FIXED_POINT_24BE,
+ ST_FIXED_POINT_24LE,
+ ST_FIXED_POINT_32BE,
+ ST_FIXED_POINT_32LE,
+ ST_INTEGER_8,
+ ST_INTEGER_16BE,
+ ST_INTEGER_16LE,
+ ST_INTEGER_24BE,
+ ST_INTEGER_24LE,
+ ST_INTEGER_32BE,
+ ST_INTEGER_32LE
+};
+
+struct mixart_stream_param_desc
+{
+ u32 coding_type; /* use enum mixart_coding_type */
+ u32 sample_type; /* use enum mixart_sample_type */
+
+ union {
+ struct {
+ u32 linear_endian_ness;
+ u32 linear_bits;
+ u32 is_signed;
+ u32 is_float;
+ } linear_format_info;
+
+ struct {
+ u32 mpeg_layer;
+ u32 mpeg_mode;
+ u32 mpeg_mode_extension;
+ u32 mpeg_pre_emphasis;
+ u32 mpeg_has_padding_bit;
+ u32 mpeg_has_crc;
+ u32 mpeg_has_extension;
+ u32 mpeg_is_original;
+ u32 mpeg_has_copyright;
+ } mpeg_format_info;
+ } format_info;
+
+ u32 delayed;
+ u64 scheduler;
+ u32 sample_size;
+ u32 has_header;
+ u32 has_suffix;
+ u32 has_bitrate;
+ u32 samples_per_frame;
+ u32 bytes_per_frame;
+ u32 bytes_per_sample;
+ u32 sampling_freq;
+ u32 number_of_channel;
+ u32 stream_number;
+ u32 buffer_size;
+ u32 differed_time;
+ u32 reserved4np[3];
+ u32 pipe_count; /* set to 1 (array size !) */
+ u32 stream_count; /* set to 1 (array size !) */
+ struct mixart_txx_stream_desc stream_desc[1]; /* only one stream per command, but this could be an array */
+
+} __attribute__((packed));
+
+
+/* MSG_CONNECTOR_GET_OUT_AUDIO_LEVEL = 0x050009,
+ */
+
+
+struct mixart_get_out_audio_level
+{
+ u32 txx_status;
+ u32 digital_level; /* float */
+ u32 analog_level; /* float */
+ u32 monitor_level; /* float */
+ u32 mute;
+ u32 monitor_mute1;
+ u32 monitor_mute2;
+} __attribute__((packed));
+
+
+/* MSG_CONNECTOR_SET_OUT_AUDIO_LEVEL = 0x05000A,
+ */
+
+/* used for valid_mask below */
+#define MIXART_AUDIO_LEVEL_ANALOG_MASK 0x01
+#define MIXART_AUDIO_LEVEL_DIGITAL_MASK 0x02
+#define MIXART_AUDIO_LEVEL_MONITOR_MASK 0x04
+#define MIXART_AUDIO_LEVEL_MUTE_MASK 0x08
+#define MIXART_AUDIO_LEVEL_MUTE_M1_MASK 0x10
+#define MIXART_AUDIO_LEVEL_MUTE_M2_MASK 0x20
+
+struct mixart_set_out_audio_level
+{
+ u32 delayed;
+ u64 scheduler;
+ u32 valid_mask1;
+ u32 valid_mask2;
+ u32 digital_level; /* float */
+ u32 analog_level; /* float */
+ u32 monitor_level; /* float */
+ u32 mute;
+ u32 monitor_mute1;
+ u32 monitor_mute2;
+ u32 reserved4np;
+} __attribute__((packed));
+
+
+/* MSG_SYSTEM_ENUM_PHYSICAL_IO = 0x16000E,
+ */
+
+#define MIXART_MAX_PHYS_IO (MIXART_MAX_CARDS * 2 * 2) /* 4 * (analog+digital) * (playback+capture) */
+
+struct mixart_uid_enumeration
+{
+ u32 error_code;
+ u32 first_uid_offset;
+ u32 nb_uid;
+ u32 current_uid_index;
+ struct mixart_uid uid[MIXART_MAX_PHYS_IO];
+} __attribute__((packed));
+
+
+/* MSG_PHYSICALIO_SET_LEVEL = 0x0F0008,
+ MSG_PHYSICALIO_GET_LEVEL = 0x0F000C,
+*/
+
+struct mixart_io_channel_level
+{
+ u32 analog_level; /* float */
+ u32 unused[2];
+} __attribute__((packed));
+
+struct mixart_io_level
+{
+ s32 channel; /* 0=left, 1=right, -1=both, -2=both same */
+ struct mixart_io_channel_level level[2];
+} __attribute__((packed));
+
+
+/* MSG_STREAM_SET_IN_AUDIO_LEVEL = 0x130015,
+ */
+
+struct mixart_in_audio_level_info
+{
+ struct mixart_uid connector;
+ u32 valid_mask1;
+ u32 valid_mask2;
+ u32 digital_level;
+ u32 analog_level;
+} __attribute__((packed));
+
+struct mixart_set_in_audio_level_req
+{
+ u32 delayed;
+ u64 scheduler;
+ u32 audio_count; /* set to <= 2 */
+ u32 reserved4np;
+ struct mixart_in_audio_level_info level[2];
+} __attribute__((packed));
+
+/* response is a 32 bit status */
+
+
+/* MSG_STREAM_SET_OUT_STREAM_LEVEL = 0x130017,
+ */
+
+/* defines used for valid_mask1 */
+#define MIXART_OUT_STREAM_SET_LEVEL_LEFT_AUDIO1 0x01
+#define MIXART_OUT_STREAM_SET_LEVEL_LEFT_AUDIO2 0x02
+#define MIXART_OUT_STREAM_SET_LEVEL_RIGHT_AUDIO1 0x04
+#define MIXART_OUT_STREAM_SET_LEVEL_RIGHT_AUDIO2 0x08
+#define MIXART_OUT_STREAM_SET_LEVEL_STREAM_1 0x10
+#define MIXART_OUT_STREAM_SET_LEVEL_STREAM_2 0x20
+#define MIXART_OUT_STREAM_SET_LEVEL_MUTE_1 0x40
+#define MIXART_OUT_STREAM_SET_LEVEL_MUTE_2 0x80
+
+struct mixart_out_stream_level_info
+{
+ u32 valid_mask1;
+ u32 valid_mask2;
+ u32 left_to_out1_level;
+ u32 left_to_out2_level;
+ u32 right_to_out1_level;
+ u32 right_to_out2_level;
+ u32 digital_level1;
+ u32 digital_level2;
+ u32 mute1;
+ u32 mute2;
+} __attribute__((packed));
+
+struct mixart_set_out_stream_level
+{
+ struct mixart_txx_stream_desc desc;
+ struct mixart_out_stream_level_info out_level;
+} __attribute__((packed));
+
+struct mixart_set_out_stream_level_req
+{
+ u32 delayed;
+ u64 scheduler;
+ u32 reserved4np[2];
+ u32 nb_of_stream; /* set to 1 */
+ struct mixart_set_out_stream_level stream_level; /* could be an array */
+} __attribute__((packed));
+
+/* response to this request is a u32 status value */
+
+
+/* exported */
+void snd_mixart_init_mailbox(struct mixart_mgr *mgr);
+void snd_mixart_exit_mailbox(struct mixart_mgr *mgr);
+
+int snd_mixart_send_msg(struct mixart_mgr *mgr, struct mixart_msg *request, int max_resp_size, void *resp_data);
+int snd_mixart_send_msg_wait_notif(struct mixart_mgr *mgr, struct mixart_msg *request, u32 notif_event);
+int snd_mixart_send_msg_nonblock(struct mixart_mgr *mgr, struct mixart_msg *request);
+
+irqreturn_t snd_mixart_interrupt(int irq, void *dev_id);
+irqreturn_t snd_mixart_threaded_irq(int irq, void *dev_id);
+
+void snd_mixart_reset_board(struct mixart_mgr *mgr);
+
+#endif /* __SOUND_MIXART_CORE_H */
diff --git a/sound/pci/mixart/mixart_hwdep.c b/sound/pci/mixart/mixart_hwdep.c
new file mode 100644
index 000000000..689c0f995
--- /dev/null
+++ b/sound/pci/mixart/mixart_hwdep.c
@@ -0,0 +1,586 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Digigram miXart soundcards
+ *
+ * DSP firmware management
+ *
+ * Copyright (c) 2003 by Digigram <alsa@digigram.com>
+ */
+
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/firmware.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include "mixart.h"
+#include "mixart_mixer.h"
+#include "mixart_core.h"
+#include "mixart_hwdep.h"
+
+
+/**
+ * mixart_wait_nice_for_register_value - wait for a value on a peudo register,
+ * exit with a timeout
+ *
+ * @mgr: pointer to miXart manager structure
+ * @offset: unsigned pseudo_register base + offset of value
+ * @is_egal: wait for the equal value
+ * @value: value
+ * @timeout: timeout in centisenconds
+ */
+static int mixart_wait_nice_for_register_value(struct mixart_mgr *mgr,
+ u32 offset, int is_egal,
+ u32 value, unsigned long timeout)
+{
+ unsigned long end_time = jiffies + (timeout * HZ / 100);
+ u32 read;
+
+ do { /* we may take too long time in this loop.
+ * so give controls back to kernel if needed.
+ */
+ cond_resched();
+
+ read = readl_be( MIXART_MEM( mgr, offset ));
+ if(is_egal) {
+ if(read == value) return 0;
+ }
+ else { /* wait for different value */
+ if(read != value) return 0;
+ }
+ } while ( time_after_eq(end_time, jiffies) );
+
+ return -EBUSY;
+}
+
+
+/*
+ structures needed to upload elf code packets
+ */
+struct snd_mixart_elf32_ehdr {
+ u8 e_ident[16];
+ __be16 e_type;
+ __be16 e_machine;
+ __be32 e_version;
+ __be32 e_entry;
+ __be32 e_phoff;
+ __be32 e_shoff;
+ __be32 e_flags;
+ __be16 e_ehsize;
+ __be16 e_phentsize;
+ __be16 e_phnum;
+ __be16 e_shentsize;
+ __be16 e_shnum;
+ __be16 e_shstrndx;
+};
+
+struct snd_mixart_elf32_phdr {
+ __be32 p_type;
+ __be32 p_offset;
+ __be32 p_vaddr;
+ __be32 p_paddr;
+ __be32 p_filesz;
+ __be32 p_memsz;
+ __be32 p_flags;
+ __be32 p_align;
+};
+
+static int mixart_load_elf(struct mixart_mgr *mgr, const struct firmware *dsp )
+{
+ char elf32_magic_number[4] = {0x7f,'E','L','F'};
+ struct snd_mixart_elf32_ehdr *elf_header;
+ int i;
+
+ elf_header = (struct snd_mixart_elf32_ehdr *)dsp->data;
+ for( i=0; i<4; i++ )
+ if ( elf32_magic_number[i] != elf_header->e_ident[i] )
+ return -EINVAL;
+
+ if( elf_header->e_phoff != 0 ) {
+ struct snd_mixart_elf32_phdr elf_programheader;
+
+ for( i=0; i < be16_to_cpu(elf_header->e_phnum); i++ ) {
+ u32 pos = be32_to_cpu(elf_header->e_phoff) + (u32)(i * be16_to_cpu(elf_header->e_phentsize));
+
+ memcpy( &elf_programheader, dsp->data + pos, sizeof(elf_programheader) );
+
+ if(elf_programheader.p_type != 0) {
+ if( elf_programheader.p_filesz != 0 ) {
+ memcpy_toio( MIXART_MEM( mgr, be32_to_cpu(elf_programheader.p_vaddr)),
+ dsp->data + be32_to_cpu( elf_programheader.p_offset ),
+ be32_to_cpu( elf_programheader.p_filesz ));
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+ * get basic information and init miXart
+ */
+
+/* audio IDs for request to the board */
+#define MIXART_FIRST_ANA_AUDIO_ID 0
+#define MIXART_FIRST_DIG_AUDIO_ID 8
+
+static int mixart_enum_connectors(struct mixart_mgr *mgr)
+{
+ u32 k;
+ int err;
+ struct mixart_msg request;
+ struct mixart_enum_connector_resp *connector;
+ struct mixart_audio_info_req *audio_info_req;
+ struct mixart_audio_info_resp *audio_info;
+
+ connector = kmalloc(sizeof(*connector), GFP_KERNEL);
+ audio_info_req = kmalloc(sizeof(*audio_info_req), GFP_KERNEL);
+ audio_info = kmalloc(sizeof(*audio_info), GFP_KERNEL);
+ if (! connector || ! audio_info_req || ! audio_info) {
+ err = -ENOMEM;
+ goto __error;
+ }
+
+ audio_info_req->line_max_level = MIXART_FLOAT_P_22_0_TO_HEX;
+ audio_info_req->micro_max_level = MIXART_FLOAT_M_20_0_TO_HEX;
+ audio_info_req->cd_max_level = MIXART_FLOAT____0_0_TO_HEX;
+
+ request.message_id = MSG_SYSTEM_ENUM_PLAY_CONNECTOR;
+ request.uid = (struct mixart_uid){0,0}; /* board num = 0 */
+ request.data = NULL;
+ request.size = 0;
+
+ err = snd_mixart_send_msg(mgr, &request, sizeof(*connector), connector);
+ if((err < 0) || (connector->error_code) || (connector->uid_count > MIXART_MAX_PHYS_CONNECTORS)) {
+ dev_err(&mgr->pci->dev,
+ "error MSG_SYSTEM_ENUM_PLAY_CONNECTOR\n");
+ err = -EINVAL;
+ goto __error;
+ }
+
+ for(k=0; k < connector->uid_count; k++) {
+ struct mixart_pipe *pipe;
+
+ if(k < MIXART_FIRST_DIG_AUDIO_ID) {
+ pipe = &mgr->chip[k/2]->pipe_out_ana;
+ } else {
+ pipe = &mgr->chip[(k-MIXART_FIRST_DIG_AUDIO_ID)/2]->pipe_out_dig;
+ }
+ if(k & 1) {
+ pipe->uid_right_connector = connector->uid[k]; /* odd */
+ } else {
+ pipe->uid_left_connector = connector->uid[k]; /* even */
+ }
+
+ /* dev_dbg(&mgr->pci->dev, "playback connector[%d].object_id = %x\n", k, connector->uid[k].object_id); */
+
+ /* TODO: really need send_msg MSG_CONNECTOR_GET_AUDIO_INFO for each connector ? perhaps for analog level caps ? */
+ request.message_id = MSG_CONNECTOR_GET_AUDIO_INFO;
+ request.uid = connector->uid[k];
+ request.data = audio_info_req;
+ request.size = sizeof(*audio_info_req);
+
+ err = snd_mixart_send_msg(mgr, &request, sizeof(*audio_info), audio_info);
+ if( err < 0 ) {
+ dev_err(&mgr->pci->dev,
+ "error MSG_CONNECTOR_GET_AUDIO_INFO\n");
+ goto __error;
+ }
+ /*dev_dbg(&mgr->pci->dev, "play analog_info.analog_level_present = %x\n", audio_info->info.analog_info.analog_level_present);*/
+ }
+
+ request.message_id = MSG_SYSTEM_ENUM_RECORD_CONNECTOR;
+ request.uid = (struct mixart_uid){0,0}; /* board num = 0 */
+ request.data = NULL;
+ request.size = 0;
+
+ err = snd_mixart_send_msg(mgr, &request, sizeof(*connector), connector);
+ if((err < 0) || (connector->error_code) || (connector->uid_count > MIXART_MAX_PHYS_CONNECTORS)) {
+ dev_err(&mgr->pci->dev,
+ "error MSG_SYSTEM_ENUM_RECORD_CONNECTOR\n");
+ err = -EINVAL;
+ goto __error;
+ }
+
+ for(k=0; k < connector->uid_count; k++) {
+ struct mixart_pipe *pipe;
+
+ if(k < MIXART_FIRST_DIG_AUDIO_ID) {
+ pipe = &mgr->chip[k/2]->pipe_in_ana;
+ } else {
+ pipe = &mgr->chip[(k-MIXART_FIRST_DIG_AUDIO_ID)/2]->pipe_in_dig;
+ }
+ if(k & 1) {
+ pipe->uid_right_connector = connector->uid[k]; /* odd */
+ } else {
+ pipe->uid_left_connector = connector->uid[k]; /* even */
+ }
+
+ /* dev_dbg(&mgr->pci->dev, "capture connector[%d].object_id = %x\n", k, connector->uid[k].object_id); */
+
+ /* TODO: really need send_msg MSG_CONNECTOR_GET_AUDIO_INFO for each connector ? perhaps for analog level caps ? */
+ request.message_id = MSG_CONNECTOR_GET_AUDIO_INFO;
+ request.uid = connector->uid[k];
+ request.data = audio_info_req;
+ request.size = sizeof(*audio_info_req);
+
+ err = snd_mixart_send_msg(mgr, &request, sizeof(*audio_info), audio_info);
+ if( err < 0 ) {
+ dev_err(&mgr->pci->dev,
+ "error MSG_CONNECTOR_GET_AUDIO_INFO\n");
+ goto __error;
+ }
+ /*dev_dbg(&mgr->pci->dev, "rec analog_info.analog_level_present = %x\n", audio_info->info.analog_info.analog_level_present);*/
+ }
+ err = 0;
+
+ __error:
+ kfree(connector);
+ kfree(audio_info_req);
+ kfree(audio_info);
+
+ return err;
+}
+
+static int mixart_enum_physio(struct mixart_mgr *mgr)
+{
+ u32 k;
+ int err;
+ struct mixart_msg request;
+ struct mixart_uid get_console_mgr;
+ struct mixart_return_uid console_mgr;
+ struct mixart_uid_enumeration phys_io;
+
+ /* get the uid for the console manager */
+ get_console_mgr.object_id = 0;
+ get_console_mgr.desc = MSG_CONSOLE_MANAGER | 0; /* cardindex = 0 */
+
+ request.message_id = MSG_CONSOLE_GET_CLOCK_UID;
+ request.uid = get_console_mgr;
+ request.data = &get_console_mgr;
+ request.size = sizeof(get_console_mgr);
+
+ err = snd_mixart_send_msg(mgr, &request, sizeof(console_mgr), &console_mgr);
+
+ if( (err < 0) || (console_mgr.error_code != 0) ) {
+ dev_dbg(&mgr->pci->dev,
+ "error MSG_CONSOLE_GET_CLOCK_UID : err=%x\n",
+ console_mgr.error_code);
+ return -EINVAL;
+ }
+
+ /* used later for clock issues ! */
+ mgr->uid_console_manager = console_mgr.uid;
+
+ request.message_id = MSG_SYSTEM_ENUM_PHYSICAL_IO;
+ request.uid = (struct mixart_uid){0,0};
+ request.data = &console_mgr.uid;
+ request.size = sizeof(console_mgr.uid);
+
+ err = snd_mixart_send_msg(mgr, &request, sizeof(phys_io), &phys_io);
+ if( (err < 0) || ( phys_io.error_code != 0 ) ) {
+ dev_err(&mgr->pci->dev,
+ "error MSG_SYSTEM_ENUM_PHYSICAL_IO err(%x) error_code(%x)\n",
+ err, phys_io.error_code);
+ return -EINVAL;
+ }
+
+ /* min 2 phys io per card (analog in + analog out) */
+ if (phys_io.nb_uid < MIXART_MAX_CARDS * 2)
+ return -EINVAL;
+
+ for(k=0; k<mgr->num_cards; k++) {
+ mgr->chip[k]->uid_in_analog_physio = phys_io.uid[k];
+ mgr->chip[k]->uid_out_analog_physio = phys_io.uid[phys_io.nb_uid/2 + k];
+ }
+
+ return 0;
+}
+
+
+static int mixart_first_init(struct mixart_mgr *mgr)
+{
+ u32 k;
+ int err;
+ struct mixart_msg request;
+
+ err = mixart_enum_connectors(mgr);
+ if (err < 0)
+ return err;
+
+ err = mixart_enum_physio(mgr);
+ if (err < 0)
+ return err;
+
+ /* send a synchro command to card (necessary to do this before first MSG_STREAM_START_STREAM_GRP_PACKET) */
+ /* though why not here */
+ request.message_id = MSG_SYSTEM_SEND_SYNCHRO_CMD;
+ request.uid = (struct mixart_uid){0,0};
+ request.data = NULL;
+ request.size = 0;
+ /* this command has no data. response is a 32 bit status */
+ err = snd_mixart_send_msg(mgr, &request, sizeof(k), &k);
+ if( (err < 0) || (k != 0) ) {
+ dev_err(&mgr->pci->dev, "error MSG_SYSTEM_SEND_SYNCHRO_CMD\n");
+ return err == 0 ? -EINVAL : err;
+ }
+
+ return 0;
+}
+
+
+/* firmware base addresses (when hard coded) */
+#define MIXART_MOTHERBOARD_XLX_BASE_ADDRESS 0x00600000
+
+static int mixart_dsp_load(struct mixart_mgr* mgr, int index, const struct firmware *dsp)
+{
+ int err, card_index;
+ u32 status_xilinx, status_elf, status_daught;
+ u32 val;
+
+ /* read motherboard xilinx status */
+ status_xilinx = readl_be( MIXART_MEM( mgr,MIXART_PSEUDOREG_MXLX_STATUS_OFFSET ));
+ /* read elf status */
+ status_elf = readl_be( MIXART_MEM( mgr,MIXART_PSEUDOREG_ELF_STATUS_OFFSET ));
+ /* read daughterboard xilinx status */
+ status_daught = readl_be( MIXART_MEM( mgr,MIXART_PSEUDOREG_DXLX_STATUS_OFFSET ));
+
+ /* motherboard xilinx status 5 will say that the board is performing a reset */
+ if (status_xilinx == 5) {
+ dev_err(&mgr->pci->dev, "miXart is resetting !\n");
+ return -EAGAIN; /* try again later */
+ }
+
+ switch (index) {
+ case MIXART_MOTHERBOARD_XLX_INDEX:
+
+ /* xilinx already loaded ? */
+ if (status_xilinx == 4) {
+ dev_dbg(&mgr->pci->dev, "xilinx is already loaded !\n");
+ return 0;
+ }
+ /* the status should be 0 == "idle" */
+ if (status_xilinx != 0) {
+ dev_err(&mgr->pci->dev,
+ "xilinx load error ! status = %d\n",
+ status_xilinx);
+ return -EIO; /* modprob -r may help ? */
+ }
+
+ /* check xilinx validity */
+ if (((u32*)(dsp->data))[0] == 0xffffffff)
+ return -EINVAL;
+ if (dsp->size % 4)
+ return -EINVAL;
+
+ /* set xilinx status to copying */
+ writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET ));
+
+ /* setup xilinx base address */
+ writel_be( MIXART_MOTHERBOARD_XLX_BASE_ADDRESS, MIXART_MEM( mgr,MIXART_PSEUDOREG_MXLX_BASE_ADDR_OFFSET ));
+ /* setup code size for xilinx file */
+ writel_be( dsp->size, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_SIZE_OFFSET ));
+
+ /* copy xilinx code */
+ memcpy_toio( MIXART_MEM( mgr, MIXART_MOTHERBOARD_XLX_BASE_ADDRESS), dsp->data, dsp->size);
+
+ /* set xilinx status to copy finished */
+ writel_be( 2, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET ));
+
+ /* return, because no further processing needed */
+ return 0;
+
+ case MIXART_MOTHERBOARD_ELF_INDEX:
+
+ if (status_elf == 4) {
+ dev_dbg(&mgr->pci->dev, "elf file already loaded !\n");
+ return 0;
+ }
+
+ /* the status should be 0 == "idle" */
+ if (status_elf != 0) {
+ dev_err(&mgr->pci->dev,
+ "elf load error ! status = %d\n",
+ status_elf);
+ return -EIO; /* modprob -r may help ? */
+ }
+
+ /* wait for xilinx status == 4 */
+ err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET, 1, 4, 500); /* 5sec */
+ if (err < 0) {
+ dev_err(&mgr->pci->dev, "xilinx was not loaded or "
+ "could not be started\n");
+ return err;
+ }
+
+ /* init some data on the card */
+ writel_be( 0, MIXART_MEM( mgr, MIXART_PSEUDOREG_BOARDNUMBER ) ); /* set miXart boardnumber to 0 */
+ writel_be( 0, MIXART_MEM( mgr, MIXART_FLOWTABLE_PTR ) ); /* reset pointer to flow table on miXart */
+
+ /* set elf status to copying */
+ writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET ));
+
+ /* process the copying of the elf packets */
+ err = mixart_load_elf( mgr, dsp );
+ if (err < 0) return err;
+
+ /* set elf status to copy finished */
+ writel_be( 2, MIXART_MEM( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET ));
+
+ /* wait for elf status == 4 */
+ err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET, 1, 4, 300); /* 3sec */
+ if (err < 0) {
+ dev_err(&mgr->pci->dev, "elf could not be started\n");
+ return err;
+ }
+
+ /* miXart waits at this point on the pointer to the flow table */
+ writel_be( (u32)mgr->flowinfo.addr, MIXART_MEM( mgr, MIXART_FLOWTABLE_PTR ) ); /* give pointer of flow table to miXart */
+
+ return 0; /* return, another xilinx file has to be loaded before */
+
+ case MIXART_AESEBUBOARD_XLX_INDEX:
+ default:
+
+ /* elf and xilinx should be loaded */
+ if (status_elf != 4 || status_xilinx != 4) {
+ dev_err(&mgr->pci->dev, "xilinx or elf not "
+ "successfully loaded\n");
+ return -EIO; /* modprob -r may help ? */
+ }
+
+ /* wait for daughter detection != 0 */
+ err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DBRD_PRESENCE_OFFSET, 0, 0, 30); /* 300msec */
+ if (err < 0) {
+ dev_err(&mgr->pci->dev, "error starting elf file\n");
+ return err;
+ }
+
+ /* the board type can now be retrieved */
+ mgr->board_type = (DAUGHTER_TYPE_MASK & readl_be( MIXART_MEM( mgr, MIXART_PSEUDOREG_DBRD_TYPE_OFFSET)));
+
+ if (mgr->board_type == MIXART_DAUGHTER_TYPE_NONE)
+ break; /* no daughter board; the file does not have to be loaded, continue after the switch */
+
+ /* only if aesebu daughter board presence (elf code must run) */
+ if (mgr->board_type != MIXART_DAUGHTER_TYPE_AES )
+ return -EINVAL;
+
+ /* daughter should be idle */
+ if (status_daught != 0) {
+ dev_err(&mgr->pci->dev,
+ "daughter load error ! status = %d\n",
+ status_daught);
+ return -EIO; /* modprob -r may help ? */
+ }
+
+ /* check daughterboard xilinx validity */
+ if (((u32*)(dsp->data))[0] == 0xffffffff)
+ return -EINVAL;
+ if (dsp->size % 4)
+ return -EINVAL;
+
+ /* inform mixart about the size of the file */
+ writel_be( dsp->size, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_SIZE_OFFSET ));
+
+ /* set daughterboard status to 1 */
+ writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET ));
+
+ /* wait for status == 2 */
+ err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET, 1, 2, 30); /* 300msec */
+ if (err < 0) {
+ dev_err(&mgr->pci->dev, "daughter board load error\n");
+ return err;
+ }
+
+ /* get the address where to write the file */
+ val = readl_be( MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_BASE_ADDR_OFFSET ));
+ if (!val)
+ return -EINVAL;
+
+ /* copy daughterboard xilinx code */
+ memcpy_toio( MIXART_MEM( mgr, val), dsp->data, dsp->size);
+
+ /* set daughterboard status to 4 */
+ writel_be( 4, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET ));
+
+ /* continue with init */
+ break;
+ } /* end of switch file index*/
+
+ /* wait for daughter status == 3 */
+ err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET, 1, 3, 300); /* 3sec */
+ if (err < 0) {
+ dev_err(&mgr->pci->dev,
+ "daughter board could not be initialised\n");
+ return err;
+ }
+
+ /* init mailbox (communication with embedded) */
+ snd_mixart_init_mailbox(mgr);
+
+ /* first communication with embedded */
+ err = mixart_first_init(mgr);
+ if (err < 0) {
+ dev_err(&mgr->pci->dev, "miXart could not be set up\n");
+ return err;
+ }
+
+ /* create devices and mixer in accordance with HW options*/
+ for (card_index = 0; card_index < mgr->num_cards; card_index++) {
+ struct snd_mixart *chip = mgr->chip[card_index];
+
+ err = snd_mixart_create_pcm(chip);
+ if (err < 0)
+ return err;
+
+ if (card_index == 0) {
+ err = snd_mixart_create_mixer(chip->mgr);
+ if (err < 0)
+ return err;
+ }
+
+ err = snd_card_register(chip->card);
+ if (err < 0)
+ return err;
+ }
+
+ dev_dbg(&mgr->pci->dev,
+ "miXart firmware downloaded and successfully set up\n");
+
+ return 0;
+}
+
+
+int snd_mixart_setup_firmware(struct mixart_mgr *mgr)
+{
+ static const char * const fw_files[3] = {
+ "miXart8.xlx", "miXart8.elf", "miXart8AES.xlx"
+ };
+ char path[32];
+
+ const struct firmware *fw_entry;
+ int i, err;
+
+ for (i = 0; i < 3; i++) {
+ sprintf(path, "mixart/%s", fw_files[i]);
+ if (request_firmware(&fw_entry, path, &mgr->pci->dev)) {
+ dev_err(&mgr->pci->dev,
+ "miXart: can't load firmware %s\n", path);
+ return -ENOENT;
+ }
+ /* fake hwdep dsp record */
+ err = mixart_dsp_load(mgr, i, fw_entry);
+ release_firmware(fw_entry);
+ if (err < 0)
+ return err;
+ mgr->dsp_loaded |= 1 << i;
+ }
+ return 0;
+}
+
+MODULE_FIRMWARE("mixart/miXart8.xlx");
+MODULE_FIRMWARE("mixart/miXart8.elf");
+MODULE_FIRMWARE("mixart/miXart8AES.xlx");
diff --git a/sound/pci/mixart/mixart_hwdep.h b/sound/pci/mixart/mixart_hwdep.h
new file mode 100644
index 000000000..69f45bb4d
--- /dev/null
+++ b/sound/pci/mixart/mixart_hwdep.h
@@ -0,0 +1,142 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Driver for Digigram miXart soundcards
+ *
+ * definitions and makros for basic card access
+ *
+ * Copyright (c) 2003 by Digigram <alsa@digigram.com>
+ */
+
+#ifndef __SOUND_MIXART_HWDEP_H
+#define __SOUND_MIXART_HWDEP_H
+
+#include <sound/hwdep.h>
+
+#ifndef readl_be
+#define readl_be(x) be32_to_cpu((__force __be32)__raw_readl(x))
+#endif
+
+#ifndef writel_be
+#define writel_be(data,addr) __raw_writel((__force u32)cpu_to_be32(data),addr)
+#endif
+
+#ifndef readl_le
+#define readl_le(x) le32_to_cpu((__force __le32)__raw_readl(x))
+#endif
+
+#ifndef writel_le
+#define writel_le(data,addr) __raw_writel((__force u32)cpu_to_le32(data),addr)
+#endif
+
+#define MIXART_MEM(mgr,x) ((mgr)->mem[0].virt + (x))
+#define MIXART_REG(mgr,x) ((mgr)->mem[1].virt + (x))
+
+
+/* Daughter board Type */
+#define DAUGHTER_TYPE_MASK 0x0F
+#define DAUGHTER_VER_MASK 0xF0
+#define DAUGHTER_TYPEVER_MASK (DAUGHTER_TYPE_MASK|DAUGHTER_VER_MASK)
+
+#define MIXART_DAUGHTER_TYPE_NONE 0x00
+#define MIXART_DAUGHTER_TYPE_COBRANET 0x08
+#define MIXART_DAUGHTER_TYPE_AES 0x0E
+
+
+
+#define MIXART_BA0_SIZE (16 * 1024 * 1024) /* 16M */
+#define MIXART_BA1_SIZE (4 * 1024) /* 4k */
+
+/*
+ * -----------BAR 0 --------------------------------------------------------------------------------------------------------
+ */
+#define MIXART_PSEUDOREG 0x2000 /* base address for pseudoregister */
+
+#define MIXART_PSEUDOREG_BOARDNUMBER MIXART_PSEUDOREG+0 /* board number */
+
+/* perfmeter (available when elf loaded)*/
+#define MIXART_PSEUDOREG_PERF_STREAM_LOAD_OFFSET MIXART_PSEUDOREG+0x70 /* streaming load */
+#define MIXART_PSEUDOREG_PERF_SYSTEM_LOAD_OFFSET MIXART_PSEUDOREG+0x78 /* system load (reference)*/
+#define MIXART_PSEUDOREG_PERF_MAILBX_LOAD_OFFSET MIXART_PSEUDOREG+0x7C /* mailbox load */
+#define MIXART_PSEUDOREG_PERF_INTERR_LOAD_OFFSET MIXART_PSEUDOREG+0x74 /* interrupt handling load */
+
+/* motherboard xilinx loader info */
+#define MIXART_PSEUDOREG_MXLX_BASE_ADDR_OFFSET MIXART_PSEUDOREG+0x9C /* 0x00600000 */
+#define MIXART_PSEUDOREG_MXLX_SIZE_OFFSET MIXART_PSEUDOREG+0xA0 /* xilinx size in bytes */
+#define MIXART_PSEUDOREG_MXLX_STATUS_OFFSET MIXART_PSEUDOREG+0xA4 /* status = EMBEBBED_STAT_XXX */
+
+/* elf loader info */
+#define MIXART_PSEUDOREG_ELF_STATUS_OFFSET MIXART_PSEUDOREG+0xB0 /* status = EMBEBBED_STAT_XXX */
+
+/*
+* after the elf code is loaded, and the flowtable info was passed to it,
+* the driver polls on this address, until it shows 1 (presence) or 2 (absence)
+* once it is non-zero, the daughter board type may be read
+*/
+#define MIXART_PSEUDOREG_DBRD_PRESENCE_OFFSET MIXART_PSEUDOREG+0x990
+
+/* Global info structure */
+#define MIXART_PSEUDOREG_DBRD_TYPE_OFFSET MIXART_PSEUDOREG+0x994 /* Type and version of daughterboard */
+
+
+/* daughterboard xilinx loader info */
+#define MIXART_PSEUDOREG_DXLX_BASE_ADDR_OFFSET MIXART_PSEUDOREG+0x998 /* get the address here where to write the file */
+#define MIXART_PSEUDOREG_DXLX_SIZE_OFFSET MIXART_PSEUDOREG+0x99C /* xilinx size in bytes */
+#define MIXART_PSEUDOREG_DXLX_STATUS_OFFSET MIXART_PSEUDOREG+0x9A0 /* status = EMBEBBED_STAT_XXX */
+
+/* */
+#define MIXART_FLOWTABLE_PTR 0x3000 /* pointer to flow table */
+
+/* mailbox addresses */
+
+/* message DRV -> EMB */
+#define MSG_INBOUND_POST_HEAD 0x010008 /* DRV posts MF + increment4 */
+#define MSG_INBOUND_POST_TAIL 0x01000C /* EMB gets MF + increment4 */
+/* message EMB -> DRV */
+#define MSG_OUTBOUND_POST_TAIL 0x01001C /* DRV gets MF + increment4 */
+#define MSG_OUTBOUND_POST_HEAD 0x010018 /* EMB posts MF + increment4 */
+/* Get Free Frames */
+#define MSG_INBOUND_FREE_TAIL 0x010004 /* DRV gets MFA + increment4 */
+#define MSG_OUTBOUND_FREE_TAIL 0x010014 /* EMB gets MFA + increment4 */
+/* Put Free Frames */
+#define MSG_OUTBOUND_FREE_HEAD 0x010010 /* DRV puts MFA + increment4 */
+#define MSG_INBOUND_FREE_HEAD 0x010000 /* EMB puts MFA + increment4 */
+
+/* firmware addresses of the message fifos */
+#define MSG_BOUND_STACK_SIZE 0x004000 /* size of each following stack */
+/* posted messages */
+#define MSG_OUTBOUND_POST_STACK 0x108000 /* stack of messages to the DRV */
+#define MSG_INBOUND_POST_STACK 0x104000 /* stack of messages to the EMB */
+/* available empty messages */
+#define MSG_OUTBOUND_FREE_STACK 0x10C000 /* stack of free enveloped for EMB */
+#define MSG_INBOUND_FREE_STACK 0x100000 /* stack of free enveloped for DRV */
+
+
+/* defines for mailbox message frames */
+#define MSG_FRAME_OFFSET 0x64
+#define MSG_FRAME_SIZE 0x6400
+#define MSG_FRAME_NUMBER 32
+#define MSG_FROM_AGENT_ITMF_OFFSET (MSG_FRAME_OFFSET + (MSG_FRAME_SIZE * MSG_FRAME_NUMBER))
+#define MSG_TO_AGENT_ITMF_OFFSET (MSG_FROM_AGENT_ITMF_OFFSET + MSG_FRAME_SIZE)
+#define MSG_HOST_RSC_PROTECTION (MSG_TO_AGENT_ITMF_OFFSET + MSG_FRAME_SIZE)
+#define MSG_AGENT_RSC_PROTECTION (MSG_HOST_RSC_PROTECTION + 4)
+
+
+/*
+ * -----------BAR 1 --------------------------------------------------------------------------------------------------------
+ */
+
+/* interrupt addresses and constants */
+#define MIXART_PCI_OMIMR_OFFSET 0x34 /* outbound message interrupt mask register */
+#define MIXART_PCI_OMISR_OFFSET 0x30 /* outbound message interrupt status register */
+#define MIXART_PCI_ODBR_OFFSET 0x60 /* outbound doorbell register */
+
+#define MIXART_BA1_BRUTAL_RESET_OFFSET 0x68 /* write 1 in LSBit to reset board */
+
+#define MIXART_HOST_ALL_INTERRUPT_MASKED 0x02B /* 0000 0010 1011 */
+#define MIXART_ALLOW_OUTBOUND_DOORBELL 0x023 /* 0000 0010 0011 */
+#define MIXART_OIDI 0x008 /* 0000 0000 1000 */
+
+
+int snd_mixart_setup_firmware(struct mixart_mgr *mgr);
+
+#endif /* __SOUND_MIXART_HWDEP_H */
diff --git a/sound/pci/mixart/mixart_mixer.c b/sound/pci/mixart/mixart_mixer.c
new file mode 100644
index 000000000..2727f3345
--- /dev/null
+++ b/sound/pci/mixart/mixart_mixer.c
@@ -0,0 +1,1192 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Digigram miXart soundcards
+ *
+ * mixer callbacks
+ *
+ * Copyright (c) 2003 by Digigram <alsa@digigram.com>
+ */
+
+#include <linux/time.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+
+#include <sound/core.h>
+#include "mixart.h"
+#include "mixart_core.h"
+#include "mixart_hwdep.h"
+#include <sound/control.h>
+#include <sound/tlv.h>
+#include "mixart_mixer.h"
+
+static const u32 mixart_analog_level[256] = {
+ 0xc2c00000, /* [000] -96.0 dB */
+ 0xc2bf0000, /* [001] -95.5 dB */
+ 0xc2be0000, /* [002] -95.0 dB */
+ 0xc2bd0000, /* [003] -94.5 dB */
+ 0xc2bc0000, /* [004] -94.0 dB */
+ 0xc2bb0000, /* [005] -93.5 dB */
+ 0xc2ba0000, /* [006] -93.0 dB */
+ 0xc2b90000, /* [007] -92.5 dB */
+ 0xc2b80000, /* [008] -92.0 dB */
+ 0xc2b70000, /* [009] -91.5 dB */
+ 0xc2b60000, /* [010] -91.0 dB */
+ 0xc2b50000, /* [011] -90.5 dB */
+ 0xc2b40000, /* [012] -90.0 dB */
+ 0xc2b30000, /* [013] -89.5 dB */
+ 0xc2b20000, /* [014] -89.0 dB */
+ 0xc2b10000, /* [015] -88.5 dB */
+ 0xc2b00000, /* [016] -88.0 dB */
+ 0xc2af0000, /* [017] -87.5 dB */
+ 0xc2ae0000, /* [018] -87.0 dB */
+ 0xc2ad0000, /* [019] -86.5 dB */
+ 0xc2ac0000, /* [020] -86.0 dB */
+ 0xc2ab0000, /* [021] -85.5 dB */
+ 0xc2aa0000, /* [022] -85.0 dB */
+ 0xc2a90000, /* [023] -84.5 dB */
+ 0xc2a80000, /* [024] -84.0 dB */
+ 0xc2a70000, /* [025] -83.5 dB */
+ 0xc2a60000, /* [026] -83.0 dB */
+ 0xc2a50000, /* [027] -82.5 dB */
+ 0xc2a40000, /* [028] -82.0 dB */
+ 0xc2a30000, /* [029] -81.5 dB */
+ 0xc2a20000, /* [030] -81.0 dB */
+ 0xc2a10000, /* [031] -80.5 dB */
+ 0xc2a00000, /* [032] -80.0 dB */
+ 0xc29f0000, /* [033] -79.5 dB */
+ 0xc29e0000, /* [034] -79.0 dB */
+ 0xc29d0000, /* [035] -78.5 dB */
+ 0xc29c0000, /* [036] -78.0 dB */
+ 0xc29b0000, /* [037] -77.5 dB */
+ 0xc29a0000, /* [038] -77.0 dB */
+ 0xc2990000, /* [039] -76.5 dB */
+ 0xc2980000, /* [040] -76.0 dB */
+ 0xc2970000, /* [041] -75.5 dB */
+ 0xc2960000, /* [042] -75.0 dB */
+ 0xc2950000, /* [043] -74.5 dB */
+ 0xc2940000, /* [044] -74.0 dB */
+ 0xc2930000, /* [045] -73.5 dB */
+ 0xc2920000, /* [046] -73.0 dB */
+ 0xc2910000, /* [047] -72.5 dB */
+ 0xc2900000, /* [048] -72.0 dB */
+ 0xc28f0000, /* [049] -71.5 dB */
+ 0xc28e0000, /* [050] -71.0 dB */
+ 0xc28d0000, /* [051] -70.5 dB */
+ 0xc28c0000, /* [052] -70.0 dB */
+ 0xc28b0000, /* [053] -69.5 dB */
+ 0xc28a0000, /* [054] -69.0 dB */
+ 0xc2890000, /* [055] -68.5 dB */
+ 0xc2880000, /* [056] -68.0 dB */
+ 0xc2870000, /* [057] -67.5 dB */
+ 0xc2860000, /* [058] -67.0 dB */
+ 0xc2850000, /* [059] -66.5 dB */
+ 0xc2840000, /* [060] -66.0 dB */
+ 0xc2830000, /* [061] -65.5 dB */
+ 0xc2820000, /* [062] -65.0 dB */
+ 0xc2810000, /* [063] -64.5 dB */
+ 0xc2800000, /* [064] -64.0 dB */
+ 0xc27e0000, /* [065] -63.5 dB */
+ 0xc27c0000, /* [066] -63.0 dB */
+ 0xc27a0000, /* [067] -62.5 dB */
+ 0xc2780000, /* [068] -62.0 dB */
+ 0xc2760000, /* [069] -61.5 dB */
+ 0xc2740000, /* [070] -61.0 dB */
+ 0xc2720000, /* [071] -60.5 dB */
+ 0xc2700000, /* [072] -60.0 dB */
+ 0xc26e0000, /* [073] -59.5 dB */
+ 0xc26c0000, /* [074] -59.0 dB */
+ 0xc26a0000, /* [075] -58.5 dB */
+ 0xc2680000, /* [076] -58.0 dB */
+ 0xc2660000, /* [077] -57.5 dB */
+ 0xc2640000, /* [078] -57.0 dB */
+ 0xc2620000, /* [079] -56.5 dB */
+ 0xc2600000, /* [080] -56.0 dB */
+ 0xc25e0000, /* [081] -55.5 dB */
+ 0xc25c0000, /* [082] -55.0 dB */
+ 0xc25a0000, /* [083] -54.5 dB */
+ 0xc2580000, /* [084] -54.0 dB */
+ 0xc2560000, /* [085] -53.5 dB */
+ 0xc2540000, /* [086] -53.0 dB */
+ 0xc2520000, /* [087] -52.5 dB */
+ 0xc2500000, /* [088] -52.0 dB */
+ 0xc24e0000, /* [089] -51.5 dB */
+ 0xc24c0000, /* [090] -51.0 dB */
+ 0xc24a0000, /* [091] -50.5 dB */
+ 0xc2480000, /* [092] -50.0 dB */
+ 0xc2460000, /* [093] -49.5 dB */
+ 0xc2440000, /* [094] -49.0 dB */
+ 0xc2420000, /* [095] -48.5 dB */
+ 0xc2400000, /* [096] -48.0 dB */
+ 0xc23e0000, /* [097] -47.5 dB */
+ 0xc23c0000, /* [098] -47.0 dB */
+ 0xc23a0000, /* [099] -46.5 dB */
+ 0xc2380000, /* [100] -46.0 dB */
+ 0xc2360000, /* [101] -45.5 dB */
+ 0xc2340000, /* [102] -45.0 dB */
+ 0xc2320000, /* [103] -44.5 dB */
+ 0xc2300000, /* [104] -44.0 dB */
+ 0xc22e0000, /* [105] -43.5 dB */
+ 0xc22c0000, /* [106] -43.0 dB */
+ 0xc22a0000, /* [107] -42.5 dB */
+ 0xc2280000, /* [108] -42.0 dB */
+ 0xc2260000, /* [109] -41.5 dB */
+ 0xc2240000, /* [110] -41.0 dB */
+ 0xc2220000, /* [111] -40.5 dB */
+ 0xc2200000, /* [112] -40.0 dB */
+ 0xc21e0000, /* [113] -39.5 dB */
+ 0xc21c0000, /* [114] -39.0 dB */
+ 0xc21a0000, /* [115] -38.5 dB */
+ 0xc2180000, /* [116] -38.0 dB */
+ 0xc2160000, /* [117] -37.5 dB */
+ 0xc2140000, /* [118] -37.0 dB */
+ 0xc2120000, /* [119] -36.5 dB */
+ 0xc2100000, /* [120] -36.0 dB */
+ 0xc20e0000, /* [121] -35.5 dB */
+ 0xc20c0000, /* [122] -35.0 dB */
+ 0xc20a0000, /* [123] -34.5 dB */
+ 0xc2080000, /* [124] -34.0 dB */
+ 0xc2060000, /* [125] -33.5 dB */
+ 0xc2040000, /* [126] -33.0 dB */
+ 0xc2020000, /* [127] -32.5 dB */
+ 0xc2000000, /* [128] -32.0 dB */
+ 0xc1fc0000, /* [129] -31.5 dB */
+ 0xc1f80000, /* [130] -31.0 dB */
+ 0xc1f40000, /* [131] -30.5 dB */
+ 0xc1f00000, /* [132] -30.0 dB */
+ 0xc1ec0000, /* [133] -29.5 dB */
+ 0xc1e80000, /* [134] -29.0 dB */
+ 0xc1e40000, /* [135] -28.5 dB */
+ 0xc1e00000, /* [136] -28.0 dB */
+ 0xc1dc0000, /* [137] -27.5 dB */
+ 0xc1d80000, /* [138] -27.0 dB */
+ 0xc1d40000, /* [139] -26.5 dB */
+ 0xc1d00000, /* [140] -26.0 dB */
+ 0xc1cc0000, /* [141] -25.5 dB */
+ 0xc1c80000, /* [142] -25.0 dB */
+ 0xc1c40000, /* [143] -24.5 dB */
+ 0xc1c00000, /* [144] -24.0 dB */
+ 0xc1bc0000, /* [145] -23.5 dB */
+ 0xc1b80000, /* [146] -23.0 dB */
+ 0xc1b40000, /* [147] -22.5 dB */
+ 0xc1b00000, /* [148] -22.0 dB */
+ 0xc1ac0000, /* [149] -21.5 dB */
+ 0xc1a80000, /* [150] -21.0 dB */
+ 0xc1a40000, /* [151] -20.5 dB */
+ 0xc1a00000, /* [152] -20.0 dB */
+ 0xc19c0000, /* [153] -19.5 dB */
+ 0xc1980000, /* [154] -19.0 dB */
+ 0xc1940000, /* [155] -18.5 dB */
+ 0xc1900000, /* [156] -18.0 dB */
+ 0xc18c0000, /* [157] -17.5 dB */
+ 0xc1880000, /* [158] -17.0 dB */
+ 0xc1840000, /* [159] -16.5 dB */
+ 0xc1800000, /* [160] -16.0 dB */
+ 0xc1780000, /* [161] -15.5 dB */
+ 0xc1700000, /* [162] -15.0 dB */
+ 0xc1680000, /* [163] -14.5 dB */
+ 0xc1600000, /* [164] -14.0 dB */
+ 0xc1580000, /* [165] -13.5 dB */
+ 0xc1500000, /* [166] -13.0 dB */
+ 0xc1480000, /* [167] -12.5 dB */
+ 0xc1400000, /* [168] -12.0 dB */
+ 0xc1380000, /* [169] -11.5 dB */
+ 0xc1300000, /* [170] -11.0 dB */
+ 0xc1280000, /* [171] -10.5 dB */
+ 0xc1200000, /* [172] -10.0 dB */
+ 0xc1180000, /* [173] -9.5 dB */
+ 0xc1100000, /* [174] -9.0 dB */
+ 0xc1080000, /* [175] -8.5 dB */
+ 0xc1000000, /* [176] -8.0 dB */
+ 0xc0f00000, /* [177] -7.5 dB */
+ 0xc0e00000, /* [178] -7.0 dB */
+ 0xc0d00000, /* [179] -6.5 dB */
+ 0xc0c00000, /* [180] -6.0 dB */
+ 0xc0b00000, /* [181] -5.5 dB */
+ 0xc0a00000, /* [182] -5.0 dB */
+ 0xc0900000, /* [183] -4.5 dB */
+ 0xc0800000, /* [184] -4.0 dB */
+ 0xc0600000, /* [185] -3.5 dB */
+ 0xc0400000, /* [186] -3.0 dB */
+ 0xc0200000, /* [187] -2.5 dB */
+ 0xc0000000, /* [188] -2.0 dB */
+ 0xbfc00000, /* [189] -1.5 dB */
+ 0xbf800000, /* [190] -1.0 dB */
+ 0xbf000000, /* [191] -0.5 dB */
+ 0x00000000, /* [192] 0.0 dB */
+ 0x3f000000, /* [193] 0.5 dB */
+ 0x3f800000, /* [194] 1.0 dB */
+ 0x3fc00000, /* [195] 1.5 dB */
+ 0x40000000, /* [196] 2.0 dB */
+ 0x40200000, /* [197] 2.5 dB */
+ 0x40400000, /* [198] 3.0 dB */
+ 0x40600000, /* [199] 3.5 dB */
+ 0x40800000, /* [200] 4.0 dB */
+ 0x40900000, /* [201] 4.5 dB */
+ 0x40a00000, /* [202] 5.0 dB */
+ 0x40b00000, /* [203] 5.5 dB */
+ 0x40c00000, /* [204] 6.0 dB */
+ 0x40d00000, /* [205] 6.5 dB */
+ 0x40e00000, /* [206] 7.0 dB */
+ 0x40f00000, /* [207] 7.5 dB */
+ 0x41000000, /* [208] 8.0 dB */
+ 0x41080000, /* [209] 8.5 dB */
+ 0x41100000, /* [210] 9.0 dB */
+ 0x41180000, /* [211] 9.5 dB */
+ 0x41200000, /* [212] 10.0 dB */
+ 0x41280000, /* [213] 10.5 dB */
+ 0x41300000, /* [214] 11.0 dB */
+ 0x41380000, /* [215] 11.5 dB */
+ 0x41400000, /* [216] 12.0 dB */
+ 0x41480000, /* [217] 12.5 dB */
+ 0x41500000, /* [218] 13.0 dB */
+ 0x41580000, /* [219] 13.5 dB */
+ 0x41600000, /* [220] 14.0 dB */
+ 0x41680000, /* [221] 14.5 dB */
+ 0x41700000, /* [222] 15.0 dB */
+ 0x41780000, /* [223] 15.5 dB */
+ 0x41800000, /* [224] 16.0 dB */
+ 0x41840000, /* [225] 16.5 dB */
+ 0x41880000, /* [226] 17.0 dB */
+ 0x418c0000, /* [227] 17.5 dB */
+ 0x41900000, /* [228] 18.0 dB */
+ 0x41940000, /* [229] 18.5 dB */
+ 0x41980000, /* [230] 19.0 dB */
+ 0x419c0000, /* [231] 19.5 dB */
+ 0x41a00000, /* [232] 20.0 dB */
+ 0x41a40000, /* [233] 20.5 dB */
+ 0x41a80000, /* [234] 21.0 dB */
+ 0x41ac0000, /* [235] 21.5 dB */
+ 0x41b00000, /* [236] 22.0 dB */
+ 0x41b40000, /* [237] 22.5 dB */
+ 0x41b80000, /* [238] 23.0 dB */
+ 0x41bc0000, /* [239] 23.5 dB */
+ 0x41c00000, /* [240] 24.0 dB */
+ 0x41c40000, /* [241] 24.5 dB */
+ 0x41c80000, /* [242] 25.0 dB */
+ 0x41cc0000, /* [243] 25.5 dB */
+ 0x41d00000, /* [244] 26.0 dB */
+ 0x41d40000, /* [245] 26.5 dB */
+ 0x41d80000, /* [246] 27.0 dB */
+ 0x41dc0000, /* [247] 27.5 dB */
+ 0x41e00000, /* [248] 28.0 dB */
+ 0x41e40000, /* [249] 28.5 dB */
+ 0x41e80000, /* [250] 29.0 dB */
+ 0x41ec0000, /* [251] 29.5 dB */
+ 0x41f00000, /* [252] 30.0 dB */
+ 0x41f40000, /* [253] 30.5 dB */
+ 0x41f80000, /* [254] 31.0 dB */
+ 0x41fc0000, /* [255] 31.5 dB */
+};
+
+#define MIXART_ANALOG_CAPTURE_LEVEL_MIN 0 /* -96.0 dB + 8.0 dB = -88.0 dB */
+#define MIXART_ANALOG_CAPTURE_LEVEL_MAX 255 /* 31.5 dB + 8.0 dB = 39.5 dB */
+#define MIXART_ANALOG_CAPTURE_ZERO_LEVEL 176 /* -8.0 dB + 8.0 dB = 0.0 dB */
+
+#define MIXART_ANALOG_PLAYBACK_LEVEL_MIN 0 /* -96.0 dB + 1.5 dB = -94.5 dB (possible is down to (-114.0+1.5)dB) */
+#define MIXART_ANALOG_PLAYBACK_LEVEL_MAX 192 /* 0.0 dB + 1.5 dB = 1.5 dB */
+#define MIXART_ANALOG_PLAYBACK_ZERO_LEVEL 189 /* -1.5 dB + 1.5 dB = 0.0 dB */
+
+static int mixart_update_analog_audio_level(struct snd_mixart* chip, int is_capture)
+{
+ int i, err;
+ struct mixart_msg request;
+ struct mixart_io_level io_level;
+ struct mixart_return_uid resp;
+
+ memset(&io_level, 0, sizeof(io_level));
+ io_level.channel = -1; /* left and right */
+
+ for(i=0; i<2; i++) {
+ if(is_capture) {
+ io_level.level[i].analog_level = mixart_analog_level[chip->analog_capture_volume[i]];
+ } else {
+ if(chip->analog_playback_active[i])
+ io_level.level[i].analog_level = mixart_analog_level[chip->analog_playback_volume[i]];
+ else
+ io_level.level[i].analog_level = mixart_analog_level[MIXART_ANALOG_PLAYBACK_LEVEL_MIN];
+ }
+ }
+
+ if(is_capture) request.uid = chip->uid_in_analog_physio;
+ else request.uid = chip->uid_out_analog_physio;
+ request.message_id = MSG_PHYSICALIO_SET_LEVEL;
+ request.data = &io_level;
+ request.size = sizeof(io_level);
+
+ err = snd_mixart_send_msg(chip->mgr, &request, sizeof(resp), &resp);
+ if((err<0) || (resp.error_code)) {
+ dev_dbg(chip->card->dev,
+ "error MSG_PHYSICALIO_SET_LEVEL card(%d) is_capture(%d) error_code(%x)\n",
+ chip->chip_idx, is_capture, resp.error_code);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * analog level control
+ */
+static int mixart_analog_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ if(kcontrol->private_value == 0) { /* playback */
+ uinfo->value.integer.min = MIXART_ANALOG_PLAYBACK_LEVEL_MIN; /* -96 dB */
+ uinfo->value.integer.max = MIXART_ANALOG_PLAYBACK_LEVEL_MAX; /* 0 dB */
+ } else { /* capture */
+ uinfo->value.integer.min = MIXART_ANALOG_CAPTURE_LEVEL_MIN; /* -96 dB */
+ uinfo->value.integer.max = MIXART_ANALOG_CAPTURE_LEVEL_MAX; /* 31.5 dB */
+ }
+ return 0;
+}
+
+static int mixart_analog_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_mixart *chip = snd_kcontrol_chip(kcontrol);
+ mutex_lock(&chip->mgr->mixer_mutex);
+ if(kcontrol->private_value == 0) { /* playback */
+ ucontrol->value.integer.value[0] = chip->analog_playback_volume[0];
+ ucontrol->value.integer.value[1] = chip->analog_playback_volume[1];
+ } else { /* capture */
+ ucontrol->value.integer.value[0] = chip->analog_capture_volume[0];
+ ucontrol->value.integer.value[1] = chip->analog_capture_volume[1];
+ }
+ mutex_unlock(&chip->mgr->mixer_mutex);
+ return 0;
+}
+
+static int mixart_analog_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_mixart *chip = snd_kcontrol_chip(kcontrol);
+ int changed = 0;
+ int is_capture, i;
+
+ mutex_lock(&chip->mgr->mixer_mutex);
+ is_capture = (kcontrol->private_value != 0);
+ for (i = 0; i < 2; i++) {
+ int new_volume = ucontrol->value.integer.value[i];
+ int *stored_volume = is_capture ?
+ &chip->analog_capture_volume[i] :
+ &chip->analog_playback_volume[i];
+ if (is_capture) {
+ if (new_volume < MIXART_ANALOG_CAPTURE_LEVEL_MIN ||
+ new_volume > MIXART_ANALOG_CAPTURE_LEVEL_MAX)
+ continue;
+ } else {
+ if (new_volume < MIXART_ANALOG_PLAYBACK_LEVEL_MIN ||
+ new_volume > MIXART_ANALOG_PLAYBACK_LEVEL_MAX)
+ continue;
+ }
+ if (*stored_volume != new_volume) {
+ *stored_volume = new_volume;
+ changed = 1;
+ }
+ }
+ if (changed)
+ mixart_update_analog_audio_level(chip, is_capture);
+ mutex_unlock(&chip->mgr->mixer_mutex);
+ return changed;
+}
+
+static const DECLARE_TLV_DB_SCALE(db_scale_analog, -9600, 50, 0);
+
+static const struct snd_kcontrol_new mixart_control_analog_level = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ /* name will be filled later */
+ .info = mixart_analog_vol_info,
+ .get = mixart_analog_vol_get,
+ .put = mixart_analog_vol_put,
+ .tlv = { .p = db_scale_analog },
+};
+
+/* shared */
+#define mixart_sw_info snd_ctl_boolean_stereo_info
+
+static int mixart_audio_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_mixart *chip = snd_kcontrol_chip(kcontrol);
+
+ mutex_lock(&chip->mgr->mixer_mutex);
+ ucontrol->value.integer.value[0] = chip->analog_playback_active[0];
+ ucontrol->value.integer.value[1] = chip->analog_playback_active[1];
+ mutex_unlock(&chip->mgr->mixer_mutex);
+ return 0;
+}
+
+static int mixart_audio_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_mixart *chip = snd_kcontrol_chip(kcontrol);
+ int i, changed = 0;
+ mutex_lock(&chip->mgr->mixer_mutex);
+ for (i = 0; i < 2; i++) {
+ if (chip->analog_playback_active[i] !=
+ ucontrol->value.integer.value[i]) {
+ chip->analog_playback_active[i] =
+ !!ucontrol->value.integer.value[i];
+ changed = 1;
+ }
+ }
+ if (changed) /* update playback levels */
+ mixart_update_analog_audio_level(chip, 0);
+ mutex_unlock(&chip->mgr->mixer_mutex);
+ return changed;
+}
+
+static const struct snd_kcontrol_new mixart_control_output_switch = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = mixart_sw_info, /* shared */
+ .get = mixart_audio_sw_get,
+ .put = mixart_audio_sw_put
+};
+
+static const u32 mixart_digital_level[256] = {
+ 0x00000000, /* [000] = 0.00e+000 = mute if <= -109.5dB */
+ 0x366e1c7a, /* [001] = 3.55e-006 = pow(10.0, 0.05 * -109.0dB) */
+ 0x367c3860, /* [002] = 3.76e-006 = pow(10.0, 0.05 * -108.5dB) */
+ 0x36859525, /* [003] = 3.98e-006 = pow(10.0, 0.05 * -108.0dB) */
+ 0x368d7f74, /* [004] = 4.22e-006 = pow(10.0, 0.05 * -107.5dB) */
+ 0x3695e1d4, /* [005] = 4.47e-006 = pow(10.0, 0.05 * -107.0dB) */
+ 0x369ec362, /* [006] = 4.73e-006 = pow(10.0, 0.05 * -106.5dB) */
+ 0x36a82ba8, /* [007] = 5.01e-006 = pow(10.0, 0.05 * -106.0dB) */
+ 0x36b222a0, /* [008] = 5.31e-006 = pow(10.0, 0.05 * -105.5dB) */
+ 0x36bcb0c1, /* [009] = 5.62e-006 = pow(10.0, 0.05 * -105.0dB) */
+ 0x36c7defd, /* [010] = 5.96e-006 = pow(10.0, 0.05 * -104.5dB) */
+ 0x36d3b6d3, /* [011] = 6.31e-006 = pow(10.0, 0.05 * -104.0dB) */
+ 0x36e0424e, /* [012] = 6.68e-006 = pow(10.0, 0.05 * -103.5dB) */
+ 0x36ed8c14, /* [013] = 7.08e-006 = pow(10.0, 0.05 * -103.0dB) */
+ 0x36fb9f6c, /* [014] = 7.50e-006 = pow(10.0, 0.05 * -102.5dB) */
+ 0x37054423, /* [015] = 7.94e-006 = pow(10.0, 0.05 * -102.0dB) */
+ 0x370d29a5, /* [016] = 8.41e-006 = pow(10.0, 0.05 * -101.5dB) */
+ 0x371586f0, /* [017] = 8.91e-006 = pow(10.0, 0.05 * -101.0dB) */
+ 0x371e631b, /* [018] = 9.44e-006 = pow(10.0, 0.05 * -100.5dB) */
+ 0x3727c5ac, /* [019] = 1.00e-005 = pow(10.0, 0.05 * -100.0dB) */
+ 0x3731b69a, /* [020] = 1.06e-005 = pow(10.0, 0.05 * -99.5dB) */
+ 0x373c3e53, /* [021] = 1.12e-005 = pow(10.0, 0.05 * -99.0dB) */
+ 0x374765c8, /* [022] = 1.19e-005 = pow(10.0, 0.05 * -98.5dB) */
+ 0x3753366f, /* [023] = 1.26e-005 = pow(10.0, 0.05 * -98.0dB) */
+ 0x375fba4f, /* [024] = 1.33e-005 = pow(10.0, 0.05 * -97.5dB) */
+ 0x376cfc07, /* [025] = 1.41e-005 = pow(10.0, 0.05 * -97.0dB) */
+ 0x377b06d5, /* [026] = 1.50e-005 = pow(10.0, 0.05 * -96.5dB) */
+ 0x3784f352, /* [027] = 1.58e-005 = pow(10.0, 0.05 * -96.0dB) */
+ 0x378cd40b, /* [028] = 1.68e-005 = pow(10.0, 0.05 * -95.5dB) */
+ 0x37952c42, /* [029] = 1.78e-005 = pow(10.0, 0.05 * -95.0dB) */
+ 0x379e030e, /* [030] = 1.88e-005 = pow(10.0, 0.05 * -94.5dB) */
+ 0x37a75fef, /* [031] = 2.00e-005 = pow(10.0, 0.05 * -94.0dB) */
+ 0x37b14ad5, /* [032] = 2.11e-005 = pow(10.0, 0.05 * -93.5dB) */
+ 0x37bbcc2c, /* [033] = 2.24e-005 = pow(10.0, 0.05 * -93.0dB) */
+ 0x37c6ecdd, /* [034] = 2.37e-005 = pow(10.0, 0.05 * -92.5dB) */
+ 0x37d2b65a, /* [035] = 2.51e-005 = pow(10.0, 0.05 * -92.0dB) */
+ 0x37df32a3, /* [036] = 2.66e-005 = pow(10.0, 0.05 * -91.5dB) */
+ 0x37ec6c50, /* [037] = 2.82e-005 = pow(10.0, 0.05 * -91.0dB) */
+ 0x37fa6e9b, /* [038] = 2.99e-005 = pow(10.0, 0.05 * -90.5dB) */
+ 0x3804a2b3, /* [039] = 3.16e-005 = pow(10.0, 0.05 * -90.0dB) */
+ 0x380c7ea4, /* [040] = 3.35e-005 = pow(10.0, 0.05 * -89.5dB) */
+ 0x3814d1cc, /* [041] = 3.55e-005 = pow(10.0, 0.05 * -89.0dB) */
+ 0x381da33c, /* [042] = 3.76e-005 = pow(10.0, 0.05 * -88.5dB) */
+ 0x3826fa6f, /* [043] = 3.98e-005 = pow(10.0, 0.05 * -88.0dB) */
+ 0x3830df51, /* [044] = 4.22e-005 = pow(10.0, 0.05 * -87.5dB) */
+ 0x383b5a49, /* [045] = 4.47e-005 = pow(10.0, 0.05 * -87.0dB) */
+ 0x3846743b, /* [046] = 4.73e-005 = pow(10.0, 0.05 * -86.5dB) */
+ 0x38523692, /* [047] = 5.01e-005 = pow(10.0, 0.05 * -86.0dB) */
+ 0x385eab48, /* [048] = 5.31e-005 = pow(10.0, 0.05 * -85.5dB) */
+ 0x386bdcf1, /* [049] = 5.62e-005 = pow(10.0, 0.05 * -85.0dB) */
+ 0x3879d6bc, /* [050] = 5.96e-005 = pow(10.0, 0.05 * -84.5dB) */
+ 0x38845244, /* [051] = 6.31e-005 = pow(10.0, 0.05 * -84.0dB) */
+ 0x388c2971, /* [052] = 6.68e-005 = pow(10.0, 0.05 * -83.5dB) */
+ 0x3894778d, /* [053] = 7.08e-005 = pow(10.0, 0.05 * -83.0dB) */
+ 0x389d43a4, /* [054] = 7.50e-005 = pow(10.0, 0.05 * -82.5dB) */
+ 0x38a6952c, /* [055] = 7.94e-005 = pow(10.0, 0.05 * -82.0dB) */
+ 0x38b0740f, /* [056] = 8.41e-005 = pow(10.0, 0.05 * -81.5dB) */
+ 0x38bae8ac, /* [057] = 8.91e-005 = pow(10.0, 0.05 * -81.0dB) */
+ 0x38c5fbe2, /* [058] = 9.44e-005 = pow(10.0, 0.05 * -80.5dB) */
+ 0x38d1b717, /* [059] = 1.00e-004 = pow(10.0, 0.05 * -80.0dB) */
+ 0x38de2440, /* [060] = 1.06e-004 = pow(10.0, 0.05 * -79.5dB) */
+ 0x38eb4de8, /* [061] = 1.12e-004 = pow(10.0, 0.05 * -79.0dB) */
+ 0x38f93f3a, /* [062] = 1.19e-004 = pow(10.0, 0.05 * -78.5dB) */
+ 0x39040206, /* [063] = 1.26e-004 = pow(10.0, 0.05 * -78.0dB) */
+ 0x390bd472, /* [064] = 1.33e-004 = pow(10.0, 0.05 * -77.5dB) */
+ 0x39141d84, /* [065] = 1.41e-004 = pow(10.0, 0.05 * -77.0dB) */
+ 0x391ce445, /* [066] = 1.50e-004 = pow(10.0, 0.05 * -76.5dB) */
+ 0x39263027, /* [067] = 1.58e-004 = pow(10.0, 0.05 * -76.0dB) */
+ 0x3930090d, /* [068] = 1.68e-004 = pow(10.0, 0.05 * -75.5dB) */
+ 0x393a7753, /* [069] = 1.78e-004 = pow(10.0, 0.05 * -75.0dB) */
+ 0x394583d2, /* [070] = 1.88e-004 = pow(10.0, 0.05 * -74.5dB) */
+ 0x395137ea, /* [071] = 2.00e-004 = pow(10.0, 0.05 * -74.0dB) */
+ 0x395d9d8a, /* [072] = 2.11e-004 = pow(10.0, 0.05 * -73.5dB) */
+ 0x396abf37, /* [073] = 2.24e-004 = pow(10.0, 0.05 * -73.0dB) */
+ 0x3978a814, /* [074] = 2.37e-004 = pow(10.0, 0.05 * -72.5dB) */
+ 0x3983b1f8, /* [075] = 2.51e-004 = pow(10.0, 0.05 * -72.0dB) */
+ 0x398b7fa6, /* [076] = 2.66e-004 = pow(10.0, 0.05 * -71.5dB) */
+ 0x3993c3b2, /* [077] = 2.82e-004 = pow(10.0, 0.05 * -71.0dB) */
+ 0x399c8521, /* [078] = 2.99e-004 = pow(10.0, 0.05 * -70.5dB) */
+ 0x39a5cb5f, /* [079] = 3.16e-004 = pow(10.0, 0.05 * -70.0dB) */
+ 0x39af9e4d, /* [080] = 3.35e-004 = pow(10.0, 0.05 * -69.5dB) */
+ 0x39ba063f, /* [081] = 3.55e-004 = pow(10.0, 0.05 * -69.0dB) */
+ 0x39c50c0b, /* [082] = 3.76e-004 = pow(10.0, 0.05 * -68.5dB) */
+ 0x39d0b90a, /* [083] = 3.98e-004 = pow(10.0, 0.05 * -68.0dB) */
+ 0x39dd1726, /* [084] = 4.22e-004 = pow(10.0, 0.05 * -67.5dB) */
+ 0x39ea30db, /* [085] = 4.47e-004 = pow(10.0, 0.05 * -67.0dB) */
+ 0x39f81149, /* [086] = 4.73e-004 = pow(10.0, 0.05 * -66.5dB) */
+ 0x3a03621b, /* [087] = 5.01e-004 = pow(10.0, 0.05 * -66.0dB) */
+ 0x3a0b2b0d, /* [088] = 5.31e-004 = pow(10.0, 0.05 * -65.5dB) */
+ 0x3a136a16, /* [089] = 5.62e-004 = pow(10.0, 0.05 * -65.0dB) */
+ 0x3a1c2636, /* [090] = 5.96e-004 = pow(10.0, 0.05 * -64.5dB) */
+ 0x3a2566d5, /* [091] = 6.31e-004 = pow(10.0, 0.05 * -64.0dB) */
+ 0x3a2f33cd, /* [092] = 6.68e-004 = pow(10.0, 0.05 * -63.5dB) */
+ 0x3a399570, /* [093] = 7.08e-004 = pow(10.0, 0.05 * -63.0dB) */
+ 0x3a44948c, /* [094] = 7.50e-004 = pow(10.0, 0.05 * -62.5dB) */
+ 0x3a503a77, /* [095] = 7.94e-004 = pow(10.0, 0.05 * -62.0dB) */
+ 0x3a5c9112, /* [096] = 8.41e-004 = pow(10.0, 0.05 * -61.5dB) */
+ 0x3a69a2d7, /* [097] = 8.91e-004 = pow(10.0, 0.05 * -61.0dB) */
+ 0x3a777ada, /* [098] = 9.44e-004 = pow(10.0, 0.05 * -60.5dB) */
+ 0x3a83126f, /* [099] = 1.00e-003 = pow(10.0, 0.05 * -60.0dB) */
+ 0x3a8ad6a8, /* [100] = 1.06e-003 = pow(10.0, 0.05 * -59.5dB) */
+ 0x3a9310b1, /* [101] = 1.12e-003 = pow(10.0, 0.05 * -59.0dB) */
+ 0x3a9bc784, /* [102] = 1.19e-003 = pow(10.0, 0.05 * -58.5dB) */
+ 0x3aa50287, /* [103] = 1.26e-003 = pow(10.0, 0.05 * -58.0dB) */
+ 0x3aaec98e, /* [104] = 1.33e-003 = pow(10.0, 0.05 * -57.5dB) */
+ 0x3ab924e5, /* [105] = 1.41e-003 = pow(10.0, 0.05 * -57.0dB) */
+ 0x3ac41d56, /* [106] = 1.50e-003 = pow(10.0, 0.05 * -56.5dB) */
+ 0x3acfbc31, /* [107] = 1.58e-003 = pow(10.0, 0.05 * -56.0dB) */
+ 0x3adc0b51, /* [108] = 1.68e-003 = pow(10.0, 0.05 * -55.5dB) */
+ 0x3ae91528, /* [109] = 1.78e-003 = pow(10.0, 0.05 * -55.0dB) */
+ 0x3af6e4c6, /* [110] = 1.88e-003 = pow(10.0, 0.05 * -54.5dB) */
+ 0x3b02c2f2, /* [111] = 2.00e-003 = pow(10.0, 0.05 * -54.0dB) */
+ 0x3b0a8276, /* [112] = 2.11e-003 = pow(10.0, 0.05 * -53.5dB) */
+ 0x3b12b782, /* [113] = 2.24e-003 = pow(10.0, 0.05 * -53.0dB) */
+ 0x3b1b690d, /* [114] = 2.37e-003 = pow(10.0, 0.05 * -52.5dB) */
+ 0x3b249e76, /* [115] = 2.51e-003 = pow(10.0, 0.05 * -52.0dB) */
+ 0x3b2e5f8f, /* [116] = 2.66e-003 = pow(10.0, 0.05 * -51.5dB) */
+ 0x3b38b49f, /* [117] = 2.82e-003 = pow(10.0, 0.05 * -51.0dB) */
+ 0x3b43a669, /* [118] = 2.99e-003 = pow(10.0, 0.05 * -50.5dB) */
+ 0x3b4f3e37, /* [119] = 3.16e-003 = pow(10.0, 0.05 * -50.0dB) */
+ 0x3b5b85e0, /* [120] = 3.35e-003 = pow(10.0, 0.05 * -49.5dB) */
+ 0x3b6887cf, /* [121] = 3.55e-003 = pow(10.0, 0.05 * -49.0dB) */
+ 0x3b764f0e, /* [122] = 3.76e-003 = pow(10.0, 0.05 * -48.5dB) */
+ 0x3b8273a6, /* [123] = 3.98e-003 = pow(10.0, 0.05 * -48.0dB) */
+ 0x3b8a2e77, /* [124] = 4.22e-003 = pow(10.0, 0.05 * -47.5dB) */
+ 0x3b925e89, /* [125] = 4.47e-003 = pow(10.0, 0.05 * -47.0dB) */
+ 0x3b9b0ace, /* [126] = 4.73e-003 = pow(10.0, 0.05 * -46.5dB) */
+ 0x3ba43aa2, /* [127] = 5.01e-003 = pow(10.0, 0.05 * -46.0dB) */
+ 0x3badf5d1, /* [128] = 5.31e-003 = pow(10.0, 0.05 * -45.5dB) */
+ 0x3bb8449c, /* [129] = 5.62e-003 = pow(10.0, 0.05 * -45.0dB) */
+ 0x3bc32fc3, /* [130] = 5.96e-003 = pow(10.0, 0.05 * -44.5dB) */
+ 0x3bcec08a, /* [131] = 6.31e-003 = pow(10.0, 0.05 * -44.0dB) */
+ 0x3bdb00c0, /* [132] = 6.68e-003 = pow(10.0, 0.05 * -43.5dB) */
+ 0x3be7facc, /* [133] = 7.08e-003 = pow(10.0, 0.05 * -43.0dB) */
+ 0x3bf5b9b0, /* [134] = 7.50e-003 = pow(10.0, 0.05 * -42.5dB) */
+ 0x3c02248a, /* [135] = 7.94e-003 = pow(10.0, 0.05 * -42.0dB) */
+ 0x3c09daac, /* [136] = 8.41e-003 = pow(10.0, 0.05 * -41.5dB) */
+ 0x3c1205c6, /* [137] = 8.91e-003 = pow(10.0, 0.05 * -41.0dB) */
+ 0x3c1aacc8, /* [138] = 9.44e-003 = pow(10.0, 0.05 * -40.5dB) */
+ 0x3c23d70a, /* [139] = 1.00e-002 = pow(10.0, 0.05 * -40.0dB) */
+ 0x3c2d8c52, /* [140] = 1.06e-002 = pow(10.0, 0.05 * -39.5dB) */
+ 0x3c37d4dd, /* [141] = 1.12e-002 = pow(10.0, 0.05 * -39.0dB) */
+ 0x3c42b965, /* [142] = 1.19e-002 = pow(10.0, 0.05 * -38.5dB) */
+ 0x3c4e4329, /* [143] = 1.26e-002 = pow(10.0, 0.05 * -38.0dB) */
+ 0x3c5a7bf1, /* [144] = 1.33e-002 = pow(10.0, 0.05 * -37.5dB) */
+ 0x3c676e1e, /* [145] = 1.41e-002 = pow(10.0, 0.05 * -37.0dB) */
+ 0x3c7524ac, /* [146] = 1.50e-002 = pow(10.0, 0.05 * -36.5dB) */
+ 0x3c81d59f, /* [147] = 1.58e-002 = pow(10.0, 0.05 * -36.0dB) */
+ 0x3c898712, /* [148] = 1.68e-002 = pow(10.0, 0.05 * -35.5dB) */
+ 0x3c91ad39, /* [149] = 1.78e-002 = pow(10.0, 0.05 * -35.0dB) */
+ 0x3c9a4efc, /* [150] = 1.88e-002 = pow(10.0, 0.05 * -34.5dB) */
+ 0x3ca373af, /* [151] = 2.00e-002 = pow(10.0, 0.05 * -34.0dB) */
+ 0x3cad2314, /* [152] = 2.11e-002 = pow(10.0, 0.05 * -33.5dB) */
+ 0x3cb76563, /* [153] = 2.24e-002 = pow(10.0, 0.05 * -33.0dB) */
+ 0x3cc24350, /* [154] = 2.37e-002 = pow(10.0, 0.05 * -32.5dB) */
+ 0x3ccdc614, /* [155] = 2.51e-002 = pow(10.0, 0.05 * -32.0dB) */
+ 0x3cd9f773, /* [156] = 2.66e-002 = pow(10.0, 0.05 * -31.5dB) */
+ 0x3ce6e1c6, /* [157] = 2.82e-002 = pow(10.0, 0.05 * -31.0dB) */
+ 0x3cf49003, /* [158] = 2.99e-002 = pow(10.0, 0.05 * -30.5dB) */
+ 0x3d0186e2, /* [159] = 3.16e-002 = pow(10.0, 0.05 * -30.0dB) */
+ 0x3d0933ac, /* [160] = 3.35e-002 = pow(10.0, 0.05 * -29.5dB) */
+ 0x3d1154e1, /* [161] = 3.55e-002 = pow(10.0, 0.05 * -29.0dB) */
+ 0x3d19f169, /* [162] = 3.76e-002 = pow(10.0, 0.05 * -28.5dB) */
+ 0x3d231090, /* [163] = 3.98e-002 = pow(10.0, 0.05 * -28.0dB) */
+ 0x3d2cba15, /* [164] = 4.22e-002 = pow(10.0, 0.05 * -27.5dB) */
+ 0x3d36f62b, /* [165] = 4.47e-002 = pow(10.0, 0.05 * -27.0dB) */
+ 0x3d41cd81, /* [166] = 4.73e-002 = pow(10.0, 0.05 * -26.5dB) */
+ 0x3d4d494a, /* [167] = 5.01e-002 = pow(10.0, 0.05 * -26.0dB) */
+ 0x3d597345, /* [168] = 5.31e-002 = pow(10.0, 0.05 * -25.5dB) */
+ 0x3d6655c3, /* [169] = 5.62e-002 = pow(10.0, 0.05 * -25.0dB) */
+ 0x3d73fbb4, /* [170] = 5.96e-002 = pow(10.0, 0.05 * -24.5dB) */
+ 0x3d813856, /* [171] = 6.31e-002 = pow(10.0, 0.05 * -24.0dB) */
+ 0x3d88e078, /* [172] = 6.68e-002 = pow(10.0, 0.05 * -23.5dB) */
+ 0x3d90fcbf, /* [173] = 7.08e-002 = pow(10.0, 0.05 * -23.0dB) */
+ 0x3d99940e, /* [174] = 7.50e-002 = pow(10.0, 0.05 * -22.5dB) */
+ 0x3da2adad, /* [175] = 7.94e-002 = pow(10.0, 0.05 * -22.0dB) */
+ 0x3dac5156, /* [176] = 8.41e-002 = pow(10.0, 0.05 * -21.5dB) */
+ 0x3db68738, /* [177] = 8.91e-002 = pow(10.0, 0.05 * -21.0dB) */
+ 0x3dc157fb, /* [178] = 9.44e-002 = pow(10.0, 0.05 * -20.5dB) */
+ 0x3dcccccd, /* [179] = 1.00e-001 = pow(10.0, 0.05 * -20.0dB) */
+ 0x3dd8ef67, /* [180] = 1.06e-001 = pow(10.0, 0.05 * -19.5dB) */
+ 0x3de5ca15, /* [181] = 1.12e-001 = pow(10.0, 0.05 * -19.0dB) */
+ 0x3df367bf, /* [182] = 1.19e-001 = pow(10.0, 0.05 * -18.5dB) */
+ 0x3e00e9f9, /* [183] = 1.26e-001 = pow(10.0, 0.05 * -18.0dB) */
+ 0x3e088d77, /* [184] = 1.33e-001 = pow(10.0, 0.05 * -17.5dB) */
+ 0x3e10a4d3, /* [185] = 1.41e-001 = pow(10.0, 0.05 * -17.0dB) */
+ 0x3e1936ec, /* [186] = 1.50e-001 = pow(10.0, 0.05 * -16.5dB) */
+ 0x3e224b06, /* [187] = 1.58e-001 = pow(10.0, 0.05 * -16.0dB) */
+ 0x3e2be8d7, /* [188] = 1.68e-001 = pow(10.0, 0.05 * -15.5dB) */
+ 0x3e361887, /* [189] = 1.78e-001 = pow(10.0, 0.05 * -15.0dB) */
+ 0x3e40e2bb, /* [190] = 1.88e-001 = pow(10.0, 0.05 * -14.5dB) */
+ 0x3e4c509b, /* [191] = 2.00e-001 = pow(10.0, 0.05 * -14.0dB) */
+ 0x3e586bd9, /* [192] = 2.11e-001 = pow(10.0, 0.05 * -13.5dB) */
+ 0x3e653ebb, /* [193] = 2.24e-001 = pow(10.0, 0.05 * -13.0dB) */
+ 0x3e72d424, /* [194] = 2.37e-001 = pow(10.0, 0.05 * -12.5dB) */
+ 0x3e809bcc, /* [195] = 2.51e-001 = pow(10.0, 0.05 * -12.0dB) */
+ 0x3e883aa8, /* [196] = 2.66e-001 = pow(10.0, 0.05 * -11.5dB) */
+ 0x3e904d1c, /* [197] = 2.82e-001 = pow(10.0, 0.05 * -11.0dB) */
+ 0x3e98da02, /* [198] = 2.99e-001 = pow(10.0, 0.05 * -10.5dB) */
+ 0x3ea1e89b, /* [199] = 3.16e-001 = pow(10.0, 0.05 * -10.0dB) */
+ 0x3eab8097, /* [200] = 3.35e-001 = pow(10.0, 0.05 * -9.5dB) */
+ 0x3eb5aa1a, /* [201] = 3.55e-001 = pow(10.0, 0.05 * -9.0dB) */
+ 0x3ec06dc3, /* [202] = 3.76e-001 = pow(10.0, 0.05 * -8.5dB) */
+ 0x3ecbd4b4, /* [203] = 3.98e-001 = pow(10.0, 0.05 * -8.0dB) */
+ 0x3ed7e89b, /* [204] = 4.22e-001 = pow(10.0, 0.05 * -7.5dB) */
+ 0x3ee4b3b6, /* [205] = 4.47e-001 = pow(10.0, 0.05 * -7.0dB) */
+ 0x3ef240e2, /* [206] = 4.73e-001 = pow(10.0, 0.05 * -6.5dB) */
+ 0x3f004dce, /* [207] = 5.01e-001 = pow(10.0, 0.05 * -6.0dB) */
+ 0x3f07e80b, /* [208] = 5.31e-001 = pow(10.0, 0.05 * -5.5dB) */
+ 0x3f0ff59a, /* [209] = 5.62e-001 = pow(10.0, 0.05 * -5.0dB) */
+ 0x3f187d50, /* [210] = 5.96e-001 = pow(10.0, 0.05 * -4.5dB) */
+ 0x3f21866c, /* [211] = 6.31e-001 = pow(10.0, 0.05 * -4.0dB) */
+ 0x3f2b1896, /* [212] = 6.68e-001 = pow(10.0, 0.05 * -3.5dB) */
+ 0x3f353bef, /* [213] = 7.08e-001 = pow(10.0, 0.05 * -3.0dB) */
+ 0x3f3ff911, /* [214] = 7.50e-001 = pow(10.0, 0.05 * -2.5dB) */
+ 0x3f4b5918, /* [215] = 7.94e-001 = pow(10.0, 0.05 * -2.0dB) */
+ 0x3f5765ac, /* [216] = 8.41e-001 = pow(10.0, 0.05 * -1.5dB) */
+ 0x3f642905, /* [217] = 8.91e-001 = pow(10.0, 0.05 * -1.0dB) */
+ 0x3f71adf9, /* [218] = 9.44e-001 = pow(10.0, 0.05 * -0.5dB) */
+ 0x3f800000, /* [219] = 1.00e+000 = pow(10.0, 0.05 * 0.0dB) */
+ 0x3f8795a0, /* [220] = 1.06e+000 = pow(10.0, 0.05 * 0.5dB) */
+ 0x3f8f9e4d, /* [221] = 1.12e+000 = pow(10.0, 0.05 * 1.0dB) */
+ 0x3f9820d7, /* [222] = 1.19e+000 = pow(10.0, 0.05 * 1.5dB) */
+ 0x3fa12478, /* [223] = 1.26e+000 = pow(10.0, 0.05 * 2.0dB) */
+ 0x3faab0d5, /* [224] = 1.33e+000 = pow(10.0, 0.05 * 2.5dB) */
+ 0x3fb4ce08, /* [225] = 1.41e+000 = pow(10.0, 0.05 * 3.0dB) */
+ 0x3fbf84a6, /* [226] = 1.50e+000 = pow(10.0, 0.05 * 3.5dB) */
+ 0x3fcaddc8, /* [227] = 1.58e+000 = pow(10.0, 0.05 * 4.0dB) */
+ 0x3fd6e30d, /* [228] = 1.68e+000 = pow(10.0, 0.05 * 4.5dB) */
+ 0x3fe39ea9, /* [229] = 1.78e+000 = pow(10.0, 0.05 * 5.0dB) */
+ 0x3ff11b6a, /* [230] = 1.88e+000 = pow(10.0, 0.05 * 5.5dB) */
+ 0x3fff64c1, /* [231] = 2.00e+000 = pow(10.0, 0.05 * 6.0dB) */
+ 0x40074368, /* [232] = 2.11e+000 = pow(10.0, 0.05 * 6.5dB) */
+ 0x400f4735, /* [233] = 2.24e+000 = pow(10.0, 0.05 * 7.0dB) */
+ 0x4017c496, /* [234] = 2.37e+000 = pow(10.0, 0.05 * 7.5dB) */
+ 0x4020c2bf, /* [235] = 2.51e+000 = pow(10.0, 0.05 * 8.0dB) */
+ 0x402a4952, /* [236] = 2.66e+000 = pow(10.0, 0.05 * 8.5dB) */
+ 0x40346063, /* [237] = 2.82e+000 = pow(10.0, 0.05 * 9.0dB) */
+ 0x403f1082, /* [238] = 2.99e+000 = pow(10.0, 0.05 * 9.5dB) */
+ 0x404a62c2, /* [239] = 3.16e+000 = pow(10.0, 0.05 * 10.0dB) */
+ 0x405660bd, /* [240] = 3.35e+000 = pow(10.0, 0.05 * 10.5dB) */
+ 0x406314a0, /* [241] = 3.55e+000 = pow(10.0, 0.05 * 11.0dB) */
+ 0x40708933, /* [242] = 3.76e+000 = pow(10.0, 0.05 * 11.5dB) */
+ 0x407ec9e1, /* [243] = 3.98e+000 = pow(10.0, 0.05 * 12.0dB) */
+ 0x4086f161, /* [244] = 4.22e+000 = pow(10.0, 0.05 * 12.5dB) */
+ 0x408ef052, /* [245] = 4.47e+000 = pow(10.0, 0.05 * 13.0dB) */
+ 0x4097688d, /* [246] = 4.73e+000 = pow(10.0, 0.05 * 13.5dB) */
+ 0x40a06142, /* [247] = 5.01e+000 = pow(10.0, 0.05 * 14.0dB) */
+ 0x40a9e20e, /* [248] = 5.31e+000 = pow(10.0, 0.05 * 14.5dB) */
+ 0x40b3f300, /* [249] = 5.62e+000 = pow(10.0, 0.05 * 15.0dB) */
+ 0x40be9ca5, /* [250] = 5.96e+000 = pow(10.0, 0.05 * 15.5dB) */
+ 0x40c9e807, /* [251] = 6.31e+000 = pow(10.0, 0.05 * 16.0dB) */
+ 0x40d5debc, /* [252] = 6.68e+000 = pow(10.0, 0.05 * 16.5dB) */
+ 0x40e28aeb, /* [253] = 7.08e+000 = pow(10.0, 0.05 * 17.0dB) */
+ 0x40eff755, /* [254] = 7.50e+000 = pow(10.0, 0.05 * 17.5dB) */
+ 0x40fe2f5e, /* [255] = 7.94e+000 = pow(10.0, 0.05 * 18.0dB) */
+};
+
+#define MIXART_DIGITAL_LEVEL_MIN 0 /* -109.5 dB */
+#define MIXART_DIGITAL_LEVEL_MAX 255 /* 18.0 dB */
+#define MIXART_DIGITAL_ZERO_LEVEL 219 /* 0.0 dB */
+
+
+int mixart_update_playback_stream_level(struct snd_mixart* chip, int is_aes, int idx)
+{
+ int err, i;
+ int volume[2];
+ struct mixart_msg request;
+ struct mixart_set_out_stream_level_req set_level;
+ u32 status = 0;
+ struct mixart_pipe *pipe;
+
+ memset(&set_level, 0, sizeof(set_level));
+ set_level.nb_of_stream = 1;
+ set_level.stream_level.desc.stream_idx = idx;
+
+ if(is_aes) {
+ pipe = &chip->pipe_out_dig; /* AES playback */
+ idx += MIXART_PLAYBACK_STREAMS;
+ } else {
+ pipe = &chip->pipe_out_ana; /* analog playback */
+ }
+
+ /* only when pipe exists ! */
+ if(pipe->status == PIPE_UNDEFINED)
+ return 0;
+
+ set_level.stream_level.desc.uid_pipe = pipe->group_uid;
+
+ for(i=0; i<2; i++) {
+ if(chip->digital_playback_active[idx][i])
+ volume[i] = chip->digital_playback_volume[idx][i];
+ else
+ volume[i] = MIXART_DIGITAL_LEVEL_MIN;
+ }
+
+ set_level.stream_level.out_level.valid_mask1 = MIXART_OUT_STREAM_SET_LEVEL_LEFT_AUDIO1 | MIXART_OUT_STREAM_SET_LEVEL_RIGHT_AUDIO2;
+ set_level.stream_level.out_level.left_to_out1_level = mixart_digital_level[volume[0]];
+ set_level.stream_level.out_level.right_to_out2_level = mixart_digital_level[volume[1]];
+
+ request.message_id = MSG_STREAM_SET_OUT_STREAM_LEVEL;
+ request.uid = (struct mixart_uid){0,0};
+ request.data = &set_level;
+ request.size = sizeof(set_level);
+
+ err = snd_mixart_send_msg(chip->mgr, &request, sizeof(status), &status);
+ if((err<0) || status) {
+ dev_dbg(chip->card->dev,
+ "error MSG_STREAM_SET_OUT_STREAM_LEVEL card(%d) status(%x)\n",
+ chip->chip_idx, status);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int mixart_update_capture_stream_level(struct snd_mixart* chip, int is_aes)
+{
+ int err, i, idx;
+ struct mixart_pipe *pipe;
+ struct mixart_msg request;
+ struct mixart_set_in_audio_level_req set_level;
+ u32 status = 0;
+
+ if(is_aes) {
+ idx = 1;
+ pipe = &chip->pipe_in_dig;
+ } else {
+ idx = 0;
+ pipe = &chip->pipe_in_ana;
+ }
+
+ /* only when pipe exists ! */
+ if(pipe->status == PIPE_UNDEFINED)
+ return 0;
+
+ memset(&set_level, 0, sizeof(set_level));
+ set_level.audio_count = 2;
+ set_level.level[0].connector = pipe->uid_left_connector;
+ set_level.level[1].connector = pipe->uid_right_connector;
+
+ for(i=0; i<2; i++) {
+ set_level.level[i].valid_mask1 = MIXART_AUDIO_LEVEL_DIGITAL_MASK;
+ set_level.level[i].digital_level = mixart_digital_level[chip->digital_capture_volume[idx][i]];
+ }
+
+ request.message_id = MSG_STREAM_SET_IN_AUDIO_LEVEL;
+ request.uid = (struct mixart_uid){0,0};
+ request.data = &set_level;
+ request.size = sizeof(set_level);
+
+ err = snd_mixart_send_msg(chip->mgr, &request, sizeof(status), &status);
+ if((err<0) || status) {
+ dev_dbg(chip->card->dev,
+ "error MSG_STREAM_SET_IN_AUDIO_LEVEL card(%d) status(%x)\n",
+ chip->chip_idx, status);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+/* shared */
+static int mixart_digital_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = MIXART_DIGITAL_LEVEL_MIN; /* -109.5 dB */
+ uinfo->value.integer.max = MIXART_DIGITAL_LEVEL_MAX; /* 18.0 dB */
+ return 0;
+}
+
+#define MIXART_VOL_REC_MASK 1
+#define MIXART_VOL_AES_MASK 2
+
+static int mixart_pcm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_mixart *chip = snd_kcontrol_chip(kcontrol);
+ int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
+ int *stored_volume;
+ int is_capture = kcontrol->private_value & MIXART_VOL_REC_MASK;
+ int is_aes = kcontrol->private_value & MIXART_VOL_AES_MASK;
+ mutex_lock(&chip->mgr->mixer_mutex);
+ if(is_capture) {
+ if(is_aes) stored_volume = chip->digital_capture_volume[1]; /* AES capture */
+ else stored_volume = chip->digital_capture_volume[0]; /* analog capture */
+ } else {
+ snd_BUG_ON(idx >= MIXART_PLAYBACK_STREAMS);
+ if(is_aes) stored_volume = chip->digital_playback_volume[MIXART_PLAYBACK_STREAMS + idx]; /* AES playback */
+ else stored_volume = chip->digital_playback_volume[idx]; /* analog playback */
+ }
+ ucontrol->value.integer.value[0] = stored_volume[0];
+ ucontrol->value.integer.value[1] = stored_volume[1];
+ mutex_unlock(&chip->mgr->mixer_mutex);
+ return 0;
+}
+
+static int mixart_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_mixart *chip = snd_kcontrol_chip(kcontrol);
+ int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
+ int changed = 0;
+ int is_capture = kcontrol->private_value & MIXART_VOL_REC_MASK;
+ int is_aes = kcontrol->private_value & MIXART_VOL_AES_MASK;
+ int* stored_volume;
+ int i;
+ mutex_lock(&chip->mgr->mixer_mutex);
+ if (is_capture) {
+ if (is_aes) /* AES capture */
+ stored_volume = chip->digital_capture_volume[1];
+ else /* analog capture */
+ stored_volume = chip->digital_capture_volume[0];
+ } else {
+ snd_BUG_ON(idx >= MIXART_PLAYBACK_STREAMS);
+ if (is_aes) /* AES playback */
+ stored_volume = chip->digital_playback_volume[MIXART_PLAYBACK_STREAMS + idx];
+ else /* analog playback */
+ stored_volume = chip->digital_playback_volume[idx];
+ }
+ for (i = 0; i < 2; i++) {
+ int vol = ucontrol->value.integer.value[i];
+ if (vol < MIXART_DIGITAL_LEVEL_MIN ||
+ vol > MIXART_DIGITAL_LEVEL_MAX)
+ continue;
+ if (stored_volume[i] != vol) {
+ stored_volume[i] = vol;
+ changed = 1;
+ }
+ }
+ if (changed) {
+ if (is_capture)
+ mixart_update_capture_stream_level(chip, is_aes);
+ else
+ mixart_update_playback_stream_level(chip, is_aes, idx);
+ }
+ mutex_unlock(&chip->mgr->mixer_mutex);
+ return changed;
+}
+
+static const DECLARE_TLV_DB_SCALE(db_scale_digital, -10950, 50, 0);
+
+static const struct snd_kcontrol_new snd_mixart_pcm_vol =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ /* name will be filled later */
+ /* count will be filled later */
+ .info = mixart_digital_vol_info, /* shared */
+ .get = mixart_pcm_vol_get,
+ .put = mixart_pcm_vol_put,
+ .tlv = { .p = db_scale_digital },
+};
+
+
+static int mixart_pcm_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_mixart *chip = snd_kcontrol_chip(kcontrol);
+ int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
+ snd_BUG_ON(idx >= MIXART_PLAYBACK_STREAMS);
+ mutex_lock(&chip->mgr->mixer_mutex);
+ if(kcontrol->private_value & MIXART_VOL_AES_MASK) /* AES playback */
+ idx += MIXART_PLAYBACK_STREAMS;
+ ucontrol->value.integer.value[0] = chip->digital_playback_active[idx][0];
+ ucontrol->value.integer.value[1] = chip->digital_playback_active[idx][1];
+ mutex_unlock(&chip->mgr->mixer_mutex);
+ return 0;
+}
+
+static int mixart_pcm_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_mixart *chip = snd_kcontrol_chip(kcontrol);
+ int changed = 0;
+ int is_aes = kcontrol->private_value & MIXART_VOL_AES_MASK;
+ int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
+ int i, j;
+ snd_BUG_ON(idx >= MIXART_PLAYBACK_STREAMS);
+ mutex_lock(&chip->mgr->mixer_mutex);
+ j = idx;
+ if (is_aes)
+ j += MIXART_PLAYBACK_STREAMS;
+ for (i = 0; i < 2; i++) {
+ if (chip->digital_playback_active[j][i] !=
+ ucontrol->value.integer.value[i]) {
+ chip->digital_playback_active[j][i] =
+ !!ucontrol->value.integer.value[i];
+ changed = 1;
+ }
+ }
+ if (changed)
+ mixart_update_playback_stream_level(chip, is_aes, idx);
+ mutex_unlock(&chip->mgr->mixer_mutex);
+ return changed;
+}
+
+static const struct snd_kcontrol_new mixart_control_pcm_switch = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* name will be filled later */
+ .count = MIXART_PLAYBACK_STREAMS,
+ .info = mixart_sw_info, /* shared */
+ .get = mixart_pcm_sw_get,
+ .put = mixart_pcm_sw_put
+};
+
+static int mixart_update_monitoring(struct snd_mixart* chip, int channel)
+{
+ int err;
+ struct mixart_msg request;
+ struct mixart_set_out_audio_level audio_level;
+ u32 resp = 0;
+
+ if(chip->pipe_out_ana.status == PIPE_UNDEFINED)
+ return -EINVAL; /* no pipe defined */
+
+ if(!channel) request.uid = chip->pipe_out_ana.uid_left_connector;
+ else request.uid = chip->pipe_out_ana.uid_right_connector;
+ request.message_id = MSG_CONNECTOR_SET_OUT_AUDIO_LEVEL;
+ request.data = &audio_level;
+ request.size = sizeof(audio_level);
+
+ memset(&audio_level, 0, sizeof(audio_level));
+ audio_level.valid_mask1 = MIXART_AUDIO_LEVEL_MONITOR_MASK | MIXART_AUDIO_LEVEL_MUTE_M1_MASK;
+ audio_level.monitor_level = mixart_digital_level[chip->monitoring_volume[channel!=0]];
+ audio_level.monitor_mute1 = !chip->monitoring_active[channel!=0];
+
+ err = snd_mixart_send_msg(chip->mgr, &request, sizeof(resp), &resp);
+ if((err<0) || resp) {
+ dev_dbg(chip->card->dev,
+ "error MSG_CONNECTOR_SET_OUT_AUDIO_LEVEL card(%d) resp(%x)\n",
+ chip->chip_idx, resp);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * monitoring level control
+ */
+
+static int mixart_monitor_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_mixart *chip = snd_kcontrol_chip(kcontrol);
+ mutex_lock(&chip->mgr->mixer_mutex);
+ ucontrol->value.integer.value[0] = chip->monitoring_volume[0];
+ ucontrol->value.integer.value[1] = chip->monitoring_volume[1];
+ mutex_unlock(&chip->mgr->mixer_mutex);
+ return 0;
+}
+
+static int mixart_monitor_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_mixart *chip = snd_kcontrol_chip(kcontrol);
+ int changed = 0;
+ int i;
+ mutex_lock(&chip->mgr->mixer_mutex);
+ for (i = 0; i < 2; i++) {
+ if (chip->monitoring_volume[i] !=
+ ucontrol->value.integer.value[i]) {
+ chip->monitoring_volume[i] =
+ !!ucontrol->value.integer.value[i];
+ mixart_update_monitoring(chip, i);
+ changed = 1;
+ }
+ }
+ mutex_unlock(&chip->mgr->mixer_mutex);
+ return changed;
+}
+
+static const struct snd_kcontrol_new mixart_control_monitor_vol = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .name = "Monitoring Volume",
+ .info = mixart_digital_vol_info, /* shared */
+ .get = mixart_monitor_vol_get,
+ .put = mixart_monitor_vol_put,
+ .tlv = { .p = db_scale_digital },
+};
+
+/*
+ * monitoring switch control
+ */
+
+static int mixart_monitor_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_mixart *chip = snd_kcontrol_chip(kcontrol);
+ mutex_lock(&chip->mgr->mixer_mutex);
+ ucontrol->value.integer.value[0] = chip->monitoring_active[0];
+ ucontrol->value.integer.value[1] = chip->monitoring_active[1];
+ mutex_unlock(&chip->mgr->mixer_mutex);
+ return 0;
+}
+
+static int mixart_monitor_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_mixart *chip = snd_kcontrol_chip(kcontrol);
+ int changed = 0;
+ int i;
+ mutex_lock(&chip->mgr->mixer_mutex);
+ for (i = 0; i < 2; i++) {
+ if (chip->monitoring_active[i] !=
+ ucontrol->value.integer.value[i]) {
+ chip->monitoring_active[i] =
+ !!ucontrol->value.integer.value[i];
+ changed |= (1<<i); /* mask 0x01 ans 0x02 */
+ }
+ }
+ if (changed) {
+ /* allocate or release resources for monitoring */
+ int allocate = chip->monitoring_active[0] ||
+ chip->monitoring_active[1];
+ if (allocate) {
+ /* allocate the playback pipe for monitoring */
+ snd_mixart_add_ref_pipe(chip, MIXART_PCM_ANALOG, 0, 1);
+ /* allocate the capture pipe for monitoring */
+ snd_mixart_add_ref_pipe(chip, MIXART_PCM_ANALOG, 1, 1);
+ }
+ if (changed & 0x01)
+ mixart_update_monitoring(chip, 0);
+ if (changed & 0x02)
+ mixart_update_monitoring(chip, 1);
+ if (!allocate) {
+ /* release the capture pipe for monitoring */
+ snd_mixart_kill_ref_pipe(chip->mgr,
+ &chip->pipe_in_ana, 1);
+ /* release the playback pipe for monitoring */
+ snd_mixart_kill_ref_pipe(chip->mgr,
+ &chip->pipe_out_ana, 1);
+ }
+ }
+
+ mutex_unlock(&chip->mgr->mixer_mutex);
+ return (changed != 0);
+}
+
+static const struct snd_kcontrol_new mixart_control_monitor_sw = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Monitoring Switch",
+ .info = mixart_sw_info, /* shared */
+ .get = mixart_monitor_sw_get,
+ .put = mixart_monitor_sw_put
+};
+
+
+static void mixart_reset_audio_levels(struct snd_mixart *chip)
+{
+ /* analog volumes can be set even if there is no pipe */
+ mixart_update_analog_audio_level(chip, 0);
+ /* analog levels for capture only on the first two chips */
+ if(chip->chip_idx < 2) {
+ mixart_update_analog_audio_level(chip, 1);
+ }
+ return;
+}
+
+
+int snd_mixart_create_mixer(struct mixart_mgr *mgr)
+{
+ struct snd_mixart *chip;
+ int err, i;
+
+ mutex_init(&mgr->mixer_mutex); /* can be in another place */
+
+ for(i=0; i<mgr->num_cards; i++) {
+ struct snd_kcontrol_new temp;
+ chip = mgr->chip[i];
+
+ /* analog output level control */
+ temp = mixart_control_analog_level;
+ temp.name = "Master Playback Volume";
+ temp.private_value = 0; /* playback */
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
+ return err;
+ /* output mute controls */
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&mixart_control_output_switch, chip));
+ if (err < 0)
+ return err;
+
+ /* analog input level control only on first two chips !*/
+ if(i<2) {
+ temp = mixart_control_analog_level;
+ temp.name = "Master Capture Volume";
+ temp.private_value = 1; /* capture */
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
+ return err;
+ }
+
+ temp = snd_mixart_pcm_vol;
+ temp.name = "PCM Playback Volume";
+ temp.count = MIXART_PLAYBACK_STREAMS;
+ temp.private_value = 0; /* playback analog */
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
+ return err;
+
+ temp.name = "PCM Capture Volume";
+ temp.count = 1;
+ temp.private_value = MIXART_VOL_REC_MASK; /* capture analog */
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
+ return err;
+
+ if(mgr->board_type == MIXART_DAUGHTER_TYPE_AES) {
+ temp.name = "AES Playback Volume";
+ temp.count = MIXART_PLAYBACK_STREAMS;
+ temp.private_value = MIXART_VOL_AES_MASK; /* playback AES/EBU */
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
+ return err;
+
+ temp.name = "AES Capture Volume";
+ temp.count = 0;
+ temp.private_value = MIXART_VOL_REC_MASK | MIXART_VOL_AES_MASK; /* capture AES/EBU */
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
+ return err;
+ }
+ temp = mixart_control_pcm_switch;
+ temp.name = "PCM Playback Switch";
+ temp.private_value = 0; /* playback analog */
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
+ return err;
+
+ if(mgr->board_type == MIXART_DAUGHTER_TYPE_AES) {
+ temp.name = "AES Playback Switch";
+ temp.private_value = MIXART_VOL_AES_MASK; /* playback AES/EBU */
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
+ return err;
+ }
+
+ /* monitoring */
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&mixart_control_monitor_vol, chip));
+ if (err < 0)
+ return err;
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&mixart_control_monitor_sw, chip));
+ if (err < 0)
+ return err;
+
+ /* init all mixer data and program the master volumes/switches */
+ mixart_reset_audio_levels(chip);
+ }
+ return 0;
+}
diff --git a/sound/pci/mixart/mixart_mixer.h b/sound/pci/mixart/mixart_mixer.h
new file mode 100644
index 000000000..42e189272
--- /dev/null
+++ b/sound/pci/mixart/mixart_mixer.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Driver for Digigram miXart soundcards
+ *
+ * include file for mixer
+ *
+ * Copyright (c) 2003 by Digigram <alsa@digigram.com>
+ */
+
+#ifndef __SOUND_MIXART_MIXER_H
+#define __SOUND_MIXART_MIXER_H
+
+/* exported */
+int mixart_update_playback_stream_level(struct snd_mixart* chip, int is_aes, int idx);
+int mixart_update_capture_stream_level(struct snd_mixart* chip, int is_aes);
+int snd_mixart_create_mixer(struct mixart_mgr* mgr);
+
+#endif /* __SOUND_MIXART_MIXER_H */