summaryrefslogtreecommitdiff
path: root/sound/drivers/saudio.c
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
committerYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
commit8e4bff4cab0ddac6060645b0715210484d02ff40 (patch)
tree3a5c3024371a04692a3a6d9974d001cdff8ebf84 /sound/drivers/saudio.c
Initial file dump from open source releaseHEADmaster
Diffstat (limited to 'sound/drivers/saudio.c')
-rw-r--r--sound/drivers/saudio.c1435
1 files changed, 1435 insertions, 0 deletions
diff --git a/sound/drivers/saudio.c b/sound/drivers/saudio.c
new file mode 100644
index 00000000..0a1d0350
--- /dev/null
+++ b/sound/drivers/saudio.c
@@ -0,0 +1,1435 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#define pr_fmt(fmt) "[saudio] " fmt
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+
+#ifdef CONFIG_OF
+#include <linux/of.h>
+#include <linux/of_device.h>
+#endif
+
+#include <linux/sipc.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/mutex.h>
+#include <linux/memory.h>
+#include <linux/io.h>
+#include <linux/workqueue.h>
+#include <linux/kthread.h>
+#include <sound/saudio.h>
+
+#define ETRACE(x...) printk(KERN_ERR "Error: " x)
+#define WTRACE(x...) pr_debug(KERN_ERR x)
+
+#define ADEBUG() pr_debug(KERN_ERR "saudio.c: function: %s,line %d\n",__FUNCTION__,__LINE__)
+
+#define CMD_BLOCK_SIZE 80
+#define TX_DATA_BLOCK_SIZE 80
+#define RX_DATA_BLOCK_SIZE 0
+#define MAX_BUFFER_SIZE (10*1024)
+
+#define MAX_PERIOD_SIZE MAX_BUFFER_SIZE
+
+#define USE_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE)
+
+#define USE_RATE SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000
+#define USE_RATE_MIN 5500
+#define USE_RATE_MAX 48000
+
+#define USE_CHANNELS_MIN 1
+
+#define USE_CHANNELS_MAX 2
+
+#define USE_PERIODS_MIN 1
+
+#define USE_PERIODS_MAX 1024
+
+#define CMD_TIMEOUT msecs_to_jiffies(5000)
+#define CMD_MODEM_RESET_TIMEOUT msecs_to_jiffies(10000)
+
+#define SAUDIO_CMD_NONE 0x00000000
+#define SAUDIO_CMD_OPEN 0x00000001
+#define SAUDIO_CMD_CLOSE 0x00000002
+#define SAUDIO_CMD_START 0x00000004
+#define SAUDIO_CMD_STOP 0x00000008
+#define SAUDIO_CMD_PREPARE 0x00000010
+#define SAUDIO_CMD_TRIGGER 0x00000020
+#define SAUDIO_CMD_HANDSHAKE 0x00000040
+#define SAUDIO_CMD_RESET 0x00000080
+
+#define SAUDIO_CMD_OPEN_RET 0x00010000
+#define SAUDIO_CMD_CLOSE_RET 0x00020000
+#define SAUDIO_CMD_START_RET 0x00040000
+#define SAUDIO_CMD_STOP_RET 0x00080000
+#define SAUDIO_CMD_PREPARE_RET 0x00100000
+#define SAUDIO_CMD_TRIGGER_RET 0x00200000
+#define SAUDIO_CMD_HANDSHAKE_RET 0x00400000
+#define SAUDIO_CMD_RESET_RET 0x00800000
+
+#define SAUDIO_DATA_PCM 0x00000040
+#define SAUDIO_DATA_SILENCE 0x00000080
+
+#define PLAYBACK_DATA_ABORT 0x00000001
+#define PLAYBACK_DATA_BREAK 0x00000002
+
+#define SAUDIO_DEV_CTRL_ABORT 0x00000001
+#define SAUDIO_DEV_CTRL_BREAK 0x00000002
+
+#define SAUDIO_SUBCMD_CAPTURE 0x0
+#define SAUDIO_SUBCMD_PLAYBACK 0x1
+
+#define SAUDIO_DEV_MAX 1
+#define SAUDIO_STREAM_MAX 2
+#define SAUDIO_CARD_NAME_LEN_MAX 16
+
+#define SAUDIO_STREAM_BLOCK_COUNT 4
+#define SAUDIO_CMD_BLOCK_COUNT 4
+
+#define SAUDIO_MONITOR_BLOCK_COUNT 2
+#define SAUDIO_MONITOR_BLOCK_COUNT 2
+
+struct cmd_common {
+ unsigned int command;
+ unsigned int sub_cmd;
+ unsigned int reserved1;
+ unsigned int reserved2;
+};
+
+struct cmd_prepare {
+ struct cmd_common common;
+ unsigned int rate; /* rate in Hz */
+ unsigned char channels; /* channels */
+ unsigned char format;
+ unsigned char reserved1;
+ unsigned char reserved2;
+ unsigned int period; /* period size */
+ unsigned int periods; /* periods */
+};
+
+struct cmd_open {
+ struct cmd_common common;
+ uint32_t stream_type;
+};
+
+struct saudio_msg {
+ uint32_t command;
+ uint32_t stream_id;
+ uint32_t reserved1;
+ uint32_t reserved2;
+ void *param;
+};
+
+enum snd_status {
+ SAUDIO_IDLE,
+ SAUDIO_OPENNED,
+ SAUDIO_CLOSED,
+ SAUDIO_STOPPED,
+ SAUDIO_PREPARED,
+ SAUDIO_TRIGGERED,
+ SAUDIO_PARAMIZED,
+ SAUDIO_ABORT,
+};
+
+struct saudio_dev_ctrl;
+struct snd_saudio;
+
+struct saudio_stream {
+
+ struct snd_saudio *saudio;
+ struct snd_pcm_substream *substream;
+ struct saudio_dev_ctrl *dev_ctrl;
+ int stream_id; /* numeric identification */
+
+ uint32_t stream_state;
+
+ uint32_t dst;
+ uint32_t channel;
+
+ int32_t period;
+ int32_t periods_avail;
+ int32_t periods_tosend;
+
+ uint32_t hwptr_done;
+
+ uint32_t last_elapsed_count;
+ uint32_t last_getblk_count;
+ uint32_t blk_count;
+
+ struct mutex mutex;
+};
+
+struct saudio_dev_ctrl {
+ uint32_t dev_state;
+ struct mutex mutex;
+ uint32_t dst;
+ uint32_t monitor_channel;
+ uint32_t channel;
+ uint8_t name[SAUDIO_CARD_NAME_LEN_MAX];
+ struct saudio_stream stream[SAUDIO_STREAM_MAX];
+};
+
+struct snd_saudio {
+ struct snd_card *card;
+ struct snd_pcm *pcm[SAUDIO_DEV_MAX];
+ struct saudio_dev_ctrl dev_ctrl[SAUDIO_DEV_MAX];
+ wait_queue_head_t wait;
+ struct platform_device *pdev;
+ struct workqueue_struct *queue;
+ struct work_struct card_free_work;
+ uint32_t dst;
+ uint32_t channel;
+ uint32_t in_init;
+ struct task_struct * thread_id;
+ int32_t state;
+ struct mutex mutex;
+};
+
+static DEFINE_MUTEX(snd_sound);
+
+static int saudio_snd_card_free(const struct snd_saudio *saudio);
+
+
+static int saudio_clear_cmd(uint32_t dst, uint32_t channel)
+{
+ int result = 0;
+ int i = 0;
+ struct sblock blk = { 0 };
+ do {
+ result = sblock_receive(dst, channel, (struct sblock *)&blk, 0);
+ if (!result) {
+ sblock_release(dst, channel, &blk);
+ }
+ } while (!result);
+ return result;
+}
+
+
+static int saudio_send_common_cmd(uint32_t dst, uint32_t channel,
+ uint32_t cmd, uint32_t subcmd,
+ int32_t timeout)
+{
+ int result = 0;
+ struct sblock blk = { 0 };
+ ADEBUG();
+ pr_debug(" dst is %d, channel %d, cmd %x, subcmd %x\n", dst, channel,
+ cmd, subcmd);
+ saudio_clear_cmd( dst, channel);
+ result = sblock_get(dst, channel, (struct sblock *)&blk, timeout);
+ if (result >= 0) {
+ struct cmd_common *common = (struct cmd_common *)blk.addr;
+ common->command = cmd;
+ common->sub_cmd = subcmd;
+ blk.length = sizeof(struct cmd_common);
+ pr_debug(" dst is %d, channel %d, cmd %x, subcmd %x send ok\n",
+ dst, channel, cmd, subcmd);
+ result = sblock_send(dst, channel, (struct sblock *)&blk);
+ }
+ return result;
+}
+
+static int saudio_wait_common_cmd(uint32_t dst, uint32_t channel,
+ uint32_t cmd, uint32_t subcmd,
+ int32_t timeout)
+{
+ int result = 0;
+ struct sblock blk = { 0 };
+ struct cmd_common *common = NULL;
+ ADEBUG();
+ result = sblock_receive(dst, channel, (struct sblock *)&blk, timeout);
+ if (result < 0) {
+ ETRACE("sblock_receive dst %d, channel %d result is %d \n", dst,
+ channel, result);
+ return result;
+ }
+
+ common = (struct cmd_common *)blk.addr;
+ pr_debug("dst is %d, channel %d, common->command is %x ,sub cmd %x,\n",
+ dst, channel, common->command, common->sub_cmd);
+ if (subcmd) {
+ if ((common->command == cmd) && (common->sub_cmd == subcmd)) {
+ result = 0;
+ } else {
+ result = -1;
+ }
+ } else {
+ if (common->command == cmd) {
+ result = 0;
+ } else {
+ result = -1;
+ }
+ }
+ sblock_release(dst, channel, &blk);
+ return result;
+}
+
+static int saudio_clear_ctrl_cmd(struct snd_saudio *saudio)
+{
+ int result = 0;
+ int i = 0;
+ struct sblock blk = { 0 };
+ struct saudio_dev_ctrl *dev_ctrl = NULL;
+
+ for (i = 0; i < SAUDIO_DEV_MAX; i++) { /* now only support one device */
+ dev_ctrl = &saudio->dev_ctrl[i];
+ do {
+ result =
+ sblock_receive(dev_ctrl->dst, dev_ctrl->channel,
+ (struct sblock *)&blk, 0);
+ if (!result) {
+ sblock_release(dev_ctrl->dst, dev_ctrl->channel,
+ &blk);
+ }
+ } while (!result);
+ }
+
+ return result;
+}
+
+
+static int saudio_pcm_lib_malloc_pages(struct snd_pcm_substream *substream,
+ size_t size)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_dma_buffer *dmab = &substream->dma_buffer;
+
+ /* Use the pre-allocated buffer */
+ snd_pcm_set_runtime_buffer(substream, dmab);
+ runtime->dma_bytes = size;
+ return 1;
+}
+
+static int saudio_pcm_lib_free_pages(struct snd_pcm_substream *substream)
+{
+ snd_pcm_set_runtime_buffer(substream, NULL);
+ return 0;
+}
+
+static int saudio_snd_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ ADEBUG();
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ return remap_pfn_range(vma, vma->vm_start,
+ substream->dma_buffer.addr >> PAGE_SHIFT,
+ vma->vm_end - vma->vm_start, vma->vm_page_prot);
+}
+
+static struct snd_pcm_hardware snd_card_saudio_playback = {
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = USE_FORMATS,
+ .rates = USE_RATE,
+ .rate_min = USE_RATE_MIN,
+ .rate_max = USE_RATE_MAX,
+ .channels_min = USE_CHANNELS_MIN,
+ .channels_max = USE_CHANNELS_MAX,
+ .buffer_bytes_max = MAX_BUFFER_SIZE,
+ .period_bytes_min = 64,
+ .period_bytes_max = MAX_PERIOD_SIZE,
+ .periods_min = USE_PERIODS_MIN,
+ .periods_max = USE_PERIODS_MAX,
+ .fifo_size = 0,
+};
+
+static struct snd_pcm_hardware snd_card_saudio_capture = {
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = USE_FORMATS,
+ .rates = USE_RATE,
+ .rate_min = USE_RATE_MIN,
+ .rate_max = USE_RATE_MAX,
+ .channels_min = USE_CHANNELS_MIN,
+ .channels_max = USE_CHANNELS_MAX,
+ .buffer_bytes_max = MAX_BUFFER_SIZE,
+ .period_bytes_min = 64,
+ .period_bytes_max = MAX_PERIOD_SIZE,
+ .periods_min = USE_PERIODS_MIN,
+ .periods_max = USE_PERIODS_MAX,
+ .fifo_size = 0,
+};
+
+static int snd_card_saudio_pcm_open(struct snd_pcm_substream *substream)
+{
+ const struct snd_saudio *saudio = snd_pcm_substream_chip(substream);
+ const int stream_id = substream->pstr->stream;
+ const int dev = substream->pcm->device;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct saudio_stream *stream = NULL;
+ int result = 0;
+ struct saudio_dev_ctrl *dev_ctrl = NULL;
+ ADEBUG();
+
+ mutex_lock(&saudio->mutex);
+ if(!saudio->state) {
+ mutex_unlock(&saudio->mutex);
+ printk("saudio.c: snd_pcm_open error saudio state %d\n",saudio->state);
+ return -EIO;
+ }
+ mutex_unlock(&saudio->mutex);
+
+ pr_info("%s IN, stream_id=%d\n", __func__, stream_id);
+ dev_ctrl = (struct saudio_dev_ctrl *)&(saudio->dev_ctrl[dev]);
+ stream = (struct saudio_stream *)&(dev_ctrl->stream[stream_id]);
+ stream->substream = substream;
+ stream->stream_id = stream_id;
+
+ stream->period = 0;
+ stream->periods_tosend = 0;
+ stream->periods_avail = 0;
+ stream->hwptr_done = 0;
+ stream->last_getblk_count = 0;
+ stream->last_elapsed_count = 0;
+ stream->blk_count = SAUDIO_STREAM_BLOCK_COUNT;
+
+ if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) {
+ runtime->hw = snd_card_saudio_playback;
+ } else {
+ runtime->hw = snd_card_saudio_capture;
+ }
+ mutex_lock(&dev_ctrl->mutex);
+ result = saudio_send_common_cmd(dev_ctrl->dst, dev_ctrl->channel,
+ SAUDIO_CMD_OPEN, stream_id,
+ CMD_TIMEOUT);
+ if (result) {
+ ETRACE
+ ("saudio.c: snd_card_saudio_pcm_open: saudio_send_common_cmd result is %d",
+ result);
+ if(result != (-ERESTARTSYS))
+ saudio_snd_card_free(saudio);
+ mutex_unlock(&dev_ctrl->mutex);
+ return result;
+ }
+ pr_info("%s send cmd done\n", __func__);
+ result = saudio_wait_common_cmd(dev_ctrl->dst,
+ dev_ctrl->channel,
+ SAUDIO_CMD_OPEN_RET, 0, CMD_TIMEOUT);
+ if (result && (result != (-ERESTARTSYS)))
+ saudio_snd_card_free(saudio);
+ mutex_unlock(&dev_ctrl->mutex);
+ pr_info("%s OUT, result=%d\n", __func__, result);
+
+ return result;
+}
+
+static int snd_card_saudio_pcm_close(struct snd_pcm_substream *substream)
+{
+ const struct snd_saudio *saudio = snd_pcm_substream_chip(substream);
+ const int stream_id = substream->pstr->stream;
+ const int dev = substream->pcm->device;
+ struct saudio_dev_ctrl *dev_ctrl = NULL;
+ int result = 0;
+ ADEBUG();
+ mutex_lock(&saudio->mutex);
+ if(!saudio->state) {
+ mutex_unlock(&saudio->mutex);
+ printk("saudio.c: snd_pcm_close error saudio state %d\n",saudio->state);
+ return -EIO;
+ }
+ mutex_unlock(&saudio->mutex);
+ dev_ctrl = (struct saudio_dev_ctrl *)&(saudio->dev_ctrl[dev]);
+ pr_info("%s IN, stream_id=%d,dst %d, channel %d\n", __func__, stream_id,
+ dev_ctrl->dst, dev_ctrl->channel);
+ mutex_lock(&dev_ctrl->mutex);
+ result = saudio_send_common_cmd(dev_ctrl->dst, dev_ctrl->channel,
+ SAUDIO_CMD_CLOSE, stream_id,
+ CMD_TIMEOUT);
+ if (result) {
+ ETRACE
+ ("saudio.c: snd_card_saudio_pcm_close: saudio_send_common_cmd result is %d",
+ result);
+ if(result != (-ERESTARTSYS))
+ saudio_snd_card_free(saudio);
+ mutex_unlock(&dev_ctrl->mutex);
+ return result;
+ }
+ pr_info("%s send cmd done\n", __func__);
+ result =
+ saudio_wait_common_cmd(dev_ctrl->dst,
+ dev_ctrl->channel,
+ SAUDIO_CMD_CLOSE_RET, 0, CMD_TIMEOUT);
+ if (result && (result != (-ERESTARTSYS)))
+ saudio_snd_card_free(saudio);
+ mutex_unlock(&dev_ctrl->mutex);
+ pr_info("%s OUT, result=%d,dst %d, channel %d\n", __func__, result,
+ dev_ctrl->dst, dev_ctrl->channel);
+
+ return result;
+
+}
+
+static int saudio_data_trigger_process(struct saudio_stream *stream,
+ struct saudio_msg *msg);
+
+static int snd_card_saudio_pcm_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ const struct snd_saudio *saudio = snd_pcm_substream_chip(substream);
+ const int stream_id = substream->pstr->stream;
+ const int dev = substream->pcm->device;
+ struct saudio_dev_ctrl *dev_ctrl = NULL;
+ struct saudio_stream *stream = NULL;
+ struct saudio_msg msg = { 0 };
+ int err = 0;
+ int result = 0;
+ ADEBUG();
+ dev_ctrl = (struct saudio_dev_ctrl *)&(saudio->dev_ctrl[dev]);
+ stream = (struct saudio_stream *)&(dev_ctrl->stream[stream_id]);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ pr_info("%s IN, TRIGGER_START, stream_id=%d\n", __func__,
+ stream_id);
+ msg.stream_id = stream_id;
+ stream->stream_state = SAUDIO_TRIGGERED;
+ result = saudio_data_trigger_process(stream, &msg);
+ result =
+ saudio_send_common_cmd(dev_ctrl->dst, dev_ctrl->channel,
+ SAUDIO_CMD_START, stream->stream_id,
+ 0);
+ if (result) {
+ ETRACE
+ ("saudio.c: snd_card_saudio_pcm_trigger: RESUME, send_common_cmd result is %d",
+ result);
+ if(result != (-ERESTARTSYS))
+ saudio_snd_card_free(saudio);
+ return result;
+ }
+ pr_info("%s OUT, TRIGGER_START, result=%d\n", __func__, result);
+
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ pr_info("%s IN, TRIGGER_STOP, stream_id=%d\n", __func__,
+ stream_id);
+ stream->stream_state = SAUDIO_STOPPED;
+ result =
+ saudio_send_common_cmd(dev_ctrl->dst, dev_ctrl->channel,
+ SAUDIO_CMD_STOP, stream->stream_id,
+ 0);
+ if (result) {
+ ETRACE
+ ("saudio.c: snd_card_saudio_pcm_trigger: SUSPEND, send_common_cmd result is %d",
+ result);
+ if(result != (-ERESTARTSYS))
+ saudio_snd_card_free(saudio);
+ return result;
+ }
+ pr_info("%s OUT, TRIGGER_STOP, result=%d\n", __func__, result);
+
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ return 0;
+}
+
+static int saudio_cmd_prepare_process(struct saudio_dev_ctrl *dev_ctrl,
+ struct saudio_msg *msg);
+static int snd_card_saudio_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ const struct snd_saudio *saudio = snd_pcm_substream_chip(substream);
+ const int stream_id = substream->pstr->stream;
+ const int dev = substream->pcm->device;
+ struct saudio_dev_ctrl *dev_ctrl = NULL;
+ struct saudio_msg msg = { 0 };
+ int result = 0;
+
+ ADEBUG();
+ mutex_lock(&saudio->mutex);
+ if(!saudio->state) {
+ mutex_unlock(&saudio->mutex);
+ printk("saudio.c: snd_pcm_prepare error saudio state %d\n",saudio->state);
+ return -EIO;
+ }
+ mutex_unlock(&saudio->mutex);
+ pr_info("%s IN, stream_id=%d\n", __func__, stream_id);
+ dev_ctrl = (struct saudio_dev_ctrl *)&(saudio->dev_ctrl[dev]);
+ msg.command = SAUDIO_CMD_PREPARE;
+ msg.stream_id = stream_id;
+
+ result = saudio_cmd_prepare_process(dev_ctrl, &msg);
+ pr_info("%s OUT, result=%d\n", __func__, result);
+
+ return 0;
+}
+
+static int snd_card_saudio_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ int32_t result = 0;
+ ADEBUG();
+ result =
+ saudio_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+ pr_debug("saudio.c: saudio.c: hw_params result is %d", result);
+ return result;
+}
+
+static int snd_card_saudio_hw_free(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ const struct snd_saudio *saudio = snd_pcm_substream_chip(substream);
+ const int stream_id = substream->pstr->stream;
+ const int dev = substream->pcm->device;
+ struct saudio_stream *stream =
+ (struct saudio_stream *)&(saudio->dev_ctrl[dev].stream[stream_id]);
+ ADEBUG();
+ mutex_lock(&stream->mutex);
+ ret = saudio_pcm_lib_free_pages(substream);
+ mutex_unlock(&stream->mutex);
+ return ret;
+}
+
+static snd_pcm_uframes_t snd_card_saudio_pcm_pointer(struct snd_pcm_substream
+ *substream)
+{
+ const struct snd_saudio *saudio = snd_pcm_substream_chip(substream);
+ const int stream_id = substream->pstr->stream;
+ const int dev = substream->pcm->device;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct saudio_stream *stream =
+ (struct saudio_stream *)&(saudio->dev_ctrl[dev].stream[stream_id]);
+ unsigned int offset;
+ offset =
+ stream->hwptr_done * frames_to_bytes(runtime, runtime->period_size);
+ return bytes_to_frames(runtime, offset);
+}
+
+static struct snd_pcm_ops snd_card_saudio_playback_ops = {
+ .open = snd_card_saudio_pcm_open,
+ .close = snd_card_saudio_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_card_saudio_hw_params,
+ .hw_free = snd_card_saudio_hw_free,
+ .prepare = snd_card_saudio_pcm_prepare,
+ .trigger = snd_card_saudio_pcm_trigger,
+ .pointer = snd_card_saudio_pcm_pointer,
+ .mmap = saudio_snd_mmap,
+};
+
+static struct snd_pcm_ops snd_card_saudio_capture_ops = {
+ .open = snd_card_saudio_pcm_open,
+ .close = snd_card_saudio_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_card_saudio_hw_params,
+ .hw_free = snd_card_saudio_hw_free,
+ .prepare = snd_card_saudio_pcm_prepare,
+ .trigger = snd_card_saudio_pcm_trigger,
+ .pointer = snd_card_saudio_pcm_pointer,
+ .mmap = saudio_snd_mmap,
+};
+
+void saudio_pcm_lib_preallocate_free_for_all(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ int stream;
+ pr_debug("saudio.c:saudio_pcm_lib_preallocate_free_for_all");
+ for (stream = 0; stream < 2; stream++)
+ for (substream = pcm->streams[stream].substream; substream;
+ substream = substream->next) {
+ if(substream->dma_buffer.addr) {
+ iounmap(substream->dma_buffer.area);
+ smem_free(substream->dma_buffer.addr,
+ substream->dma_buffer.bytes);
+ substream->dma_buffer.addr = (dma_addr_t) NULL;
+ substream->dma_buffer.area = (int8_t *) NULL;
+ substream->dma_buffer.bytes = 0;
+ }
+ }
+}
+
+int saudio_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm,
+ int type, void *data,
+ size_t size, size_t max)
+{
+
+ struct snd_pcm_substream *substream;
+ int stream;
+ pr_debug("saudio.c:saudio_pcm_lib_preallocate_pages_for_all in");
+ (void)size;
+ for (stream = 0; stream < SAUDIO_STREAM_MAX; stream++) {
+ for (substream = pcm->streams[stream].substream; substream;
+ substream = substream->next) {
+ struct snd_dma_buffer *dmab = &substream->dma_buffer;
+
+ uint32_t addr = smem_alloc(size);
+
+ if(addr) {
+ dmab->dev.type = type;
+ dmab->dev.dev = data;
+ dmab->area = ioremap_nocache(addr, size);
+ dmab->addr = addr;
+ dmab->bytes = size;
+ memset(dmab->area, 0x5a, size);
+ pr_debug
+ ("saudio_pcm_lib_preallocate_pages_for_all:saudio.c: dmab addr is %x, area is %lx,size is %d",
+ (uint32_t) dmab->addr, (unsigned long)dmab->area,
+ size);
+ if (substream->dma_buffer.bytes > 0)
+ substream->buffer_bytes_max =
+ substream->dma_buffer.bytes;
+ substream->dma_max = max;
+ }
+ else {
+ printk("saudio: prealloc smem error \n");
+ memset(dmab,0,sizeof(struct snd_dma_buffer));
+ substream->dma_max = 0;
+ substream->buffer_bytes_max = 0;
+ goto ERR;
+
+ }
+ }
+ }
+ return 0;
+ERR:
+ saudio_pcm_lib_preallocate_free_for_all(pcm);
+ return -1;
+}
+
+static int snd_card_saudio_pcm(struct snd_saudio *saudio, int device,
+ int substreams)
+{
+ struct snd_pcm *pcm;
+ int err;
+ ADEBUG();
+ err = snd_pcm_new(saudio->card, "SAUDIO PCM", device,
+ substreams, substreams, &pcm);
+ if (err < 0)
+ return err;
+ pcm->private_data = saudio;
+ saudio->pcm[device] = pcm;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &snd_card_saudio_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &snd_card_saudio_capture_ops);
+ pcm->private_data = saudio;
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "SAUDIO PCM");
+ pcm->private_free = saudio_pcm_lib_preallocate_free_for_all;
+ err = saudio_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+ snd_dma_continuous_data
+ (GFP_KERNEL), MAX_BUFFER_SIZE,
+ MAX_BUFFER_SIZE);
+ if(err) {
+ printk("saudio:snd_card_saudio_pcm: prealloc pages error\n");
+ saudio->pcm[device] = NULL;
+ }
+ return err;
+}
+
+static struct snd_saudio *saudio_card_probe(struct saudio_init_data *init_data)
+{
+ struct snd_saudio *saudio = NULL;
+
+ saudio = kzalloc(sizeof(struct snd_saudio), GFP_KERNEL);
+ if (!saudio)
+ return NULL;
+
+ saudio->dev_ctrl[0].channel = init_data->ctrl_channel;
+ saudio->dev_ctrl[0].monitor_channel = init_data->monitor_channel;
+ saudio->dev_ctrl[0].dst = init_data->dst;
+ saudio->dst = init_data->dst;
+ saudio->channel = init_data->ctrl_channel;
+ mutex_init(&saudio->mutex);
+ memcpy(saudio->dev_ctrl[0].name, init_data->name,
+ SAUDIO_CARD_NAME_LEN_MAX);
+ saudio->dev_ctrl[0].stream[SNDRV_PCM_STREAM_PLAYBACK].channel =
+ init_data->playback_channel;
+ saudio->dev_ctrl[0].stream[SNDRV_PCM_STREAM_PLAYBACK].dst =
+ init_data->dst;
+ saudio->dev_ctrl[0].stream[SNDRV_PCM_STREAM_CAPTURE].channel =
+ init_data->capture_channel;
+ saudio->dev_ctrl[0].stream[SNDRV_PCM_STREAM_CAPTURE].dst =
+ init_data->dst;
+
+ return saudio;
+}
+
+static int saudio_data_trigger_process(struct saudio_stream *stream,
+ struct saudio_msg *msg)
+{
+ int32_t result = 0;
+ struct sblock blk = { 0 };
+ struct cmd_common *common = NULL;
+ struct snd_pcm_runtime *runtime = stream->substream->runtime;
+ ADEBUG();
+
+ if (stream->stream_id == SNDRV_PCM_STREAM_PLAYBACK) {
+ stream->periods_avail = snd_pcm_playback_avail(runtime) /
+ runtime->period_size;
+ } else {
+ stream->periods_avail = snd_pcm_capture_avail(runtime) /
+ runtime->period_size;
+ }
+
+ pr_debug("saudio.c:stream->periods_avail is %d,block count is %d",
+ stream->periods_avail, sblock_get_free_count(stream->dst,
+ stream->channel));
+
+ stream->periods_tosend = runtime->periods - stream->periods_avail;
+
+ ADEBUG();
+
+ while (stream->periods_tosend) {
+
+ result = sblock_get(stream->dst, stream->channel, &blk, 0);
+ if (result) {
+ break;
+ }
+ stream->last_getblk_count++;
+ common = (struct cmd_common *)blk.addr;
+ blk.length = frames_to_bytes(runtime, runtime->period_size);
+ common->command = SAUDIO_DATA_PCM;
+ common->sub_cmd = stream->stream_id;
+ common->reserved1 =
+ stream->substream->dma_buffer.addr +
+ stream->period * blk.length;
+
+ sblock_send(stream->dst, stream->channel, &blk);
+
+ stream->period++;
+ stream->period = stream->period % runtime->periods;
+ stream->periods_tosend--;
+ }
+
+ pr_debug(":sblock_getblock_count trigger is %d \n",
+ stream->last_getblk_count);
+
+ return result;
+}
+
+static int saudio_data_transfer_process(struct saudio_stream *stream,
+ struct saudio_msg *msg)
+{
+ struct snd_pcm_runtime *runtime = stream->substream->runtime;
+ struct sblock blk = { 0 };
+ int32_t result = 0;
+ struct cmd_common *common = NULL;
+
+ int32_t elapsed_blks = 0;
+ int32_t periods_avail;
+ int32_t periods_tosend;
+ int32_t cur_blk_count = 0;
+
+ cur_blk_count = sblock_get_free_count(stream->dst, stream->channel);
+
+ elapsed_blks =
+ (cur_blk_count + stream->last_getblk_count - stream->blk_count) -
+ stream->last_elapsed_count;
+
+ if (stream->stream_id == SNDRV_PCM_STREAM_PLAYBACK) {
+ periods_avail = snd_pcm_playback_avail(runtime) /
+ runtime->period_size;
+ } else {
+ periods_avail = snd_pcm_capture_avail(runtime) /
+ runtime->period_size;
+ }
+
+ periods_tosend = stream->periods_avail - periods_avail;
+ if (periods_tosend > 0) {
+ stream->periods_tosend += periods_tosend;
+ }
+
+ if (stream->periods_tosend) {
+ while (stream->periods_tosend) {
+ result =
+ sblock_get(stream->dst, stream->channel, &blk, 0);
+ if (result) {
+ break;
+ }
+ stream->last_getblk_count++;
+ common = (struct cmd_common *)blk.addr;
+ blk.length =
+ frames_to_bytes(runtime, runtime->period_size);
+ common->command = SAUDIO_DATA_PCM;
+ common->sub_cmd = stream->stream_id;
+ common->reserved1 =
+ stream->substream->dma_buffer.addr +
+ stream->period * blk.length;
+
+ sblock_send(stream->dst, stream->channel, &blk);
+
+ stream->periods_tosend--;
+ stream->period++;
+ stream->period = stream->period % runtime->periods;
+ }
+
+ } else {
+ pr_debug("saudio.c: saudio no data to send ");
+ if (sblock_get_free_count(stream->dst, stream->channel) ==
+ SAUDIO_STREAM_BLOCK_COUNT) {
+ pr_debug
+ ("saudio.c: saudio no data to send and is empty ");
+ result =
+ sblock_get(stream->dst, stream->channel, &blk, 0);
+ if (result) {
+ ETRACE("saudio.c: no data and no blk\n");
+ } else {
+ stream->last_getblk_count++;
+ common = (struct cmd_common *)blk.addr;
+ common->command = SAUDIO_DATA_SILENCE;
+ common->sub_cmd = stream->stream_id;
+
+ sblock_send(stream->dst, stream->channel, &blk);
+ stream->last_elapsed_count++;
+ schedule_timeout_interruptible(msecs_to_jiffies(5));
+ }
+ }
+ }
+
+ while (elapsed_blks > 0) {
+ elapsed_blks--;
+ stream->hwptr_done++;
+ stream->hwptr_done %= runtime->periods;
+ snd_pcm_period_elapsed(stream->substream);
+ stream->periods_avail++;
+ stream->last_elapsed_count++;
+ }
+
+ return 0;
+}
+
+static int saudio_cmd_prepare_process(struct saudio_dev_ctrl *dev_ctrl,
+ struct saudio_msg *msg)
+{
+ struct sblock blk;
+ struct snd_saudio *saudio = NULL;
+ int32_t result = 0;
+ struct snd_pcm_runtime *runtime =
+ dev_ctrl->stream[msg->stream_id].substream->runtime;
+ ADEBUG();
+ saudio = dev_ctrl->stream[msg->stream_id].saudio;
+ result =
+ sblock_get(dev_ctrl->dst, dev_ctrl->channel, (struct sblock *)&blk,
+ CMD_TIMEOUT);
+ if (!result) {
+ struct cmd_prepare *prepare = (struct cmd_prepare *)blk.addr;
+ prepare->common.command = SAUDIO_CMD_PREPARE;
+ prepare->common.sub_cmd = msg->stream_id;
+ prepare->rate = runtime->rate;
+ prepare->channels = runtime->channels;
+ prepare->format = runtime->format;
+ prepare->period =
+ frames_to_bytes(runtime, runtime->period_size);
+ prepare->periods = runtime->periods;
+ blk.length = sizeof(struct cmd_prepare);
+
+ mutex_lock(&dev_ctrl->mutex);
+
+ sblock_send(dev_ctrl->dst, dev_ctrl->channel,
+ (struct sblock *)&blk);
+ result =
+ saudio_wait_common_cmd(dev_ctrl->dst, dev_ctrl->channel,
+ SAUDIO_CMD_PREPARE_RET,
+ 0, CMD_TIMEOUT);
+ if (result && (result != (-ERESTARTSYS)))
+ saudio_snd_card_free(saudio);
+
+ mutex_unlock(&dev_ctrl->mutex);
+ }
+ pr_debug("saudio_cmd_prepare_process result is %d", result);
+ return result;
+
+}
+
+static void sblock_notifier(int event, void *data)
+{
+ struct saudio_stream *stream = data;
+ struct saudio_msg msg = { 0 };
+ int result = 0;
+
+ if (event == SBLOCK_NOTIFY_GET) {
+ if (stream->stream_state == SAUDIO_TRIGGERED) {
+ mutex_lock(&stream->mutex);
+ result = saudio_data_transfer_process(stream, &msg);
+ mutex_unlock(&stream->mutex);
+ } else {
+ pr_debug("\n: saudio is stopped\n");
+ }
+ }
+}
+
+static int saudio_snd_card_free(const struct snd_saudio *saudio)
+{
+ int result = 0;
+ printk(KERN_INFO "saudio:saudio_snd_card free in dst %d,channel %d\n",
+ saudio->dst, saudio->channel);
+ queue_work(saudio->queue, (struct work_struct *)&saudio->card_free_work);
+ printk(KERN_INFO
+ "saudio:saudio_snd_card free out %d ,dst %d, channel %d\n",
+ result, saudio->dst, saudio->channel);
+ return 0;
+}
+
+static void saudio_snd_wait_modem_restart(struct snd_saudio *saudio)
+{
+ struct saudio_dev_ctrl *dev_ctrl = NULL;
+ int result = 0;
+ dev_ctrl = &saudio->dev_ctrl[0];
+ while (1) {
+ result =
+ saudio_wait_common_cmd(dev_ctrl->dst,
+ dev_ctrl->monitor_channel,
+ SAUDIO_CMD_HANDSHAKE, 0, -1);
+ if (result) {
+ schedule_timeout_interruptible(msecs_to_jiffies(1000));
+ printk(KERN_ERR "saudio_wait_monitor_cmd error %d\n",
+ result);
+ continue;
+ } else {
+ while (1) {
+ result =
+ saudio_send_common_cmd(dev_ctrl->dst,
+ dev_ctrl->
+ monitor_channel,
+ SAUDIO_CMD_HANDSHAKE_RET,
+ 0, -1);
+ if (!result) {
+ break;
+ }
+ printk(KERN_ERR
+ "saudio_send_monitor_cmd error %d\n",
+ result);
+ schedule_timeout_interruptible(msecs_to_jiffies
+ (1000));
+ }
+ }
+ break;
+ }
+}
+
+static int saudio_snd_notify_modem_clear(struct snd_saudio *saudio)
+{
+ struct saudio_dev_ctrl *dev_ctrl = NULL;
+ int result = 0;
+ dev_ctrl = &saudio->dev_ctrl[0];
+ printk(KERN_INFO "saudio.c:saudio_snd_notify_mdem_clear in");
+ result =
+ saudio_send_common_cmd(dev_ctrl->dst, dev_ctrl->monitor_channel,
+ SAUDIO_CMD_RESET, 0, -1);
+ if (result) {
+ printk(KERN_ERR "saudio_send_modem_reset_cmd error %d\n",
+ result);
+ }
+ else {
+ result =
+ saudio_wait_common_cmd(dev_ctrl->dst, dev_ctrl->monitor_channel,
+ SAUDIO_CMD_RESET_RET, 0, CMD_MODEM_RESET_TIMEOUT);
+ if (result) {
+ printk(KERN_ERR "saudio_wait_monitor_cmd error %d\n", result);
+ }
+ }
+ printk(KERN_INFO "saudio.c:saudio_snd_notify_mdem_clear out");
+ return result;
+}
+
+static int saudio_snd_init_ipc(struct snd_saudio *saudio)
+{
+ int result = 0;
+ int32_t i = 0, j = 0;
+ struct saudio_stream *stream = NULL;
+ struct saudio_dev_ctrl *dev_ctrl = NULL;
+
+ ADEBUG();
+
+ for (i = 0; i < SAUDIO_DEV_MAX; i++) { /* now only support one device */
+ dev_ctrl = &saudio->dev_ctrl[i];
+ result =
+ sblock_create(dev_ctrl->dst, dev_ctrl->monitor_channel,
+ SAUDIO_MONITOR_BLOCK_COUNT, CMD_BLOCK_SIZE,
+ SAUDIO_MONITOR_BLOCK_COUNT, CMD_BLOCK_SIZE);
+ if (result) {
+ ETRACE
+ ("saudio:monitor channel create failed result is %d\n",
+ result);
+ goto __nodev;
+ }
+ result =
+ sblock_create(dev_ctrl->dst, dev_ctrl->channel,
+ SAUDIO_CMD_BLOCK_COUNT, CMD_BLOCK_SIZE,
+ SAUDIO_CMD_BLOCK_COUNT, CMD_BLOCK_SIZE);
+ if (result) {
+ ETRACE
+ ("saudio_thread sblock create failed result is %d\n",
+ result);
+ goto __nodev;
+ }
+ pr_debug("saudio_thread sblock create result is %d\n", result);
+
+ for (j = 0; j < SAUDIO_STREAM_MAX; j++) {
+ stream = &dev_ctrl->stream[j];
+ result =
+ sblock_create(stream->dst, stream->channel,
+ SAUDIO_STREAM_BLOCK_COUNT,
+ TX_DATA_BLOCK_SIZE,
+ SAUDIO_STREAM_BLOCK_COUNT,
+ RX_DATA_BLOCK_SIZE);
+ if (result) {
+ ETRACE
+ ("saudio_thread sblock create failed result is %d\n",
+ result);
+ goto __nodev;
+ }
+ sblock_register_notifier(stream->dst, stream->channel,
+ sblock_notifier, stream);
+ pr_debug("saudio_thread sblock create result is %d\n",
+ result);
+ }
+ }
+ ADEBUG();
+ return result;
+__nodev:
+ ETRACE("initialization failed\n");
+ return result;
+}
+
+static int saudio_snd_init_card(struct snd_saudio *saudio)
+{
+ int result = 0;
+ int32_t i = 0, j = 0, err = 0;
+ struct saudio_stream *stream = NULL;
+ struct saudio_dev_ctrl *dev_ctrl = NULL;
+ struct snd_card *saudio_card = NULL;
+
+ ADEBUG();
+
+ if (!saudio) {
+ return -1;
+ }
+
+ mutex_lock(&snd_sound);
+
+ result =
+ snd_card_create(SNDRV_DEFAULT_IDX1, saudio->dev_ctrl[0].name,
+ THIS_MODULE, sizeof(struct snd_saudio *),
+ &saudio_card);
+ if (!saudio_card) {
+ printk(KERN_ERR "saudio:snd_card_create faild result is %d\n",
+ result);
+ mutex_unlock(&snd_sound);
+ return -1;
+ }
+ saudio->card = saudio_card;
+ saudio_card->private_data = saudio;
+
+ for (i = 0; i < SAUDIO_DEV_MAX; i++) { /* now only support one device */
+ dev_ctrl = &saudio->dev_ctrl[i];
+ mutex_init(&dev_ctrl->mutex);
+ err = snd_card_saudio_pcm(saudio, i, 1);
+ if (err < 0) {
+ mutex_unlock(&snd_sound);
+ goto __nodev;
+ }
+ for (j = 0; j < SAUDIO_STREAM_MAX; j++) {
+ stream = &dev_ctrl->stream[j];
+ stream->dev_ctrl = dev_ctrl;
+
+ stream->stream_state = SAUDIO_IDLE;
+ stream->stream_id = j;
+ stream->saudio = saudio;
+ mutex_init(&stream->mutex);
+ }
+ }
+ ADEBUG();
+
+ memcpy(saudio->card->driver, dev_ctrl->name, SAUDIO_CARD_NAME_LEN_MAX);
+ memcpy(saudio->card->shortname, dev_ctrl->name,
+ SAUDIO_CARD_NAME_LEN_MAX);
+ memcpy(saudio->card->longname, dev_ctrl->name,
+ SAUDIO_CARD_NAME_LEN_MAX);
+
+ err = snd_card_register(saudio->card);
+
+ mutex_unlock(&snd_sound);
+
+ if (err == 0) {
+ printk(KERN_INFO "saudio.c:snd_card create ok\n");
+ return 0;
+ }
+__nodev:
+ if (saudio) {
+ if (saudio->card) {
+ mutex_lock(&snd_sound);
+ snd_card_free(saudio->card);
+ saudio->card = NULL;
+ mutex_unlock(&snd_sound);
+ }
+ }
+ printk("saudio.c:initialization failed\n");
+ return err;
+}
+
+static int saudio_ctrl_thread(void *data)
+{
+ int result = 0;
+ struct snd_saudio *saudio = (struct snd_saudio *)data;
+ ADEBUG();
+
+ result = saudio_snd_init_ipc(saudio);
+ if (result) {
+ printk(KERN_ERR "saudio:saudio_snd_init_ipc error %d\n",
+ result);
+ return -1;
+ }
+ while (!kthread_should_stop()) {
+ printk(KERN_INFO
+ "%s,saudio: waiting for modem boot handshake,dst %d,channel %d\n",
+ __func__, saudio->dst, saudio->channel);
+ saudio_snd_wait_modem_restart(saudio);
+
+ saudio->in_init = 1;
+
+ printk(KERN_INFO
+ "%s,saudio: modem boot and handshake ok,dst %d, channel %d\n",
+ __func__, saudio->dst, saudio->channel);
+ saudio_snd_card_free(saudio);
+ printk(KERN_INFO
+ "saudio_ctrl_thread flush work queue in dst %d, channel %d\n",
+ saudio->dst, saudio->channel);
+
+ flush_workqueue(saudio->queue);
+
+ saudio->in_init = 0;
+
+ printk(KERN_INFO
+ "saudio_ctrl_thread flush work queue out,dst %d, channel %d\n",
+ saudio->dst, saudio->channel);
+
+ if(saudio_snd_notify_modem_clear(saudio)) {
+ printk(KERN_ERR " saudio_ctrl_thread modem error again when notify modem clear \n");
+ continue;
+ }
+
+ saudio_clear_ctrl_cmd(saudio);
+
+ if(!saudio->card) {
+ result = saudio_snd_init_card(saudio);
+ printk(KERN_INFO
+ "saudio: snd card init reulst %d, dst %d, channel %d\n",
+ result, saudio->dst, saudio->channel);
+ }
+ mutex_lock(&saudio->mutex);
+ saudio->state = 1;
+ mutex_unlock(&saudio->mutex);
+ }
+ ETRACE("saudio_ctrl_thread create ok\n");
+
+ return 0;
+}
+
+static void saudio_work_card_free_handler(struct work_struct *data)
+{
+ struct snd_saudio *saudio =
+ container_of(data, struct snd_saudio, card_free_work);
+ printk(KERN_INFO "saudio: card free handler in\n");
+
+ if (saudio->card) {
+ int result;
+ printk(KERN_INFO
+ "saudio: work_handler:snd card free in,dst %d, channel %d\n",
+ saudio->dst, saudio->channel);
+ mutex_lock(&saudio->mutex);
+ saudio->state = 0;
+ mutex_unlock(&saudio->mutex);
+ if (!saudio->in_init)
+ saudio_send_common_cmd(saudio->dst, saudio->channel, 0,
+ SAUDIO_CMD_HANDSHAKE, -1);
+ printk(KERN_INFO
+ "saudio: work_handler:snd card free reulst %d,dst %d, channel %d\n",
+ result, saudio->dst, saudio->channel);
+ }
+ printk(KERN_INFO "saudio: card free handler out\n");
+}
+
+static int snd_saudio_probe(struct platform_device *devptr)
+{
+ struct snd_saudio *saudio = NULL;
+ static pid_t thread_id = (pid_t) - 1;
+#ifdef CONFIG_OF
+ int ret, id, ctrl_ch, playback_ch, capture_ch, monitor_ch;
+ const char *name = NULL;
+ struct saudio_init_data snd_init_data = {0};
+ const char *saudio_names = "sprd,saudio-names";
+ const char *saudio_dst_id = "sprd,saudio-dst-id";
+ const char *ctrl_channel = "sprd,ctrl_channel";
+ const char *playback_channel = "sprd,playback_channel";
+ const char *capture_channel = "sprd,capture_channel";
+ const char *monitor_channel = "sprd,monitor_channel";
+ struct saudio_init_data *init_data = &snd_init_data;
+#else
+ struct saudio_init_data *init_data = devptr->dev.platform_data;
+#endif
+
+ ADEBUG();
+
+#ifdef CONFIG_OF
+ ret = of_property_read_u32(devptr->dev.of_node, saudio_dst_id, &id);
+ if (ret) {
+ printk("saudio: %s: missing %s in dt node\n", __func__, saudio_dst_id);
+ return ret;
+ } else {
+ printk("saudio: %s: %s is %d\n", __func__, saudio_dst_id, id);
+ }
+ ret = of_property_read_u32(devptr->dev.of_node, ctrl_channel, &ctrl_ch);
+ if (ret) {
+ printk("saudio: %s: missing %s in dt node\n", __func__, ctrl_channel);
+ return ret;
+ } else {
+ printk("saudio: %s: %s is %d\n", __func__, ctrl_channel, ctrl_ch);
+ }
+ ret = of_property_read_u32(devptr->dev.of_node, playback_channel, &playback_ch);
+ if (ret) {
+ printk("saudio: %s: missing %s in dt node\n", __func__, playback_channel);
+ return ret;
+ } else {
+ printk("saudio: %s: %s is %d\n", __func__, playback_channel, playback_ch);
+ }
+ ret = of_property_read_u32(devptr->dev.of_node, capture_channel, &capture_ch);
+ if (ret) {
+ printk("saudio: %s: missing %s in dt node\n", __func__, capture_channel);
+ return ret;
+ } else {
+ printk("saudio: %s: %s is %d\n", __func__, capture_channel, capture_ch);
+ }
+ ret = of_property_read_u32(devptr->dev.of_node, monitor_channel, &monitor_ch);
+ if (ret) {
+ printk("saudio: %s: missing %s in dt node\n", __func__, monitor_channel);
+ return ret;
+ } else {
+ printk("saudio: %s: %s is %d\n", __func__, monitor_channel, monitor_ch);
+ }
+ ret = of_property_read_string(devptr->dev.of_node, saudio_names, &name);
+ if (ret) {
+ printk("saudio: %s: missing %s in dt node\n", __func__, saudio_names);
+ return ret;
+ } else {
+ printk("saudio: %s: %s is %s\n", __func__, saudio_names, name);
+ }
+ init_data->name = (char *)name;
+ init_data->dst = id;
+ init_data->ctrl_channel = ctrl_ch;
+ init_data->playback_channel = playback_ch;
+ init_data->capture_channel = capture_ch;
+ init_data->monitor_channel = monitor_ch;
+#endif
+
+ if (!(saudio = saudio_card_probe(init_data))) {
+ return -1;
+ }
+ saudio->pdev = devptr;
+
+ saudio->queue = create_singlethread_workqueue("saudio");
+ if (!saudio->queue) {
+ printk("saudio:workqueue create error %d\n", (int)saudio->queue);
+ if (saudio) {
+ kfree(saudio);
+ saudio = NULL;
+ }
+ return -1;
+ }
+ printk("saudio:workqueue create ok");
+ INIT_WORK(&saudio->card_free_work, saudio_work_card_free_handler);
+
+ platform_set_drvdata(devptr, saudio);
+
+ saudio->thread_id = kthread_create(saudio_ctrl_thread, saudio, "saudio-%d-%d",saudio->dst,saudio->channel);
+ if (IS_ERR(saudio->thread_id)) {
+ ETRACE("virtual audio cmd kernel thread creation failure \n");
+ destroy_workqueue(saudio->queue);
+ saudio->queue = NULL;
+ saudio->thread_id = NULL;
+ kfree(saudio);
+ platform_set_drvdata(devptr, NULL);
+ return -1;
+ }
+ wake_up_process(saudio->thread_id);
+ return 0;
+}
+
+static int snd_saudio_remove(struct platform_device *devptr)
+{
+ struct snd_saudio *saudio = platform_get_drvdata(devptr);
+
+ if (saudio) {
+ if (!IS_ERR_OR_NULL(saudio->thread_id)) {
+ kthread_stop(saudio->thread_id);
+ saudio->thread_id = NULL;
+ }
+ if (saudio->queue)
+ destroy_workqueue(saudio->queue);
+ if (saudio->pdev) {
+ platform_device_unregister(saudio->pdev);
+ }
+ if (saudio->card) {
+ mutex_lock(&snd_sound);
+ snd_card_free(saudio->card);
+ saudio->card = NULL;
+ mutex_unlock(&snd_sound);
+ }
+ kfree(saudio);
+ }
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id saudio_dt_match[] = {
+ {.compatible = "sprd,saudio"},
+ {},
+};
+#endif
+
+static struct platform_driver snd_saudio_driver = {
+ .probe = snd_saudio_probe,
+ .remove = snd_saudio_remove,
+ .driver = {
+ .name = "saudio",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_OF
+ .of_match_table = saudio_dt_match,
+#endif
+ },
+};
+
+static int __init alsa_card_saudio_init(void)
+{
+ int err;
+ err = platform_driver_register(&snd_saudio_driver);
+ if (err < 0)
+ printk("saudio: platform_driver_register err %d\n", err);
+ return 0;
+}
+
+static void __exit alsa_card_saudio_exit(void)
+{
+ platform_driver_unregister(&snd_saudio_driver);
+ return;
+}
+
+module_init(alsa_card_saudio_init)
+module_exit(alsa_card_saudio_exit)