Initial Contribution
msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142
Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/arch/arm/mach-msm/qdsp5v2/Makefile b/arch/arm/mach-msm/qdsp5v2/Makefile
new file mode 100755
index 0000000..3ae3c1b
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/Makefile
@@ -0,0 +1,22 @@
+obj-y += afe.o audio_interct.o mi2s.o audio_dev_ctl.o voice.o
+
+ifeq ($(CONFIG_TIMPANI_CODEC), y)
+obj-y += snddev_icodec.o
+else ifeq ($(CONFIG_MARIMBA_CODEC), y)
+obj-y += snddev_icodec.o
+endif
+
+obj-$(CONFIG_MARIMBA_CODEC) += snddev_data_marimba.o
+obj-$(CONFIG_TIMPANI_CODEC) += snddev_data_timpani.o
+
+obj-y += audio_pcm.o audpp.o audio_mp3.o audio_wma.o audio_aac.o audio_amrnb.o
+obj-y += audio_amrwb.o audio_wmapro.o audio_adpcm.o audio_evrc.o audio_qcelp.o
+obj-y += aux_pcm.o snddev_ecodec.o audio_out.o
+obj-y += audio_lpa.o mp3_funcs.o pcm_funcs.o
+obj-y += audpreproc.o audio_pcm_in.o audio_aac_in.o audio_amrnb_in.o audio_a2dp_in.o
+obj-y += audio_evrc_in.o audio_qcelp_in.o
+obj-y += adsp.o adsp_driver.o adsp_info.o
+obj-y += audio_acdb.o snddev_virtual.o
+obj-y += audio_fm.o
+obj-y += lpa.o snddev_mi2s.o
+obj-y += audio_mvs.o
\ No newline at end of file
diff --git a/arch/arm/mach-msm/qdsp5v2/adsp.c b/arch/arm/mach-msm/qdsp5v2/adsp.c
new file mode 100644
index 0000000..6d4d074
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/adsp.c
@@ -0,0 +1,1229 @@
+/*
+ * Register/Interrupt access for userspace aDSP library.
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (c) 2008-2009,2011 Code Aurora Forum. All rights reserved.
+ * Author: Iliyan Malchev <ibm@android.com>
+ *
+ * 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.
+ *
+ */
+
+/* TODO:
+ * - move shareable rpc code outside of adsp.c
+ * - general solution for virt->phys patchup
+ * - queue IDs should be relative to modules
+ * - disallow access to non-associated queues
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <mach/msm_iomap.h>
+#include <mach/clk.h>
+#include <mach/msm_adsp.h>
+#include "adsp.h"
+#include <mach/debug_mm.h>
+#include <linux/debugfs.h>
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *dentry_adsp;
+static struct dentry *dentry_wdata;
+static struct dentry *dentry_rdata;
+static int wdump, rdump;
+#endif /* CONFIG_DEBUG_FS */
+
+#define INT_ADSP INT_ADSP_A9_A11
+
+static struct adsp_info adsp_info;
+static struct msm_adsp_module *adsp_modules;
+static int adsp_open_count;
+
+static DEFINE_MUTEX(adsp_open_lock);
+
+/* protect interactions with the ADSP command/message queue */
+static spinlock_t adsp_cmd_lock;
+static spinlock_t adsp_write_lock;
+
+static uint32_t current_image = -1;
+
+void adsp_set_image(struct adsp_info *info, uint32_t image)
+{
+ current_image = image;
+}
+
+/*
+ * Checks whether the module_id is available in the
+ * module_entries table.If module_id is available returns `0`.
+ * If module_id is not available returns `-ENXIO`.
+ */
+static int32_t adsp_validate_module(uint32_t module_id)
+{
+ uint32_t *ptr;
+ uint32_t module_index;
+ uint32_t num_mod_entries;
+
+ ptr = adsp_info.init_info_ptr->module_entries;
+ num_mod_entries = adsp_info.init_info_ptr->module_table_size;
+
+ for (module_index = 0; module_index < num_mod_entries; module_index++)
+ if (module_id == ptr[module_index])
+ return 0;
+
+ return -ENXIO;
+}
+
+static int32_t adsp_validate_queue(uint32_t mod_id, unsigned q_idx,
+ uint32_t size)
+{
+ int32_t i;
+ struct adsp_rtos_mp_mtoa_init_info_type *sptr;
+
+ sptr = adsp_info.init_info_ptr;
+ for (i = 0; i < sptr->mod_to_q_entries; i++)
+ if (mod_id == sptr->mod_to_q_tbl[i].module)
+ if (q_idx == sptr->mod_to_q_tbl[i].q_type) {
+ if (size <= sptr->mod_to_q_tbl[i].q_max_len)
+ return 0;
+ MM_ERR("q_idx: %d is not a valid queue \
+ for module %x\n", q_idx, mod_id);
+ return -EINVAL;
+ }
+ MM_ERR("cmd_buf size is more than allowed size\n");
+ return -EINVAL;
+}
+
+uint32_t adsp_get_module(struct adsp_info *info, uint32_t task)
+{
+ return info->task_to_module[current_image][task];
+}
+
+uint32_t adsp_get_queue_offset(struct adsp_info *info, uint32_t queue_id)
+{
+ return info->queue_offset[current_image][queue_id];
+}
+
+static int rpc_adsp_rtos_app_to_modem(uint32_t cmd, uint32_t module,
+ struct msm_adsp_module *adsp_module)
+{
+ struct adsp_rtos_atom_cmd adspsvc_cmd;
+ int err;
+
+ adspsvc_cmd.cmd = cmd;
+ adspsvc_cmd.proc_id = RPC_ADSP_RTOS_PROC_APPS;
+ adspsvc_cmd.module = module;
+ adspsvc_cmd.cb_handle = adsp_info.cb_handle;
+
+ err = dalrpc_fcn_5(DALDEVICE_ADSP_CMD_IDX | 0x80000000,
+ adsp_info.handle,
+ &adspsvc_cmd, sizeof(adspsvc_cmd));
+ if (err < 0)
+ MM_ERR("ADSP command send Failed\n");
+
+ return 0;
+}
+
+static int get_module_index(uint32_t id)
+{
+ int mod_idx;
+ for (mod_idx = 0; mod_idx < adsp_info.module_count; mod_idx++)
+ if (adsp_info.module[mod_idx].id == id)
+ return mod_idx;
+
+ return -ENXIO;
+}
+
+static struct msm_adsp_module *find_adsp_module_by_id(
+ struct adsp_info *info, uint32_t id)
+{
+ int mod_idx;
+
+ if (id > info->max_module_id) {
+ return NULL;
+ } else {
+ mod_idx = get_module_index(id);
+ if (mod_idx < 0)
+ return NULL;
+ return info->id_to_module[mod_idx];
+ }
+}
+
+static struct msm_adsp_module *find_adsp_module_by_name(
+ struct adsp_info *info, const char *name)
+{
+ unsigned n;
+ for (n = 0; n < info->module_count; n++)
+ if (!strcmp(name, adsp_modules[n].name))
+ return adsp_modules + n;
+ return NULL;
+}
+
+/*
+ * Send RPC_ADSP_RTOS_CMD_GET_INIT_INFO cmd to ARM9 and get
+ * queue offsets and module entries (init info) as part of the event.
+ */
+static void msm_get_init_info(void)
+{
+ struct adsp_rtos_atom_cmd cmd;
+ int err;
+
+ cmd.cmd = RPC_ADSP_RTOS_CMD_GET_INIT_INFO;
+ cmd.proc_id = RPC_ADSP_RTOS_PROC_APPS;
+ cmd.module = 0;
+ cmd.cb_handle = adsp_info.cb_handle;
+
+ err = dalrpc_fcn_5(DALDEVICE_ADSP_CMD_IDX | 0x80000000,
+ adsp_info.handle,
+ &cmd, sizeof(cmd));
+ if (err < 0)
+ MM_ERR("INIT_INFO command send Failed\n");
+}
+
+int msm_adsp_get(const char *name, struct msm_adsp_module **out,
+ struct msm_adsp_ops *ops, void *driver_data)
+{
+ struct msm_adsp_module *module;
+ int rc = 0;
+
+ module = find_adsp_module_by_name(&adsp_info, name);
+ if (!module)
+ return -ENODEV;
+
+ mutex_lock(&module->lock);
+ MM_DBG("opening module %s\n", module->name);
+
+ if (module->ops) {
+ rc = -EBUSY;
+ mutex_unlock(&module->lock);
+ goto done;
+ }
+
+ module->ops = ops;
+ module->driver_data = driver_data;
+ *out = module;
+ mutex_unlock(&module->lock);
+ rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_REGISTER_APP,
+ module->id, module);
+ if (rc) {
+ mutex_lock(&module->lock);
+ module->ops = NULL;
+ module->driver_data = NULL;
+ *out = NULL;
+ MM_ERR("REGISTER_APP failed\n");
+ mutex_unlock(&module->lock);
+ goto done;
+ }
+
+ MM_INFO("module %s has been registered\n", module->name);
+
+done:
+ return rc;
+}
+EXPORT_SYMBOL(msm_adsp_get);
+
+void msm_adsp_put(struct msm_adsp_module *module)
+{
+ unsigned long flags;
+
+ mutex_lock(&module->lock);
+ if (module->ops) {
+ MM_INFO("closing module %s\n", module->name);
+
+ /* lock to ensure a dsp event cannot be delivered
+ * during or after removal of the ops and driver_data
+ */
+ spin_lock_irqsave(&adsp_cmd_lock, flags);
+ module->ops = NULL;
+ module->driver_data = NULL;
+ spin_unlock_irqrestore(&adsp_cmd_lock, flags);
+
+ if (module->state != ADSP_STATE_DISABLED) {
+ MM_INFO("disabling module %s\n", module->name);
+ mutex_unlock(&module->lock);
+ msm_adsp_disable(module);
+ return;
+ }
+ } else {
+ MM_INFO("module %s is already closed\n", module->name);
+ }
+ mutex_unlock(&module->lock);
+}
+EXPORT_SYMBOL(msm_adsp_put);
+
+int __msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr,
+ void *cmd_buf, size_t cmd_size)
+{
+ uint32_t ctrl_word;
+ uint32_t dsp_q_addr;
+ uint32_t dsp_addr;
+ uint32_t cmd_id = 0;
+ int cnt = 0;
+ int ret_status = 0;
+ unsigned long flags;
+ struct adsp_info *info;
+
+ if (!module || !cmd_buf) {
+ MM_ERR("Called with NULL parameters\n");
+ return -EINVAL;
+ }
+ info = module->info;
+ spin_lock_irqsave(&adsp_write_lock, flags);
+
+ if (module->state != ADSP_STATE_ENABLED) {
+ spin_unlock_irqrestore(&adsp_write_lock, flags);
+ MM_ERR("module %s not enabled before write\n", module->name);
+ return -ENODEV;
+ }
+ if (adsp_validate_module(module->id)) {
+ spin_unlock_irqrestore(&adsp_write_lock, flags);
+ MM_ERR("module id validation failed %s %d\n",
+ module->name, module->id);
+ return -ENXIO;
+ }
+ if (dsp_queue_addr >= QDSP_MAX_NUM_QUEUES) {
+ spin_unlock_irqrestore(&adsp_write_lock, flags);
+ MM_ERR("Invalid Queue Index: %d\n", dsp_queue_addr);
+ return -ENXIO;
+ }
+ if (adsp_validate_queue(module->id, dsp_queue_addr, cmd_size)) {
+ spin_unlock_irqrestore(&adsp_write_lock, flags);
+ return -EINVAL;
+ }
+ dsp_q_addr = adsp_get_queue_offset(info, dsp_queue_addr);
+ dsp_q_addr &= ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M;
+
+ /* Poll until the ADSP is ready to accept a command.
+ * Wait for 100us, return error if it's not responding.
+ * If this returns an error, we need to disable ALL modules and
+ * then retry.
+ */
+ while (((ctrl_word = readl(info->write_ctrl)) &
+ ADSP_RTOS_WRITE_CTRL_WORD_READY_M) !=
+ ADSP_RTOS_WRITE_CTRL_WORD_READY_V) {
+ if (cnt > 50) {
+ MM_ERR("timeout waiting for DSP write ready\n");
+ ret_status = -EIO;
+ goto fail;
+ }
+ MM_DBG("waiting for DSP write ready\n");
+ udelay(2);
+ cnt++;
+ }
+
+ /* Set the mutex bits */
+ ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M);
+ ctrl_word |= ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V;
+
+ /* Clear the command bits */
+ ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_CMD_M);
+
+ /* Set the queue address bits */
+ ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M);
+ ctrl_word |= dsp_q_addr;
+
+ writel(ctrl_word, info->write_ctrl);
+
+ /* Generate an interrupt to the DSP. This notifies the DSP that
+ * we are about to send a command on this particular queue. The
+ * DSP will in response change its state.
+ */
+ writel(1, info->send_irq);
+
+ /* Poll until the adsp responds to the interrupt; this does not
+ * generate an interrupt from the adsp. This should happen within
+ * 5ms.
+ */
+ cnt = 0;
+ while ((readl(info->write_ctrl) &
+ ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M) ==
+ ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V) {
+ if (cnt > 2500) {
+ MM_ERR("timeout waiting for adsp ack\n");
+ ret_status = -EIO;
+ goto fail;
+ }
+ udelay(2);
+ cnt++;
+ }
+
+ /* Read the ctrl word */
+ ctrl_word = readl(info->write_ctrl);
+
+ if ((ctrl_word & ADSP_RTOS_WRITE_CTRL_WORD_STATUS_M) !=
+ ADSP_RTOS_WRITE_CTRL_WORD_NO_ERR_V) {
+ ret_status = -EAGAIN;
+ goto fail;
+ } else {
+ /* No error */
+ /* Get the DSP buffer address */
+ dsp_addr = (ctrl_word & ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M) +
+ (uint32_t)MSM_AD5_BASE;
+
+ if (dsp_addr < (uint32_t)(MSM_AD5_BASE + QDSP_RAMC_OFFSET)) {
+ uint16_t *buf_ptr = (uint16_t *) cmd_buf;
+ uint16_t *dsp_addr16 = (uint16_t *)dsp_addr;
+ cmd_size /= sizeof(uint16_t);
+
+ /* Save the command ID */
+ cmd_id = (uint32_t) buf_ptr[0];
+
+ /* Copy the command to DSP memory */
+ cmd_size++;
+ while (--cmd_size)
+ *dsp_addr16++ = *buf_ptr++;
+ } else {
+ uint32_t *buf_ptr = (uint32_t *) cmd_buf;
+ uint32_t *dsp_addr32 = (uint32_t *)dsp_addr;
+ cmd_size /= sizeof(uint32_t);
+
+ /* Save the command ID */
+ cmd_id = buf_ptr[0];
+
+ cmd_size++;
+ while (--cmd_size)
+ *dsp_addr32++ = *buf_ptr++;
+ }
+
+ /* Set the mutex bits */
+ ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M);
+ ctrl_word |= ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V;
+
+ /* Set the command bits to write done */
+ ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_CMD_M);
+ ctrl_word |= ADSP_RTOS_WRITE_CTRL_WORD_CMD_WRITE_DONE_V;
+
+ /* Set the queue address bits */
+ ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M);
+ ctrl_word |= dsp_q_addr;
+
+ writel(ctrl_word, info->write_ctrl);
+
+ /* Generate an interrupt to the DSP. It does not respond with
+ * an interrupt, and we do not need to wait for it to
+ * acknowledge, because it will hold the mutex lock until it's
+ * ready to receive more commands again.
+ */
+ writel(1, info->send_irq);
+
+ module->num_commands++;
+ } /* Ctrl word status bits were 00, no error in the ctrl word */
+
+fail:
+ spin_unlock_irqrestore(&adsp_write_lock, flags);
+ return ret_status;
+}
+EXPORT_SYMBOL(msm_adsp_write);
+
+int msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr,
+ void *cmd_buf, size_t cmd_size)
+{
+ int rc, retries = 0;
+#ifdef CONFIG_DEBUG_FS
+ uint16_t *ptr;
+ int ii;
+
+ if (wdump > 0) {
+ ptr = cmd_buf;
+ pr_info("A->D:%x\n", module->id);
+ pr_info("adsp: %x %d\n", dsp_queue_addr, cmd_size);
+ for (ii = 0; ii < cmd_size/2; ii++)
+ pr_info("%x ", ptr[ii]);
+ pr_info("\n");
+ }
+#endif /* CONFIG_DEBUG_FS */
+ do {
+ rc = __msm_adsp_write(module, dsp_queue_addr, cmd_buf,
+ cmd_size);
+ if (rc == -EAGAIN)
+ udelay(50);
+ } while (rc == -EAGAIN && retries++ < 300);
+ if (retries > 20)
+ MM_INFO("%s command took %d attempts: rc %d\n",
+ module->name, retries, rc);
+ return rc;
+}
+
+#ifdef CONFIG_MSM_ADSP_REPORT_EVENTS
+static void *event_addr;
+static void read_event(void *buf, size_t len)
+{
+ uint32_t dptr[3];
+ struct adsp_rtos_mp_mtoa_s_type *sptr;
+ struct adsp_rtos_mp_mtoa_type *pkt_ptr;
+
+ sptr = event_addr;
+ pkt_ptr = &sptr->adsp_rtos_mp_mtoa_data.mp_mtoa_packet;
+
+ dptr[0] = sptr->mp_mtoa_header.event;
+ dptr[1] = pkt_ptr->module;
+ dptr[2] = pkt_ptr->image;
+
+ if (len > EVENT_LEN)
+ len = EVENT_LEN;
+
+ memcpy(buf, dptr, len);
+}
+#endif
+
+static void adsp_rtos_mtoa_cb(void *context, uint32_t param,
+ void *evt_buf, uint32_t len)
+{
+ struct adsp_rtos_mp_mtoa_s_type *args = NULL;
+ uint32_t event = 0;
+ uint32_t proc_id = 0;
+ uint32_t module_id;
+ uint32_t image;
+ struct msm_adsp_module *module;
+ struct adsp_rtos_mp_mtoa_type *pkt_ptr;
+ struct queue_to_offset_type *qptr;
+ struct queue_to_offset_type *qtbl;
+ struct mod_to_queue_offsets *mqptr;
+ struct mod_to_queue_offsets *mqtbl;
+ uint32_t *mptr;
+ uint32_t *mtbl;
+ uint32_t q_idx;
+ uint32_t num_entries;
+ uint32_t entries_per_image;
+ struct adsp_rtos_mp_mtoa_init_info_type *iptr;
+ struct adsp_rtos_mp_mtoa_init_info_type *sptr;
+ int32_t i_no, e_idx;
+ static uint32_t init_info_completed;
+ static uint32_t init_info_len =
+ sizeof(struct adsp_rtos_mp_mtoa_header_type);
+ static uint32_t next_init_info_byte;
+ static uint32_t expected_byte = 1;
+ uint32_t hdr_len = sizeof(struct adsp_rtos_mp_mtoa_header_type);
+
+ if (len) {
+ args = (struct adsp_rtos_mp_mtoa_s_type *) evt_buf;
+ event = args->mp_mtoa_header.event;
+ proc_id = args->mp_mtoa_header.proc_id;
+ }
+
+ if (!init_info_completed && event == RPC_ADSP_RTOS_INIT_INFO) {
+ memcpy(((char *)adsp_info.raw_event) + init_info_len,
+ (char *)evt_buf + hdr_len + 4,
+ len - ((hdr_len + 4)));
+ init_info_len += (len - (hdr_len + 4));
+ evt_buf += hdr_len;
+ next_init_info_byte = *(uint32_t *) evt_buf;
+ expected_byte += len;
+ if (next_init_info_byte &&
+ (expected_byte != next_init_info_byte)) {
+ MM_ERR("INIT_INFO - expecting next byte to be %d\n"
+ "\tbut ADSPSVC indicated next byte to be %d\n",
+ expected_byte, next_init_info_byte);
+ return;
+ }
+ if (!next_init_info_byte) {
+ args = adsp_info.raw_event;
+ args->mp_mtoa_header.event = event;
+ args->mp_mtoa_header.proc_id = proc_id;
+ init_info_completed = 1;
+ } else
+ return;
+ }
+
+ if (event == RPC_ADSP_RTOS_INIT_INFO) {
+ MM_INFO("INIT_INFO Event\n");
+ sptr = &args->adsp_rtos_mp_mtoa_data.mp_mtoa_init_packet;
+
+ iptr = adsp_info.init_info_ptr;
+ iptr->image_count = sptr->image_count;
+ if (iptr->image_count > IMG_MAX)
+ iptr->image_count = IMG_MAX;
+ iptr->num_queue_offsets = sptr->num_queue_offsets;
+ num_entries = iptr->num_queue_offsets;
+ if (num_entries > ENTRIES_MAX) {
+ num_entries = ENTRIES_MAX;
+ iptr->num_queue_offsets = ENTRIES_MAX;
+ }
+ qptr = &sptr->queue_offsets_tbl[0][0];
+ for (i_no = 0; i_no < iptr->image_count; i_no++) {
+ qtbl = &iptr->queue_offsets_tbl[i_no][0];
+ for (e_idx = 0; e_idx < num_entries; e_idx++) {
+ qtbl[e_idx].offset = qptr->offset;
+ qtbl[e_idx].queue = qptr->queue;
+ q_idx = qptr->queue;
+ iptr->queue_offsets[i_no][q_idx] =
+ qtbl[e_idx].offset;
+ qptr++;
+ }
+ }
+
+ num_entries = sptr->num_task_module_entries;
+ if (num_entries > ENTRIES_MAX)
+ num_entries = ENTRIES_MAX;
+ iptr->num_task_module_entries = num_entries;
+ entries_per_image = num_entries / iptr->image_count;
+ mptr = &sptr->task_to_module_tbl[0][0];
+ for (i_no = 0; i_no < iptr->image_count; i_no++) {
+ mtbl = &iptr->task_to_module_tbl[i_no][0];
+ for (e_idx = 0; e_idx < entries_per_image; e_idx++) {
+ mtbl[e_idx] = *mptr;
+ mptr++;
+ }
+ }
+
+ iptr->module_table_size = sptr->module_table_size;
+ if (iptr->module_table_size > MODULES_MAX)
+ iptr->module_table_size = MODULES_MAX;
+ mptr = &sptr->module_entries[0];
+ for (i_no = 0; i_no < iptr->module_table_size; i_no++)
+ iptr->module_entries[i_no] = mptr[i_no];
+
+ mqptr = &sptr->mod_to_q_tbl[0];
+ mqtbl = &iptr->mod_to_q_tbl[0];
+ iptr->mod_to_q_entries = sptr->mod_to_q_entries;
+ if (iptr->mod_to_q_entries > ENTRIES_MAX)
+ iptr->mod_to_q_entries = ENTRIES_MAX;
+ for (e_idx = 0; e_idx < iptr->mod_to_q_entries; e_idx++) {
+ mqtbl[e_idx].module = mqptr->module;
+ mqtbl[e_idx].q_type = mqptr->q_type;
+ mqtbl[e_idx].q_max_len = mqptr->q_max_len;
+ mqptr++;
+ }
+
+ adsp_info.init_info_state = ADSP_STATE_INIT_INFO;
+ kfree(adsp_info.raw_event);
+ wake_up(&adsp_info.init_info_wait);
+ return;
+ }
+ pkt_ptr = &args->adsp_rtos_mp_mtoa_data.mp_mtoa_packet;
+ module_id = pkt_ptr->module;
+ image = pkt_ptr->image;
+
+ MM_INFO("rpc event=%d, proc_id=%d, module=%d, image=%d\n",
+ event, proc_id, module_id, image);
+
+ module = find_adsp_module_by_id(&adsp_info, module_id);
+ if (!module) {
+ MM_ERR("module %d is not supported!\n", module_id);
+ return;
+ }
+
+ mutex_lock(&module->lock);
+ switch (event) {
+ case RPC_ADSP_RTOS_MOD_READY:
+ MM_INFO("module %s: READY\n", module->name);
+ module->state = ADSP_STATE_ENABLED;
+ wake_up(&module->state_wait);
+ adsp_set_image(module->info, image);
+ break;
+ case RPC_ADSP_RTOS_MOD_DISABLE:
+ MM_INFO("module %s: DISABLED\n", module->name);
+ module->state = ADSP_STATE_DISABLED;
+ wake_up(&module->state_wait);
+ break;
+ case RPC_ADSP_RTOS_SERVICE_RESET:
+ MM_INFO("module %s: SERVICE_RESET\n", module->name);
+ module->state = ADSP_STATE_DISABLED;
+ wake_up(&module->state_wait);
+ break;
+ case RPC_ADSP_RTOS_CMD_SUCCESS:
+ MM_INFO("module %s: CMD_SUCCESS\n", module->name);
+ break;
+ case RPC_ADSP_RTOS_CMD_FAIL:
+ MM_INFO("module %s: CMD_FAIL\n", module->name);
+ break;
+ case RPC_ADSP_RTOS_DISABLE_FAIL:
+ MM_INFO("module %s: DISABLE_FAIL\n", module->name);
+ break;
+ default:
+ MM_ERR("unknown event %d\n", event);
+ mutex_unlock(&module->lock);
+ return;
+ }
+#ifdef CONFIG_MSM_ADSP_REPORT_EVENTS
+ event_addr = (uint32_t *)evt_buf;
+ if (module->ops)
+ module->ops->event(module->driver_data,
+ EVENT_MSG_ID,
+ EVENT_LEN,
+ read_event);
+#endif
+ mutex_unlock(&module->lock);
+}
+
+static size_t read_event_size;
+static void *read_event_addr;
+
+static void read_event_16(void *buf, size_t len)
+{
+ uint16_t *dst = buf;
+ uint16_t *src = read_event_addr;
+ len /= 2;
+ if (len > read_event_size)
+ len = read_event_size;
+ while (len--)
+ *dst++ = *src++;
+}
+
+static void read_event_32(void *buf, size_t len)
+{
+ uint32_t *dst = buf;
+ uint32_t *src = read_event_addr;
+ len /= 2;
+ if (len > read_event_size)
+ len = read_event_size;
+ while (len--)
+ *dst++ = *src++;
+}
+
+static int adsp_rtos_read_ctrl_word_cmd_tast_to_h_v(
+ struct adsp_info *info, void *dsp_addr)
+{
+ struct msm_adsp_module *module;
+ unsigned rtos_task_id;
+ unsigned msg_id;
+ unsigned msg_length;
+#ifdef CONFIG_DEBUG_FS
+ uint16_t *ptr;
+ int ii;
+#endif /* CONFIG_DEBUG_FS */
+ void (*func)(void *, size_t);
+
+ if (dsp_addr >= (void *)(MSM_AD5_BASE + QDSP_RAMC_OFFSET)) {
+ uint32_t *dsp_addr32 = dsp_addr;
+ uint32_t tmp = *dsp_addr32++;
+ rtos_task_id = (tmp & ADSP_RTOS_READ_CTRL_WORD_TASK_ID_M) >> 8;
+ msg_id = (tmp & ADSP_RTOS_READ_CTRL_WORD_MSG_ID_M);
+ read_event_size = tmp >> 16;
+ read_event_addr = dsp_addr32;
+ msg_length = read_event_size * sizeof(uint32_t);
+ func = read_event_32;
+ } else {
+ uint16_t *dsp_addr16 = dsp_addr;
+ uint16_t tmp = *dsp_addr16++;
+ rtos_task_id = (tmp & ADSP_RTOS_READ_CTRL_WORD_TASK_ID_M) >> 8;
+ msg_id = tmp & ADSP_RTOS_READ_CTRL_WORD_MSG_ID_M;
+ read_event_size = *dsp_addr16++;
+ read_event_addr = dsp_addr16;
+ msg_length = read_event_size * sizeof(uint16_t);
+ func = read_event_16;
+ }
+
+ if (rtos_task_id > info->max_task_id) {
+ MM_ERR("bogus task id %d\n", rtos_task_id);
+ return 0;
+ }
+ module = find_adsp_module_by_id(info,
+ adsp_get_module(info, rtos_task_id));
+
+ if (!module) {
+ MM_ERR("no module for task id %d\n", rtos_task_id);
+ return 0;
+ }
+
+ module->num_events++;
+
+ if (!module->ops) {
+ MM_ERR("module %s is not open\n", module->name);
+ return 0;
+ }
+#ifdef CONFIG_DEBUG_FS
+ if (rdump > 0) {
+ ptr = read_event_addr;
+ pr_info("D->A\n");
+ pr_info("m_id = %x id = %x\n", module->id, msg_id);
+ for (ii = 0; ii < msg_length/2; ii++)
+ pr_info("%x ", ptr[ii]);
+ pr_info("\n");
+ }
+#endif /* CONFIG_DEBUG_FS */
+
+ module->ops->event(module->driver_data, msg_id, msg_length, func);
+ return 0;
+}
+
+static int adsp_get_event(struct adsp_info *info)
+{
+ uint32_t ctrl_word;
+ uint32_t ready;
+ void *dsp_addr;
+ uint32_t cmd_type;
+ int cnt;
+ unsigned long flags;
+ int rc = 0;
+
+ spin_lock_irqsave(&adsp_cmd_lock, flags);
+
+ /* Whenever the DSP has a message, it updates this control word
+ * and generates an interrupt. When we receive the interrupt, we
+ * read this register to find out what ADSP task the command is
+ * comming from.
+ *
+ * The ADSP should *always* be ready on the first call, but the
+ * irq handler calls us in a loop (to handle back-to-back command
+ * processing), so we give the DSP some time to return to the
+ * ready state. The DSP will not issue another IRQ for events
+ * pending between the first IRQ and the event queue being drained,
+ * unfortunately.
+ */
+
+ for (cnt = 0; cnt < 50; cnt++) {
+ ctrl_word = readl(info->read_ctrl);
+
+ if ((ctrl_word & ADSP_RTOS_READ_CTRL_WORD_FLAG_M) ==
+ ADSP_RTOS_READ_CTRL_WORD_FLAG_UP_CONT_V)
+ goto ready;
+
+ udelay(2);
+ }
+ MM_ERR("not ready after 100uS\n");
+ rc = -EBUSY;
+ goto done;
+
+ready:
+ /* Here we check to see if there are pending messages. If there are
+ * none, we siply return -EAGAIN to indicate that there are no more
+ * messages pending.
+ */
+ ready = ctrl_word & ADSP_RTOS_READ_CTRL_WORD_READY_M;
+ if ((ready != ADSP_RTOS_READ_CTRL_WORD_READY_V) &&
+ (ready != ADSP_RTOS_READ_CTRL_WORD_CONT_V)) {
+ rc = -EAGAIN;
+ goto done;
+ }
+
+ /* DSP says that there are messages waiting for the host to read */
+
+ /* Get the Command Type */
+ cmd_type = ctrl_word & ADSP_RTOS_READ_CTRL_WORD_CMD_TYPE_M;
+
+ /* Get the DSP buffer address */
+ dsp_addr = (void *)((ctrl_word &
+ ADSP_RTOS_READ_CTRL_WORD_DSP_ADDR_M) +
+ (uint32_t)MSM_AD5_BASE);
+
+ /* We can only handle Task-to-Host messages */
+ if (cmd_type != ADSP_RTOS_READ_CTRL_WORD_CMD_TASK_TO_H_V) {
+ MM_ERR("unknown dsp cmd_type %d\n", cmd_type);
+ rc = -EIO;
+ goto done;
+ }
+
+ adsp_rtos_read_ctrl_word_cmd_tast_to_h_v(info, dsp_addr);
+
+ ctrl_word = readl(info->read_ctrl);
+ ctrl_word &= ~ADSP_RTOS_READ_CTRL_WORD_READY_M;
+
+ /* Write ctrl word to the DSP */
+ writel(ctrl_word, info->read_ctrl);
+
+ /* Generate an interrupt to the DSP */
+ writel(1, info->send_irq);
+
+done:
+ spin_unlock_irqrestore(&adsp_cmd_lock, flags);
+ return rc;
+}
+
+static irqreturn_t adsp_irq_handler(int irq, void *data)
+{
+ struct adsp_info *info = &adsp_info;
+ int cnt = 0;
+ for (cnt = 0; cnt < 15; cnt++)
+ if (adsp_get_event(info) < 0)
+ break;
+ if (cnt > info->event_backlog_max)
+ info->event_backlog_max = cnt;
+ info->events_received += cnt;
+ if (cnt == 15)
+ MM_ERR("too many (%d) events for single irq!\n", cnt);
+ return IRQ_HANDLED;
+}
+
+int adsp_set_clkrate(struct msm_adsp_module *module, unsigned long clk_rate)
+{
+ if (module->clk && clk_rate)
+ return clk_set_min_rate(module->clk, clk_rate);
+
+ return -EINVAL;
+}
+
+int msm_adsp_enable(struct msm_adsp_module *module)
+{
+ int rc = 0;
+
+ MM_INFO("enable '%s'state[%d] id[%d]\n",
+ module->name, module->state, module->id);
+
+ mutex_lock(&module->lock);
+ switch (module->state) {
+ case ADSP_STATE_DISABLED:
+ module->state = ADSP_STATE_ENABLING;
+ mutex_unlock(&module->lock);
+ rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_ENABLE,
+ module->id, module);
+ if (rc) {
+ mutex_lock(&module->lock);
+ module->state = ADSP_STATE_DISABLED;
+ break;
+ }
+ rc = wait_event_timeout(module->state_wait,
+ module->state != ADSP_STATE_ENABLING,
+ 1 * HZ);
+ mutex_lock(&module->lock);
+ if (module->state == ADSP_STATE_ENABLED) {
+ rc = 0;
+ } else {
+ MM_ERR("module '%s' enable timed out\n", module->name);
+ rc = -ETIMEDOUT;
+ }
+ if (module->open_count++ == 0 && module->clk)
+ clk_enable(module->clk);
+
+ mutex_lock(&adsp_open_lock);
+ if (adsp_open_count++ == 0)
+ enable_irq(INT_ADSP);
+ mutex_unlock(&adsp_open_lock);
+ break;
+ case ADSP_STATE_ENABLING:
+ MM_DBG("module '%s' enable in progress\n", module->name);
+ break;
+ case ADSP_STATE_ENABLED:
+ MM_DBG("module '%s' already enabled\n", module->name);
+ break;
+ case ADSP_STATE_DISABLING:
+ MM_ERR("module '%s' disable in progress\n", module->name);
+ rc = -EBUSY;
+ break;
+ }
+ mutex_unlock(&module->lock);
+ return rc;
+}
+EXPORT_SYMBOL(msm_adsp_enable);
+
+int msm_adsp_disable_event_rsp(struct msm_adsp_module *module)
+{
+ int rc = 0;
+
+ mutex_lock(&module->lock);
+
+ rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_DISABLE_EVENT_RSP,
+ module->id, module);
+ mutex_unlock(&module->lock);
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_adsp_disable_event_rsp);
+
+int msm_adsp_disable(struct msm_adsp_module *module)
+{
+ int rc = 0;
+
+ mutex_lock(&module->lock);
+ switch (module->state) {
+ case ADSP_STATE_DISABLED:
+ MM_DBG("module '%s' already disabled\n", module->name);
+ mutex_unlock(&module->lock);
+ break;
+ case ADSP_STATE_ENABLING:
+ case ADSP_STATE_ENABLED:
+ mutex_unlock(&module->lock);
+ rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_DISABLE,
+ module->id, module);
+ mutex_lock(&module->lock);
+ module->state = ADSP_STATE_DISABLED;
+ if (--module->open_count == 0 && module->clk)
+ clk_disable(module->clk);
+ mutex_unlock(&module->lock);
+ mutex_lock(&adsp_open_lock);
+ if (--adsp_open_count == 0) {
+ disable_irq(INT_ADSP);
+ MM_INFO("disable interrupt\n");
+ }
+ mutex_unlock(&adsp_open_lock);
+ break;
+ }
+ return rc;
+}
+EXPORT_SYMBOL(msm_adsp_disable);
+
+static int msm_adsp_probe(struct platform_device *pdev)
+{
+ unsigned count;
+ int rc, i;
+
+ adsp_info.init_info_ptr = kzalloc(
+ (sizeof(struct adsp_rtos_mp_mtoa_init_info_type)), GFP_KERNEL);
+ if (!adsp_info.init_info_ptr)
+ return -ENOMEM;
+
+ adsp_info.raw_event = kzalloc(
+ (sizeof(struct adsp_rtos_mp_mtoa_s_type)), GFP_KERNEL);
+ if (!adsp_info.raw_event) {
+ kfree(adsp_info.init_info_ptr);
+ return -ENOMEM;
+ }
+
+ rc = adsp_init_info(&adsp_info);
+ if (rc) {
+ kfree(adsp_info.init_info_ptr);
+ kfree(adsp_info.raw_event);
+ return rc;
+ }
+ adsp_info.send_irq += (uint32_t) MSM_AD5_BASE;
+ adsp_info.read_ctrl += (uint32_t) MSM_AD5_BASE;
+ adsp_info.write_ctrl += (uint32_t) MSM_AD5_BASE;
+ count = adsp_info.module_count;
+
+ adsp_modules = kzalloc(
+ (sizeof(struct msm_adsp_module) + sizeof(void *)) *
+ count, GFP_KERNEL);
+ if (!adsp_modules) {
+ kfree(adsp_info.init_info_ptr);
+ kfree(adsp_info.raw_event);
+ return -ENOMEM;
+ }
+
+ adsp_info.id_to_module = (void *) (adsp_modules + count);
+
+ spin_lock_init(&adsp_cmd_lock);
+ spin_lock_init(&adsp_write_lock);
+
+ rc = request_irq(INT_ADSP, adsp_irq_handler,
+ IRQF_TRIGGER_RISING, "adsp", 0);
+ if (rc < 0)
+ goto fail_request_irq;
+ disable_irq(INT_ADSP);
+
+ for (i = 0; i < count; i++) {
+ struct msm_adsp_module *mod = adsp_modules + i;
+ mutex_init(&mod->lock);
+ init_waitqueue_head(&mod->state_wait);
+ mod->info = &adsp_info;
+ mod->name = adsp_info.module[i].name;
+ mod->id = adsp_info.module[i].id;
+ if (adsp_info.module[i].clk_name)
+ mod->clk = clk_get(NULL, adsp_info.module[i].clk_name);
+ else
+ mod->clk = NULL;
+ if (mod->clk && adsp_info.module[i].clk_rate)
+ clk_set_min_rate(mod->clk,
+ adsp_info.module[i].clk_rate);
+ mod->verify_cmd = adsp_info.module[i].verify_cmd;
+ mod->patch_event = adsp_info.module[i].patch_event;
+ INIT_HLIST_HEAD(&mod->pmem_regions);
+ mod->pdev.name = adsp_info.module[i].pdev_name;
+ mod->pdev.id = -1;
+ adsp_info.id_to_module[i] = mod;
+ platform_device_register(&mod->pdev);
+ }
+
+ msm_adsp_publish_cdevs(adsp_modules, count);
+
+ rc = daldevice_attach(DALRPC_ADSPSVC_DEVICEID, DALRPC_ADSPSVC_PORT,
+ DALRPC_ADSPSVC_DEST, &adsp_info.handle);
+ if (rc) {
+ MM_ERR("adsp attach failed : %d\n", rc);
+ goto fail_dal_attach;
+ }
+
+ adsp_info.cb_handle = dalrpc_alloc_cb(adsp_info.handle,
+ adsp_rtos_mtoa_cb, NULL);
+ if (adsp_info.cb_handle == NULL) {
+ MM_ERR("Callback registration failed\n");
+ goto fail_allocate_cb;
+ }
+
+ /* Get INIT_INFO */
+ init_waitqueue_head(&adsp_info.init_info_wait);
+ msm_get_init_info();
+ rc = wait_event_timeout(adsp_info.init_info_wait,
+ adsp_info.init_info_state == ADSP_STATE_INIT_INFO,
+ 10 * HZ);
+ if (!rc) {
+ MM_ERR("INIT_INFO failed\n");
+ rc = -ETIMEDOUT;
+ } else
+ return 0;
+
+fail_allocate_cb:
+ daldevice_detach(adsp_info.handle);
+ adsp_info.handle = NULL;
+fail_dal_attach:
+ enable_irq(INT_ADSP);
+ free_irq(INT_ADSP, 0);
+fail_request_irq:
+ kfree(adsp_modules);
+ kfree(adsp_info.init_info_ptr);
+ kfree(adsp_info.raw_event);
+ return rc;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int get_parameters(char *buf, long int *param1, int num_of_par)
+{
+ char *token;
+ int base, cnt;
+
+ token = strsep(&buf, " ");
+
+ for (cnt = 0; cnt < num_of_par; cnt++) {
+ if (token != NULL) {
+ if ((token[1] == 'x') || (token[1] == 'X'))
+ base = 16;
+ else
+ base = 10;
+
+ if (strict_strtoul(token, base, ¶m1[cnt]) != 0)
+ return -EINVAL;
+
+ token = strsep(&buf, " ");
+ }
+ else
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static ssize_t adsp_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ pr_debug("adsp debugfs opened\n");
+ return 0;
+}
+static ssize_t adsp_debug_write(struct file *file, const char __user *buf,
+ size_t cnt, loff_t *ppos)
+{
+ char *access_str = file->private_data;
+ char lbuf[32];
+ int rc;
+ long int param[5];
+
+ if (cnt > sizeof(lbuf) - 1)
+ return -EINVAL;
+ rc = copy_from_user(lbuf, buf, cnt);
+ if (rc) {
+ pr_info("Unable to copy data from user space\n");
+ return -EFAULT;
+ }
+ lbuf[cnt] = '\0';
+
+ if (!strncmp(access_str, "write_log", 9)) {
+ if (get_parameters(lbuf, param, 1) == 0) {
+ switch (param[0]) {
+ case 1:
+ if (wdump <= 0)
+ wdump = 1;
+ pr_debug("write cmd to DSP(A->D) dump \
+ started:%d\n", wdump);
+ break;
+ case 0:
+ if (wdump > 0)
+ wdump = 0;
+ pr_debug("Stop write cmd to \
+ DSP(A->D):%d\n", wdump);
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ } else
+ rc = -EINVAL;
+ } else if (!strncmp(access_str, "read_log", 8)) {
+ if (get_parameters(lbuf, param, 1) == 0) {
+ switch (param[0]) {
+ case 1:
+ if (rdump <= 0)
+ rdump = 1;
+ pr_debug("write cmd from DSP(D->A) dump \
+ started:%d\n", wdump);
+ break;
+ case 0:
+ if (rdump > 0)
+ rdump = 0;
+ pr_debug("Stop write cmd from \
+ DSP(D->A):%d\n", wdump);
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ } else
+ rc = -EINVAL;
+ } else {
+ rc = -EINVAL;
+ }
+ if (rc == 0)
+ rc = cnt;
+ else {
+ pr_err("%s: rc = %d\n", __func__, rc);
+ pr_info("\nWrong command: Use =>\n");
+ pr_info("-------------------------\n");
+ pr_info("To Start A->D:: echo \"1\">/sys/kernel/debug/ \
+ adsp_cmd/write_log\n");
+ pr_info("To Start D->A:: echo \"1\">/sys/kernel/debug/ \
+ adsp_cmd/read_log\n");
+ pr_info("To Stop A->D:: echo \"0\">/sys/kernel/debug/ \
+ adsp_cmd/write_log\n");
+ pr_info("To Stop D->A:: echo \"0\">/sys/kernel/debug/ \
+ adsp_cmd/read_log\n");
+ pr_info("------------------------\n");
+ }
+
+ return rc;
+}
+#endif
+
+static struct platform_driver msm_adsp_driver = {
+ .probe = msm_adsp_probe,
+ .driver = {
+ .owner = THIS_MODULE,
+ },
+};
+
+struct platform_device msm_adsp_device = {
+ .name = "msm_adsp",
+ .id = -1,
+};
+
+static char msm_adsp_driver_name[] = "msm_adsp";
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations adsp_debug_fops = {
+ .write = adsp_debug_write,
+ .open = adsp_debug_open,
+};
+#endif
+
+static int __init adsp_init(void)
+{
+ int rc;
+
+#ifdef CONFIG_DEBUG_FS
+ dentry_adsp = debugfs_create_dir("adsp_cmd", 0);
+ if (!IS_ERR(dentry_adsp)) {
+ dentry_wdata = debugfs_create_file("write_log", \
+ S_IFREG | S_IRUGO, dentry_adsp,
+ (void *) "write_log" , &adsp_debug_fops);
+ dentry_rdata = debugfs_create_file("read_log", \
+ S_IFREG | S_IRUGO, dentry_adsp,
+ (void *) "read_log", &adsp_debug_fops);
+ }
+#endif /* CONFIG_DEBUG_FS */
+
+ msm_adsp_driver.driver.name = msm_adsp_driver_name;
+ rc = platform_device_register(&msm_adsp_device);
+ rc = platform_driver_register(&msm_adsp_driver);
+ MM_INFO("%s -- %d\n", msm_adsp_driver_name, rc);
+ return rc;
+}
+
+device_initcall(adsp_init);
diff --git a/arch/arm/mach-msm/qdsp5v2/adsp.h b/arch/arm/mach-msm/qdsp5v2/adsp.h
new file mode 100644
index 0000000..18f4046
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/adsp.h
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ * Author: Iliyan Malchev <ibm@android.com>
+ *
+ * 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.
+ *
+ */
+
+#ifndef _ARCH_ARM_MACH_MSM_ADSP_H
+#define _ARCH_ARM_MACH_MSM_ADSP_H
+
+#include <linux/types.h>
+#include <linux/msm_adsp.h>
+#include <linux/platform_device.h>
+#include <mach/msm_adsp.h>
+#include <mach/dal.h>
+
+int adsp_pmem_fixup(struct msm_adsp_module *module, void **addr,
+ unsigned long len);
+int adsp_pmem_fixup_kvaddr(struct msm_adsp_module *module, void **addr,
+ unsigned long *kvaddr, unsigned long len);
+int adsp_pmem_paddr_fixup(struct msm_adsp_module *module, void **addr);
+
+int adsp_vfe_verify_cmd(struct msm_adsp_module *module,
+ unsigned int queue_id, void *cmd_data,
+ size_t cmd_size);
+int adsp_jpeg_verify_cmd(struct msm_adsp_module *module,
+ unsigned int queue_id, void *cmd_data,
+ size_t cmd_size);
+int adsp_lpm_verify_cmd(struct msm_adsp_module *module,
+ unsigned int queue_id, void *cmd_data,
+ size_t cmd_size);
+int adsp_video_verify_cmd(struct msm_adsp_module *module,
+ unsigned int queue_id, void *cmd_data,
+ size_t cmd_size);
+int adsp_videoenc_verify_cmd(struct msm_adsp_module *module,
+ unsigned int queue_id, void *cmd_data,
+ size_t cmd_size);
+
+
+struct adsp_event;
+
+int adsp_vfe_patch_event(struct msm_adsp_module *module,
+ struct adsp_event *event);
+
+int adsp_jpeg_patch_event(struct msm_adsp_module *module,
+ struct adsp_event *event);
+
+
+struct adsp_module_info {
+ const char *name;
+ const char *pdev_name;
+ uint32_t id;
+ const char *clk_name;
+ unsigned long clk_rate;
+ int (*verify_cmd) (struct msm_adsp_module*, unsigned int, void *,
+ size_t);
+ int (*patch_event) (struct msm_adsp_module*, struct adsp_event *);
+};
+
+#define ADSP_EVENT_MAX_SIZE 496
+#define EVENT_LEN 12
+#define EVENT_MSG_ID ((uint16_t)~0)
+
+struct adsp_event {
+ struct list_head list;
+ uint32_t size; /* always in bytes */
+ uint16_t msg_id;
+ uint16_t type; /* 0 for msgs (from aDSP), -1 for events (from ARM9) */
+ int is16; /* always 0 (msg is 32-bit) when the event type is 1(ARM9) */
+ union {
+ uint16_t msg16[ADSP_EVENT_MAX_SIZE / 2];
+ uint32_t msg32[ADSP_EVENT_MAX_SIZE / 4];
+ } data;
+};
+
+#define DALRPC_ADSPSVC_DEVICEID 0x0200009A
+#define DALRPC_ADSPSVC_DEST SMD_APPS_MODEM
+#define DALRPC_ADSPSVC_PORT "DAL00"
+
+enum {
+ DALDEVICE_ADSP_CMD_IDX = DALDEVICE_FIRST_DEVICE_API_IDX,
+};
+
+struct adsp_rtos_atom_cmd {
+ uint32_t cmd;
+ uint32_t proc_id;
+ uint32_t module;
+ void *cb_handle;
+};
+
+enum rpc_adsp_rtos_proc_type {
+ RPC_ADSP_RTOS_PROC_NONE = 0,
+ RPC_ADSP_RTOS_PROC_MODEM = 1,
+ RPC_ADSP_RTOS_PROC_APPS = 2,
+};
+
+enum {
+ RPC_ADSP_RTOS_CMD_REGISTER_APP,
+ RPC_ADSP_RTOS_CMD_ENABLE,
+ RPC_ADSP_RTOS_CMD_DISABLE,
+ RPC_ADSP_RTOS_CMD_KERNEL_COMMAND,
+ RPC_ADSP_RTOS_CMD_16_COMMAND,
+ RPC_ADSP_RTOS_CMD_32_COMMAND,
+ RPC_ADSP_RTOS_CMD_DISABLE_EVENT_RSP,
+ RPC_ADSP_RTOS_CMD_REMOTE_EVENT,
+ RPC_ADSP_RTOS_CMD_SET_STATE,
+ RPC_ADSP_RTOS_CMD_REMOTE_INIT_INFO_EVENT,
+ RPC_ADSP_RTOS_CMD_GET_INIT_INFO,
+};
+
+enum rpc_adsp_rtos_mod_status_type {
+ RPC_ADSP_RTOS_MOD_READY,
+ RPC_ADSP_RTOS_MOD_DISABLE,
+ RPC_ADSP_RTOS_SERVICE_RESET,
+ RPC_ADSP_RTOS_CMD_FAIL,
+ RPC_ADSP_RTOS_CMD_SUCCESS,
+ RPC_ADSP_RTOS_INIT_INFO,
+ RPC_ADSP_RTOS_DISABLE_FAIL,
+};
+
+enum qdsp_image_type {
+ QDSP_IMAGE_COMBO,
+ QDSP_IMAGE_GAUDIO,
+ QDSP_IMAGE_QTV_LP,
+ QDSP_IMAGE_MAX,
+ /* DO NOT USE: Force this enum to be a 32bit type to improve speed */
+ QDSP_IMAGE_32BIT_DUMMY = 0x10000
+};
+
+struct adsp_rtos_mp_mtoa_header_type {
+ enum rpc_adsp_rtos_mod_status_type event;
+ uint32_t version;
+ enum rpc_adsp_rtos_proc_type proc_id;
+};
+
+/* ADSP RTOS MP Communications - Modem to APP's Event Info*/
+struct adsp_rtos_mp_mtoa_type {
+ uint32_t module;
+ uint32_t image;
+ uint32_t apps_okts;
+};
+
+/* ADSP RTOS MP Communications - Modem to APP's Init Info */
+#define IMG_MAX 2
+#define ENTRIES_MAX 36
+#define MODULES_MAX 64
+#define QUEUES_MAX 64
+
+struct queue_to_offset_type {
+ uint32_t queue;
+ uint32_t offset;
+};
+
+struct mod_to_queue_offsets {
+ uint32_t module;
+ uint32_t q_type;
+ uint32_t q_max_len;
+};
+
+struct adsp_rtos_mp_mtoa_init_info_type {
+ uint32_t image_count;
+ uint32_t num_queue_offsets;
+ struct queue_to_offset_type queue_offsets_tbl[IMG_MAX][ENTRIES_MAX];
+ uint32_t num_task_module_entries;
+ uint32_t task_to_module_tbl[IMG_MAX][ENTRIES_MAX];
+
+ uint32_t module_table_size;
+ uint32_t module_entries[MODULES_MAX];
+ uint32_t mod_to_q_entries;
+ struct mod_to_queue_offsets mod_to_q_tbl[ENTRIES_MAX];
+ /*
+ * queue_offsets[] is to store only queue_offsets
+ */
+ uint32_t queue_offsets[IMG_MAX][QUEUES_MAX];
+};
+
+struct adsp_rtos_mp_mtoa_s_type {
+ struct adsp_rtos_mp_mtoa_header_type mp_mtoa_header;
+
+ union {
+ struct adsp_rtos_mp_mtoa_init_info_type mp_mtoa_init_packet;
+ struct adsp_rtos_mp_mtoa_type mp_mtoa_packet;
+ } adsp_rtos_mp_mtoa_data;
+};
+
+struct adsp_info {
+ uint32_t send_irq;
+ uint32_t read_ctrl;
+ uint32_t write_ctrl;
+
+ uint32_t max_msg16_size;
+ uint32_t max_msg32_size;
+
+ uint32_t max_task_id;
+ uint32_t max_module_id;
+ uint32_t max_queue_id;
+ uint32_t max_image_id;
+
+ /* for each image id, a map of queue id to offset */
+ uint32_t **queue_offset;
+
+ /* for each image id, a map of task id to module id */
+ uint32_t **task_to_module;
+
+ /* for each module id, map of module id to module */
+ struct msm_adsp_module **id_to_module;
+
+ uint32_t module_count;
+ struct adsp_module_info *module;
+
+ /* stats */
+ uint32_t events_received;
+ uint32_t event_backlog_max;
+
+ /* rpc_client for init_info */
+ struct adsp_rtos_mp_mtoa_init_info_type *init_info_ptr;
+ struct adsp_rtos_mp_mtoa_s_type *raw_event;
+ wait_queue_head_t init_info_wait;
+ unsigned init_info_state;
+
+ void *handle;
+ void *cb_handle;
+};
+
+#define ADSP_STATE_DISABLED 0
+#define ADSP_STATE_ENABLING 1
+#define ADSP_STATE_ENABLED 2
+#define ADSP_STATE_DISABLING 3
+#define ADSP_STATE_INIT_INFO 4
+
+struct msm_adsp_module {
+ struct mutex lock;
+ const char *name;
+ unsigned id;
+ struct adsp_info *info;
+
+ struct msm_adsp_ops *ops;
+ void *driver_data;
+
+ /* statistics */
+ unsigned num_commands;
+ unsigned num_events;
+
+ wait_queue_head_t state_wait;
+ unsigned state;
+
+ struct platform_device pdev;
+ struct clk *clk;
+ int open_count;
+
+ struct mutex pmem_regions_lock;
+ struct hlist_head pmem_regions;
+ int (*verify_cmd) (struct msm_adsp_module*, unsigned int, void *,
+ size_t);
+ int (*patch_event) (struct msm_adsp_module*, struct adsp_event *);
+};
+
+extern void msm_adsp_publish_cdevs(struct msm_adsp_module *, unsigned);
+extern int adsp_init_info(struct adsp_info *info);
+
+/* Value to indicate that a queue is not defined for a particular image */
+#define QDSP_RTOS_NO_QUEUE 0xfffffffe
+
+/*
+ * Constants used to communicate with the ADSP RTOS
+ */
+#define ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M 0x80000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V 0x80000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_AVAIL_V 0x00000000U
+
+#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_M 0x70000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_WRITE_REQ_V 0x00000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_WRITE_DONE_V 0x10000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_NO_CMD_V 0x70000000U
+
+#define ADSP_RTOS_WRITE_CTRL_WORD_STATUS_M 0x0E000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_NO_ERR_V 0x00000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_NO_FREE_BUF_V 0x02000000U
+
+#define ADSP_RTOS_WRITE_CTRL_WORD_KERNEL_FLG_M 0x01000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_HTOD_MSG_WRITE_V 0x00000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_HTOD_CMD_V 0x01000000U
+
+#define ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M 0x00FFFFFFU
+#define ADSP_RTOS_WRITE_CTRL_WORD_HTOD_CMD_ID_M 0x00FFFFFFU
+
+/* Combination of MUTEX and CMD bits to check if the DSP is busy */
+#define ADSP_RTOS_WRITE_CTRL_WORD_READY_M 0xF0000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_READY_V 0x70000000U
+
+/* RTOS to Host processor command mask values */
+#define ADSP_RTOS_READ_CTRL_WORD_FLAG_M 0x80000000U
+#define ADSP_RTOS_READ_CTRL_WORD_FLAG_UP_WAIT_V 0x00000000U
+#define ADSP_RTOS_READ_CTRL_WORD_FLAG_UP_CONT_V 0x80000000U
+
+#define ADSP_RTOS_READ_CTRL_WORD_CMD_M 0x60000000U
+#define ADSP_RTOS_READ_CTRL_WORD_READ_DONE_V 0x00000000U
+#define ADSP_RTOS_READ_CTRL_WORD_READ_REQ_V 0x20000000U
+#define ADSP_RTOS_READ_CTRL_WORD_NO_CMD_V 0x60000000U
+
+/* Combination of FLAG and COMMAND bits to check if MSG ready */
+#define ADSP_RTOS_READ_CTRL_WORD_READY_M 0xE0000000U
+#define ADSP_RTOS_READ_CTRL_WORD_READY_V 0xA0000000U
+#define ADSP_RTOS_READ_CTRL_WORD_CONT_V 0xC0000000U
+#define ADSP_RTOS_READ_CTRL_WORD_DONE_V 0xE0000000U
+
+#define ADSP_RTOS_READ_CTRL_WORD_STATUS_M 0x18000000U
+#define ADSP_RTOS_READ_CTRL_WORD_NO_ERR_V 0x00000000U
+
+#define ADSP_RTOS_READ_CTRL_WORD_IN_PROG_M 0x04000000U
+#define ADSP_RTOS_READ_CTRL_WORD_NO_READ_IN_PROG_V 0x00000000U
+#define ADSP_RTOS_READ_CTRL_WORD_READ_IN_PROG_V 0x04000000U
+
+#define ADSP_RTOS_READ_CTRL_WORD_CMD_TYPE_M 0x03000000U
+#define ADSP_RTOS_READ_CTRL_WORD_CMD_TASK_TO_H_V 0x00000000U
+#define ADSP_RTOS_READ_CTRL_WORD_CMD_KRNL_TO_H_V 0x01000000U
+#define ADSP_RTOS_READ_CTRL_WORD_CMD_H_TO_KRNL_CFM_V 0x02000000U
+
+#define ADSP_RTOS_READ_CTRL_WORD_DSP_ADDR_M 0x00FFFFFFU
+
+#define ADSP_RTOS_READ_CTRL_WORD_MSG_ID_M 0x000000FFU
+#define ADSP_RTOS_READ_CTRL_WORD_TASK_ID_M 0x0000FF00U
+
+/* Base address of DSP and DSP hardware registers */
+#define QDSP_RAMC_OFFSET 0x400000
+
+#endif
diff --git a/arch/arm/mach-msm/qdsp5v2/adsp_driver.c b/arch/arm/mach-msm/qdsp5v2/adsp_driver.c
new file mode 100644
index 0000000..28f9dd6
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/adsp_driver.c
@@ -0,0 +1,655 @@
+/*
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ * Author: Iliyan Malchev <ibm@android.com>
+ *
+ * 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.
+ *
+ */
+
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include <linux/msm_adsp.h>
+#include <linux/android_pmem.h>
+#include "adsp.h"
+#include <mach/debug_mm.h>
+#include <linux/slab.h>
+
+struct adsp_pmem_info {
+ int fd;
+ void *vaddr;
+};
+
+struct adsp_pmem_region {
+ struct hlist_node list;
+ void *vaddr;
+ unsigned long paddr;
+ unsigned long kvaddr;
+ unsigned long len;
+ struct file *file;
+};
+
+struct adsp_device {
+ struct msm_adsp_module *module;
+
+ spinlock_t event_queue_lock;
+ wait_queue_head_t event_wait;
+ struct list_head event_queue;
+ int abort;
+
+ const char *name;
+ struct device *device;
+ struct cdev cdev;
+};
+
+static struct adsp_device *inode_to_device(struct inode *inode);
+
+#define __CONTAINS(r, v, l) ({ \
+ typeof(r) __r = r; \
+ typeof(v) __v = v; \
+ typeof(v) __e = __v + l; \
+ int res = __v >= __r->vaddr && \
+ __e <= __r->vaddr + __r->len; \
+ res; \
+})
+
+#define CONTAINS(r1, r2) ({ \
+ typeof(r2) __r2 = r2; \
+ __CONTAINS(r1, __r2->vaddr, __r2->len); \
+})
+
+#define IN_RANGE(r, v) ({ \
+ typeof(r) __r = r; \
+ typeof(v) __vv = v; \
+ int res = ((__vv >= __r->vaddr) && \
+ (__vv < (__r->vaddr + __r->len))); \
+ res; \
+})
+
+#define OVERLAPS(r1, r2) ({ \
+ typeof(r1) __r1 = r1; \
+ typeof(r2) __r2 = r2; \
+ typeof(__r2->vaddr) __v = __r2->vaddr; \
+ typeof(__v) __e = __v + __r2->len - 1; \
+ int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \
+ res; \
+})
+
+static int adsp_pmem_check(struct msm_adsp_module *module,
+ void *vaddr, unsigned long len)
+{
+ struct adsp_pmem_region *region_elt;
+ struct hlist_node *node;
+ struct adsp_pmem_region t = { .vaddr = vaddr, .len = len };
+
+ hlist_for_each_entry(region_elt, node, &module->pmem_regions, list) {
+ if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) ||
+ OVERLAPS(region_elt, &t)) {
+ MM_ERR("module %s:"
+ " region (vaddr %p len %ld)"
+ " clashes with registered region"
+ " (vaddr %p paddr %p len %ld)\n",
+ module->name,
+ vaddr, len,
+ region_elt->vaddr,
+ (void *)region_elt->paddr,
+ region_elt->len);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int adsp_pmem_add(struct msm_adsp_module *module,
+ struct adsp_pmem_info *info)
+{
+ unsigned long paddr, kvaddr, len;
+ struct file *file;
+ struct adsp_pmem_region *region;
+ int rc = -EINVAL;
+
+ mutex_lock(&module->pmem_regions_lock);
+ region = kmalloc(sizeof(*region), GFP_KERNEL);
+ if (!region) {
+ rc = -ENOMEM;
+ goto end;
+ }
+ INIT_HLIST_NODE(®ion->list);
+ if (get_pmem_file(info->fd, &paddr, &kvaddr, &len, &file)) {
+ kfree(region);
+ goto end;
+ }
+
+ rc = adsp_pmem_check(module, info->vaddr, len);
+ if (rc < 0) {
+ put_pmem_file(file);
+ kfree(region);
+ goto end;
+ }
+
+ region->vaddr = info->vaddr;
+ region->paddr = paddr;
+ region->kvaddr = kvaddr;
+ region->len = len;
+ region->file = file;
+
+ hlist_add_head(®ion->list, &module->pmem_regions);
+end:
+ mutex_unlock(&module->pmem_regions_lock);
+ return rc;
+}
+
+static int adsp_pmem_lookup_vaddr(struct msm_adsp_module *module, void **addr,
+ unsigned long len, struct adsp_pmem_region **region)
+{
+ struct hlist_node *node;
+ void *vaddr = *addr;
+ struct adsp_pmem_region *region_elt;
+
+ int match_count = 0;
+
+ *region = NULL;
+
+ /* returns physical address or zero */
+ hlist_for_each_entry(region_elt, node, &module->pmem_regions, list) {
+ if (vaddr >= region_elt->vaddr &&
+ vaddr < region_elt->vaddr + region_elt->len &&
+ vaddr + len <= region_elt->vaddr + region_elt->len) {
+ /* offset since we could pass vaddr inside a registerd
+ * pmem buffer
+ */
+
+ match_count++;
+ if (!*region)
+ *region = region_elt;
+ }
+ }
+
+ if (match_count > 1) {
+ MM_ERR("module %s: "
+ "multiple hits for vaddr %p, len %ld\n",
+ module->name, vaddr, len);
+ hlist_for_each_entry(region_elt, node,
+ &module->pmem_regions, list) {
+ if (vaddr >= region_elt->vaddr &&
+ vaddr < region_elt->vaddr + region_elt->len &&
+ vaddr + len <= region_elt->vaddr + region_elt->len)
+ MM_ERR("%p, %ld --> %p\n",
+ region_elt->vaddr,
+ region_elt->len,
+ (void *)region_elt->paddr);
+ }
+ }
+
+ return *region ? 0 : -1;
+}
+
+int adsp_pmem_fixup_kvaddr(struct msm_adsp_module *module, void **addr,
+ unsigned long *kvaddr, unsigned long len)
+{
+ struct adsp_pmem_region *region;
+ void *vaddr = *addr;
+ unsigned long *paddr = (unsigned long *)addr;
+ int ret;
+
+ ret = adsp_pmem_lookup_vaddr(module, addr, len, ®ion);
+ if (ret) {
+ MM_ERR("not patching %s (paddr & kvaddr),"
+ " lookup (%p, %ld) failed\n",
+ module->name, vaddr, len);
+ return ret;
+ }
+ *paddr = region->paddr + (vaddr - region->vaddr);
+ *kvaddr = region->kvaddr + (vaddr - region->vaddr);
+ return 0;
+}
+
+int adsp_pmem_fixup(struct msm_adsp_module *module, void **addr,
+ unsigned long len)
+{
+ struct adsp_pmem_region *region;
+ void *vaddr = *addr;
+ unsigned long *paddr = (unsigned long *)addr;
+ int ret;
+
+ ret = adsp_pmem_lookup_vaddr(module, addr, len, ®ion);
+ if (ret) {
+ MM_ERR("not patching %s, lookup (%p, %ld) failed\n",
+ module->name, vaddr, len);
+ return ret;
+ }
+
+ *paddr = region->paddr + (vaddr - region->vaddr);
+ return 0;
+}
+
+static int adsp_verify_cmd(struct msm_adsp_module *module,
+ unsigned int queue_id, void *cmd_data,
+ size_t cmd_size)
+{
+ /* call the per module verifier */
+ if (module->verify_cmd)
+ return module->verify_cmd(module, queue_id, cmd_data,
+ cmd_size);
+ else
+ MM_INFO("no packet verifying function "
+ "for task %s\n", module->name);
+ return 0;
+}
+
+static long adsp_write_cmd(struct adsp_device *adev, void __user *arg)
+{
+ struct adsp_command_t cmd;
+ unsigned char buf[256];
+ void *cmd_data;
+ long rc;
+
+ if (copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)))
+ return -EFAULT;
+
+ if (cmd.len > 256) {
+ cmd_data = kmalloc(cmd.len, GFP_USER);
+ if (!cmd_data)
+ return -ENOMEM;
+ } else {
+ cmd_data = buf;
+ }
+
+ if (copy_from_user(cmd_data, (void __user *)(cmd.data), cmd.len)) {
+ rc = -EFAULT;
+ goto end;
+ }
+
+ mutex_lock(&adev->module->pmem_regions_lock);
+ if (adsp_verify_cmd(adev->module, cmd.queue, cmd_data, cmd.len)) {
+ MM_ERR("module %s: verify failed.\n", adev->module->name);
+ rc = -EINVAL;
+ goto end;
+ }
+ rc = msm_adsp_write(adev->module, cmd.queue, cmd_data, cmd.len);
+end:
+ mutex_unlock(&adev->module->pmem_regions_lock);
+
+ if (cmd.len > 256)
+ kfree(cmd_data);
+
+ return rc;
+}
+
+static int adsp_events_pending(struct adsp_device *adev)
+{
+ unsigned long flags;
+ int yes;
+ spin_lock_irqsave(&adev->event_queue_lock, flags);
+ yes = !list_empty(&adev->event_queue);
+ spin_unlock_irqrestore(&adev->event_queue_lock, flags);
+ return yes || adev->abort;
+}
+
+static int adsp_pmem_lookup_paddr(struct msm_adsp_module *module, void **addr,
+ struct adsp_pmem_region **region)
+{
+ struct hlist_node *node;
+ unsigned long paddr = (unsigned long)(*addr);
+ struct adsp_pmem_region *region_elt;
+
+ hlist_for_each_entry(region_elt, node, &module->pmem_regions, list) {
+ if (paddr >= region_elt->paddr &&
+ paddr < region_elt->paddr + region_elt->len) {
+ *region = region_elt;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int adsp_pmem_paddr_fixup(struct msm_adsp_module *module, void **addr)
+{
+ struct adsp_pmem_region *region;
+ unsigned long paddr = (unsigned long)(*addr);
+ unsigned long *vaddr = (unsigned long *)addr;
+ int ret;
+
+ ret = adsp_pmem_lookup_paddr(module, addr, ®ion);
+ if (ret) {
+ MM_ERR("not patching %s, paddr %p lookup failed\n",
+ module->name, vaddr);
+ return ret;
+ }
+
+ *vaddr = (unsigned long)region->vaddr + (paddr - region->paddr);
+ return 0;
+}
+
+static int adsp_patch_event(struct msm_adsp_module *module,
+ struct adsp_event *event)
+{
+ /* call the per-module msg verifier */
+ if (module->patch_event)
+ return module->patch_event(module, event);
+ return 0;
+}
+
+static long adsp_get_event(struct adsp_device *adev, void __user *arg)
+{
+ unsigned long flags;
+ struct adsp_event *data = NULL;
+ struct adsp_event_t evt;
+ int timeout;
+ long rc = 0;
+
+ if (copy_from_user(&evt, arg, sizeof(struct adsp_event_t)))
+ return -EFAULT;
+
+ timeout = (int)evt.timeout_ms;
+
+ if (timeout > 0) {
+ rc = wait_event_interruptible_timeout(
+ adev->event_wait, adsp_events_pending(adev),
+ msecs_to_jiffies(timeout));
+ if (rc == 0)
+ return -ETIMEDOUT;
+ } else {
+ rc = wait_event_interruptible(
+ adev->event_wait, adsp_events_pending(adev));
+ }
+ if (rc < 0)
+ return rc;
+
+ if (adev->abort)
+ return -ENODEV;
+
+ spin_lock_irqsave(&adev->event_queue_lock, flags);
+ if (!list_empty(&adev->event_queue)) {
+ data = list_first_entry(&adev->event_queue,
+ struct adsp_event, list);
+ list_del(&data->list);
+ }
+ spin_unlock_irqrestore(&adev->event_queue_lock, flags);
+
+ if (!data)
+ return -EAGAIN;
+
+ /* DSP messages are type 0; they may contain physical addresses */
+ if (data->type == 0)
+ adsp_patch_event(adev->module, data);
+
+ /* map adsp_event --> adsp_event_t */
+ if (evt.len < data->size) {
+ rc = -ETOOSMALL;
+ goto end;
+ }
+ if (data->msg_id != EVENT_MSG_ID) {
+ if (copy_to_user((void *)(evt.data), data->data.msg16,
+ data->size)) {
+ rc = -EFAULT;
+ goto end;
+ }
+ } else {
+ if (copy_to_user((void *)(evt.data), data->data.msg32,
+ data->size)) {
+ rc = -EFAULT;
+ goto end;
+ }
+ }
+
+ evt.type = data->type; /* 0 --> from aDSP, 1 --> from ARM9 */
+ evt.msg_id = data->msg_id;
+ evt.flags = data->is16;
+ evt.len = data->size;
+ if (copy_to_user(arg, &evt, sizeof(evt)))
+ rc = -EFAULT;
+end:
+ kfree(data);
+ return rc;
+}
+
+static int adsp_pmem_del(struct msm_adsp_module *module)
+{
+ struct hlist_node *node, *tmp;
+ struct adsp_pmem_region *region;
+
+ mutex_lock(&module->pmem_regions_lock);
+ hlist_for_each_safe(node, tmp, &module->pmem_regions) {
+ region = hlist_entry(node, struct adsp_pmem_region, list);
+ hlist_del(node);
+ put_pmem_file(region->file);
+ kfree(region);
+ }
+ mutex_unlock(&module->pmem_regions_lock);
+ BUG_ON(!hlist_empty(&module->pmem_regions));
+
+ return 0;
+}
+
+static long adsp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ struct adsp_device *adev = filp->private_data;
+
+ switch (cmd) {
+ case ADSP_IOCTL_ENABLE:
+ return msm_adsp_enable(adev->module);
+
+ case ADSP_IOCTL_DISABLE:
+ return msm_adsp_disable(adev->module);
+
+ case ADSP_IOCTL_DISABLE_EVENT_RSP:
+ return msm_adsp_disable_event_rsp(adev->module);
+
+ case ADSP_IOCTL_DISABLE_ACK:
+ MM_ERR("ADSP_IOCTL_DISABLE_ACK is not implemented\n");
+ break;
+
+ case ADSP_IOCTL_WRITE_COMMAND:
+ return adsp_write_cmd(adev, (void __user *) arg);
+
+ case ADSP_IOCTL_GET_EVENT:
+ return adsp_get_event(adev, (void __user *) arg);
+
+ case ADSP_IOCTL_SET_CLKRATE: {
+ unsigned long clk_rate;
+ if (copy_from_user(&clk_rate, (void *) arg, sizeof(clk_rate)))
+ return -EFAULT;
+ return adsp_set_clkrate(adev->module, clk_rate);
+ }
+
+ case ADSP_IOCTL_REGISTER_PMEM: {
+ struct adsp_pmem_info info;
+ if (copy_from_user(&info, (void *) arg, sizeof(info)))
+ return -EFAULT;
+ return adsp_pmem_add(adev->module, &info);
+ }
+
+ case ADSP_IOCTL_ABORT_EVENT_READ:
+ adev->abort = 1;
+ wake_up(&adev->event_wait);
+ break;
+
+ case ADSP_IOCTL_UNREGISTER_PMEM:
+ return adsp_pmem_del(adev->module);
+
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+static int adsp_release(struct inode *inode, struct file *filp)
+{
+ struct adsp_device *adev = filp->private_data;
+ struct msm_adsp_module *module = adev->module;
+ int rc = 0;
+
+ MM_INFO("release '%s'\n", adev->name);
+
+ /* clear module before putting it to avoid race with open() */
+ adev->module = NULL;
+
+ rc = adsp_pmem_del(module);
+
+ msm_adsp_put(module);
+ return rc;
+}
+
+static void adsp_event(void *driver_data, unsigned id, size_t len,
+ void (*getevent)(void *ptr, size_t len))
+{
+ struct adsp_device *adev = driver_data;
+ struct adsp_event *event;
+ unsigned long flags;
+
+ if (len > ADSP_EVENT_MAX_SIZE) {
+ MM_ERR("event too large (%d bytes)\n", len);
+ return;
+ }
+
+ event = kmalloc(sizeof(*event), GFP_ATOMIC);
+ if (!event) {
+ MM_ERR("cannot allocate buffer\n");
+ return;
+ }
+
+ if (id != EVENT_MSG_ID) {
+ event->type = 0;
+ event->is16 = 0;
+ event->msg_id = id;
+ event->size = len;
+
+ getevent(event->data.msg16, len);
+ } else {
+ event->type = 1;
+ event->is16 = 1;
+ event->msg_id = id;
+ event->size = len;
+ getevent(event->data.msg32, len);
+ }
+
+ spin_lock_irqsave(&adev->event_queue_lock, flags);
+ list_add_tail(&event->list, &adev->event_queue);
+ spin_unlock_irqrestore(&adev->event_queue_lock, flags);
+ wake_up(&adev->event_wait);
+}
+
+static struct msm_adsp_ops adsp_ops = {
+ .event = adsp_event,
+};
+
+static int adsp_open(struct inode *inode, struct file *filp)
+{
+ struct adsp_device *adev;
+ int rc;
+
+ rc = nonseekable_open(inode, filp);
+ if (rc < 0)
+ return rc;
+
+ adev = inode_to_device(inode);
+ if (!adev)
+ return -ENODEV;
+
+ MM_INFO("open '%s'\n", adev->name);
+
+ rc = msm_adsp_get(adev->name, &adev->module, &adsp_ops, adev);
+ if (rc)
+ return rc;
+
+ MM_INFO("opened module '%s' adev %p\n", adev->name, adev);
+ filp->private_data = adev;
+ adev->abort = 0;
+ INIT_HLIST_HEAD(&adev->module->pmem_regions);
+ mutex_init(&adev->module->pmem_regions_lock);
+
+ return 0;
+}
+
+static unsigned adsp_device_count;
+static struct adsp_device *adsp_devices;
+
+static struct adsp_device *inode_to_device(struct inode *inode)
+{
+ unsigned n = MINOR(inode->i_rdev);
+ if (n < adsp_device_count) {
+ if (adsp_devices[n].device)
+ return adsp_devices + n;
+ }
+ return NULL;
+}
+
+static dev_t adsp_devno;
+static struct class *adsp_class;
+
+static const struct file_operations adsp_fops = {
+ .owner = THIS_MODULE,
+ .open = adsp_open,
+ .unlocked_ioctl = adsp_ioctl,
+ .release = adsp_release,
+};
+
+static void adsp_create(struct adsp_device *adev, const char *name,
+ struct device *parent, dev_t devt)
+{
+ struct device *dev;
+ int rc;
+
+ dev = device_create(adsp_class, parent, devt, "%s", name);
+ if (IS_ERR(dev))
+ return;
+
+ init_waitqueue_head(&adev->event_wait);
+ INIT_LIST_HEAD(&adev->event_queue);
+ spin_lock_init(&adev->event_queue_lock);
+
+ cdev_init(&adev->cdev, &adsp_fops);
+ adev->cdev.owner = THIS_MODULE;
+
+ rc = cdev_add(&adev->cdev, devt, 1);
+ if (rc < 0) {
+ device_destroy(adsp_class, devt);
+ } else {
+ adev->device = dev;
+ adev->name = name;
+ }
+}
+
+void msm_adsp_publish_cdevs(struct msm_adsp_module *modules, unsigned n)
+{
+ int rc;
+
+ adsp_devices = kzalloc(sizeof(struct adsp_device) * n, GFP_KERNEL);
+ if (!adsp_devices)
+ return;
+
+ adsp_class = class_create(THIS_MODULE, "adsp");
+ if (IS_ERR(adsp_class))
+ goto fail_create_class;
+
+ rc = alloc_chrdev_region(&adsp_devno, 0, n, "adsp");
+ if (rc < 0)
+ goto fail_alloc_region;
+
+ adsp_device_count = n;
+ for (n = 0; n < adsp_device_count; n++) {
+ adsp_create(adsp_devices + n,
+ modules[n].name, &modules[n].pdev.dev,
+ MKDEV(MAJOR(adsp_devno), n));
+ }
+
+ return;
+
+fail_alloc_region:
+ class_unregister(adsp_class);
+fail_create_class:
+ kfree(adsp_devices);
+}
diff --git a/arch/arm/mach-msm/qdsp5v2/adsp_info.c b/arch/arm/mach-msm/qdsp5v2/adsp_info.c
new file mode 100644
index 0000000..4026367
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/adsp_info.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include "adsp.h"
+
+/* Firmware modules */
+#define QDSP_MODULE_KERNEL 0x0106dd4e
+#define QDSP_MODULE_AFETASK 0x0106dd6f
+#define QDSP_MODULE_AUDPLAY0TASK 0x0106dd70
+#define QDSP_MODULE_AUDPLAY1TASK 0x0106dd71
+#define QDSP_MODULE_AUDPPTASK 0x0106dd72
+#define QDSP_MODULE_VIDEOTASK 0x0106dd73
+#define QDSP_MODULE_VIDEO_AAC_VOC 0x0106dd74
+#define QDSP_MODULE_PCM_DEC 0x0106dd75
+#define QDSP_MODULE_AUDIO_DEC_MP3 0x0106dd76
+#define QDSP_MODULE_AUDIO_DEC_AAC 0x0106dd77
+#define QDSP_MODULE_AUDIO_DEC_WMA 0x0106dd78
+#define QDSP_MODULE_HOSTPCM 0x0106dd79
+#define QDSP_MODULE_DTMF 0x0106dd7a
+#define QDSP_MODULE_AUDRECTASK 0x0106dd7b
+#define QDSP_MODULE_AUDPREPROCTASK 0x0106dd7c
+#define QDSP_MODULE_SBC_ENC 0x0106dd7d
+#define QDSP_MODULE_VOC_UMTS 0x0106dd9a
+#define QDSP_MODULE_VOC_CDMA 0x0106dd98
+#define QDSP_MODULE_VOC_PCM 0x0106dd7f
+#define QDSP_MODULE_VOCENCTASK 0x0106dd80
+#define QDSP_MODULE_VOCDECTASK 0x0106dd81
+#define QDSP_MODULE_VOICEPROCTASK 0x0106dd82
+#define QDSP_MODULE_VIDEOENCTASK 0x0106dd83
+#define QDSP_MODULE_VFETASK 0x0106dd84
+#define QDSP_MODULE_WAV_ENC 0x0106dd85
+#define QDSP_MODULE_AACLC_ENC 0x0106dd86
+#define QDSP_MODULE_VIDEO_AMR 0x0106dd87
+#define QDSP_MODULE_VOC_AMR 0x0106dd88
+#define QDSP_MODULE_VOC_EVRC 0x0106dd89
+#define QDSP_MODULE_VOC_13K 0x0106dd8a
+#define QDSP_MODULE_VOC_FGV 0x0106dd8b
+#define QDSP_MODULE_DIAGTASK 0x0106dd8c
+#define QDSP_MODULE_JPEGTASK 0x0106dd8d
+#define QDSP_MODULE_LPMTASK 0x0106dd8e
+#define QDSP_MODULE_QCAMTASK 0x0106dd8f
+#define QDSP_MODULE_MODMATHTASK 0x0106dd90
+#define QDSP_MODULE_AUDPLAY2TASK 0x0106dd91
+#define QDSP_MODULE_AUDPLAY3TASK 0x0106dd92
+#define QDSP_MODULE_AUDPLAY4TASK 0x0106dd93
+#define QDSP_MODULE_GRAPHICSTASK 0x0106dd94
+#define QDSP_MODULE_MIDI 0x0106dd95
+#define QDSP_MODULE_GAUDIO 0x0106dd96
+#define QDSP_MODULE_VDEC_LP_MODE 0x0106dd97
+#define QDSP_MODULE_VIDEO_AAC_VOC_TURBO 0x01089f77
+#define QDSP_MODULE_VIDEO_AMR_TURBO 0x01089f78
+#define QDSP_MODULE_WM_TURBO_MODE 0x01089f79
+#define QDSP_MODULE_VDEC_LP_MODE_TURBO 0x01089f7a
+#define QDSP_MODULE_AUDREC0TASK 0x0109696f
+#define QDSP_MODULE_AUDREC1TASK 0x01096970
+#define QDSP_MODULE_AUDREC2TASK 0x010a2f59
+#define QDSP_MODULE_MAX 0x7fffffff
+
+ /* DO NOT USE: Force this enum to be a 32bit type to improve speed */
+#define QDSP_MODULE_32BIT_DUMMY 0x10000
+
+static uint32_t *qdsp_task_to_module[IMG_MAX];
+static uint32_t *qdsp_queue_offset_table[IMG_MAX];
+
+#define QDSP_MODULE(n, clkname, clkrate, verify_cmd_func, patch_event_func) \
+ { .name = #n, .pdev_name = "adsp_" #n, .id = QDSP_MODULE_##n, \
+ .clk_name = clkname, .clk_rate = clkrate, \
+ .verify_cmd = verify_cmd_func, .patch_event = patch_event_func }
+
+static struct adsp_module_info module_info[] = {
+ QDSP_MODULE(AUDPLAY0TASK, NULL, 0, NULL, NULL),
+ QDSP_MODULE(AUDPLAY1TASK, NULL, 0, NULL, NULL),
+ QDSP_MODULE(AUDPLAY2TASK, NULL, 0, NULL, NULL),
+ QDSP_MODULE(AUDPLAY3TASK, NULL, 0, NULL, NULL),
+ QDSP_MODULE(AUDPPTASK, NULL, 0, NULL, NULL),
+ QDSP_MODULE(AUDPREPROCTASK, NULL, 0, NULL, NULL),
+ QDSP_MODULE(AFETASK , NULL, 0, NULL, NULL),
+ QDSP_MODULE(AUDREC0TASK, NULL, 0, NULL, NULL),
+ QDSP_MODULE(AUDREC1TASK, NULL, 0, NULL, NULL),
+ QDSP_MODULE(AUDREC2TASK, NULL, 0, NULL, NULL),
+};
+
+int adsp_init_info(struct adsp_info *info)
+{
+ uint32_t img_num;
+
+ info->send_irq = 0x00c00200;
+ info->read_ctrl = 0x00400038;
+ info->write_ctrl = 0x00400034;
+
+ info->max_msg16_size = 193;
+ info->max_msg32_size = 8;
+ for (img_num = 0; img_num < IMG_MAX; img_num++)
+ qdsp_queue_offset_table[img_num] =
+ &info->init_info_ptr->queue_offsets[img_num][0];
+
+ for (img_num = 0; img_num < IMG_MAX; img_num++)
+ qdsp_task_to_module[img_num] =
+ &info->init_info_ptr->task_to_module_tbl[img_num][0];
+ info->max_task_id = ENTRIES_MAX;
+ info->max_module_id = QDSP_MODULE_MAX - 1;
+ info->max_queue_id = QDSP_MAX_NUM_QUEUES;
+ info->max_image_id = 0;
+ info->queue_offset = qdsp_queue_offset_table;
+ info->task_to_module = qdsp_task_to_module;
+
+ info->module_count = ARRAY_SIZE(module_info);
+ info->module = module_info;
+ return 0;
+}
diff --git a/arch/arm/mach-msm/qdsp5v2/afe.c b/arch/arm/mach-msm/qdsp5v2/afe.c
new file mode 100644
index 0000000..20c9898
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/afe.c
@@ -0,0 +1,534 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+#include <linux/module.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/jiffies.h>
+#include <linux/debugfs.h>
+#include <asm/uaccess.h>
+#include <mach/qdsp5v2/qdsp5afecmdi.h>
+#include <mach/qdsp5v2/qdsp5afemsg.h>
+#include <mach/qdsp5v2/afe.h>
+#include <mach/msm_adsp.h>
+#include <mach/debug_mm.h>
+
+#define AFE_MAX_TIMEOUT 500 /* 500 ms */
+#define AFE_MAX_CLNT 6 /* 6 HW path defined so far */
+#define GETDEVICEID(x) ((x) - 1)
+
+struct msm_afe_state {
+ struct msm_adsp_module *mod;
+ struct msm_adsp_ops adsp_ops;
+ struct mutex lock;
+ u8 in_use;
+ u8 codec_config[AFE_MAX_CLNT];
+ wait_queue_head_t wait;
+ u8 aux_conf_flag;
+};
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *debugfs_afelb;
+#endif
+
+
+static struct msm_afe_state the_afe_state;
+
+#define afe_send_queue(afe, cmd, len) \
+ msm_adsp_write(afe->mod, QDSP_apuAfeQueue, \
+ cmd, len)
+
+static void afe_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent)(void *ptr, size_t len))
+{
+ struct msm_afe_state *afe = data;
+
+ MM_DBG("msg_id %d \n", id);
+
+ switch (id) {
+ case AFE_APU_MSG_CODEC_CONFIG_ACK: {
+ struct afe_msg_codec_config_ack afe_ack;
+ getevent(&afe_ack, AFE_APU_MSG_CODEC_CONFIG_ACK_LEN);
+ MM_DBG("%s: device_id: %d device activity: %d\n", __func__,
+ afe_ack.device_id, afe_ack.device_activity);
+ if (afe_ack.device_activity == AFE_MSG_CODEC_CONFIG_DISABLED)
+ afe->codec_config[GETDEVICEID(afe_ack.device_id)] = 0;
+ else
+ afe->codec_config[GETDEVICEID(afe_ack.device_id)] =
+ afe_ack.device_activity;
+
+ wake_up(&afe->wait);
+ break;
+ }
+ case AFE_APU_MSG_VOC_TIMING_SUCCESS:
+ MM_INFO("Received VOC_TIMING_SUCCESS message from AFETASK\n");
+ break;
+ case ADSP_MESSAGE_ID:
+ MM_DBG("Received ADSP event: module enable/disable(audpptask)");
+ break;
+ default:
+ MM_ERR("unexpected message from afe \n");
+ }
+
+ return;
+}
+
+static void afe_dsp_codec_config(struct msm_afe_state *afe,
+ u8 path_id, u8 enable, struct msm_afe_config *config)
+{
+ struct afe_cmd_codec_config cmd;
+
+ MM_DBG("%s() %p\n", __func__, config);
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AFE_CMD_CODEC_CONFIG_CMD;
+ cmd.device_id = path_id;
+ cmd.activity = enable;
+ if (config) {
+ MM_DBG("%s: sample_rate %x ch mode %x vol %x\n",
+ __func__, config->sample_rate,
+ config->channel_mode, config->volume);
+ cmd.sample_rate = config->sample_rate;
+ cmd.channel_mode = config->channel_mode;
+ cmd.volume = config->volume;
+ }
+ afe_send_queue(afe, &cmd, sizeof(cmd));
+}
+/* Function is called after afe module been enabled */
+void afe_loopback(int enable)
+{
+ struct afe_cmd_loopback cmd;
+ struct msm_afe_state *afe;
+
+ afe = &the_afe_state;
+ MM_DBG("enable %d\n", enable);
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AFE_CMD_LOOPBACK;
+ if (enable)
+ cmd.enable_flag = AFE_LOOPBACK_ENABLE_COMMAND;
+
+ afe_send_queue(afe, &cmd, sizeof(cmd));
+}
+EXPORT_SYMBOL(afe_loopback);
+
+void afe_ext_loopback(int enable, int rx_copp_id, int tx_copp_id)
+{
+ struct afe_cmd_ext_loopback cmd;
+ struct msm_afe_state *afe;
+
+ afe = &the_afe_state;
+ MM_DBG("enable %d\n", enable);
+ if ((rx_copp_id == 0) && (tx_copp_id == 0)) {
+ afe_loopback(enable);
+ } else {
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AFE_CMD_EXT_LOOPBACK;
+ cmd.source_id = tx_copp_id;
+ cmd.dst_id = rx_copp_id;
+ if (enable)
+ cmd.enable_flag = AFE_LOOPBACK_ENABLE_COMMAND;
+
+ afe_send_queue(afe, &cmd, sizeof(cmd));
+ }
+}
+EXPORT_SYMBOL(afe_ext_loopback);
+
+void afe_device_volume_ctrl(u16 device_id, u16 device_volume)
+{
+ struct afe_cmd_device_volume_ctrl cmd;
+ struct msm_afe_state *afe;
+
+ afe = &the_afe_state;
+ MM_DBG("device 0x%4x volume 0x%4x\n", device_id, device_volume);
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AFE_CMD_DEVICE_VOLUME_CTRL;
+ cmd.device_id = device_id;
+ cmd.device_volume = device_volume;
+ afe_send_queue(afe, &cmd, sizeof(cmd));
+}
+EXPORT_SYMBOL(afe_device_volume_ctrl);
+
+int afe_enable(u8 path_id, struct msm_afe_config *config)
+{
+ struct msm_afe_state *afe = &the_afe_state;
+ int rc;
+
+ MM_DBG("%s: path %d\n", __func__, path_id);
+ if ((GETDEVICEID(path_id) < 0) || (GETDEVICEID(path_id) > 5)) {
+ MM_ERR("Invalid path_id: %d\n", path_id);
+ return -EINVAL;
+ }
+ mutex_lock(&afe->lock);
+ if (!afe->in_use && !afe->aux_conf_flag) {
+ /* enable afe */
+ rc = msm_adsp_get("AFETASK", &afe->mod, &afe->adsp_ops, afe);
+ if (rc < 0) {
+ MM_ERR("%s: failed to get AFETASK module\n", __func__);
+ goto error_adsp_get;
+ }
+ rc = msm_adsp_enable(afe->mod);
+ if (rc < 0)
+ goto error_adsp_enable;
+ }
+ /* Issue codec config command */
+ afe_dsp_codec_config(afe, path_id, 1, config);
+ rc = wait_event_timeout(afe->wait,
+ afe->codec_config[GETDEVICEID(path_id)],
+ msecs_to_jiffies(AFE_MAX_TIMEOUT));
+ if (!rc) {
+ MM_ERR("AFE failed to respond within %d ms\n", AFE_MAX_TIMEOUT);
+ rc = -ENODEV;
+ if (!afe->in_use) {
+ if (!afe->aux_conf_flag ||
+ (afe->aux_conf_flag &&
+ (path_id == AFE_HW_PATH_AUXPCM_RX ||
+ path_id == AFE_HW_PATH_AUXPCM_TX))) {
+ /* clean up if there is no client */
+ msm_adsp_disable(afe->mod);
+ msm_adsp_put(afe->mod);
+ afe->aux_conf_flag = 0;
+ afe->mod = NULL;
+ }
+ }
+
+ } else {
+ rc = 0;
+ afe->in_use++;
+ }
+
+ mutex_unlock(&afe->lock);
+ return rc;
+
+error_adsp_enable:
+ msm_adsp_put(afe->mod);
+ afe->mod = NULL;
+error_adsp_get:
+ mutex_unlock(&afe->lock);
+ return rc;
+}
+EXPORT_SYMBOL(afe_enable);
+
+int afe_config_fm_codec(int fm_enable, uint16_t source)
+{
+ struct afe_cmd_fm_codec_config cmd;
+ struct msm_afe_state *afe = &the_afe_state;
+ int rc = 0;
+ int i = 0;
+ unsigned short *ptrmem = (unsigned short *)&cmd;
+
+ MM_INFO(" configure fm codec\n");
+ mutex_lock(&afe->lock);
+ if (!afe->in_use) {
+ /* enable afe */
+ rc = msm_adsp_get("AFETASK", &afe->mod, &afe->adsp_ops, afe);
+ if (rc < 0) {
+ MM_ERR("%s: failed to get AFETASK module\n", __func__);
+ goto error_adsp_get;
+ }
+ rc = msm_adsp_enable(afe->mod);
+ if (rc < 0)
+ goto error_adsp_enable;
+ }
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AFE_CMD_FM_RX_ROUTING_CMD;
+ cmd.enable = fm_enable;
+ cmd.device_id = source;
+
+ for (i = 0; i < sizeof(cmd)/2; i++, ++ptrmem)
+ MM_DBG("cmd[%d]=0x%04x\n", i, *ptrmem);
+ afe_send_queue(afe, &cmd, sizeof(cmd));
+
+ mutex_unlock(&afe->lock);
+ return rc;
+error_adsp_enable:
+ msm_adsp_put(afe->mod);
+ afe->mod = NULL;
+error_adsp_get:
+ mutex_unlock(&afe->lock);
+ return rc;
+}
+EXPORT_SYMBOL(afe_config_fm_codec);
+
+int afe_config_fm_volume(uint16_t volume)
+{
+ struct afe_cmd_fm_volume_config cmd;
+ struct msm_afe_state *afe = &the_afe_state;
+ int rc = 0;
+
+ MM_INFO(" configure fm volume\n");
+ mutex_lock(&afe->lock);
+ if (!afe->in_use) {
+ /* enable afe */
+ rc = msm_adsp_get("AFETASK", &afe->mod, &afe->adsp_ops, afe);
+ if (rc < 0) {
+ MM_ERR("%s: failed to get AFETASK module\n", __func__);
+ goto error_adsp_get;
+ }
+ rc = msm_adsp_enable(afe->mod);
+ if (rc < 0)
+ goto error_adsp_enable;
+ }
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AFE_CMD_FM_PLAYBACK_VOLUME_CMD;
+ cmd.volume = volume;
+
+ afe_send_queue(afe, &cmd, sizeof(cmd));
+
+ mutex_unlock(&afe->lock);
+ return rc;
+error_adsp_enable:
+ msm_adsp_put(afe->mod);
+ afe->mod = NULL;
+error_adsp_get:
+ mutex_unlock(&afe->lock);
+ return rc;
+}
+EXPORT_SYMBOL(afe_config_fm_volume);
+
+int afe_config_fm_calibration_gain(uint16_t device_id,
+ uint16_t calibration_gain)
+{
+ struct afe_cmd_fm_calibgain_config cmd;
+ struct msm_afe_state *afe = &the_afe_state;
+ int rc = 0;
+
+ MM_INFO("Configure for rx device = 0x%4x, gain = 0x%4x\n", device_id,
+ calibration_gain);
+ mutex_lock(&afe->lock);
+ if (!afe->in_use) {
+ /* enable afe */
+ rc = msm_adsp_get("AFETASK", &afe->mod, &afe->adsp_ops, afe);
+ if (rc < 0) {
+ MM_ERR("%s: failed to get AFETASK module\n", __func__);
+ goto error_adsp_get;
+ }
+ rc = msm_adsp_enable(afe->mod);
+ if (rc < 0)
+ goto error_adsp_enable;
+ }
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AFE_CMD_FM_CALIBRATION_GAIN_CMD;
+ cmd.device_id = device_id;
+ cmd.calibration_gain = calibration_gain;
+
+ afe_send_queue(afe, &cmd, sizeof(cmd));
+
+ mutex_unlock(&afe->lock);
+ return rc;
+error_adsp_enable:
+ msm_adsp_put(afe->mod);
+ afe->mod = NULL;
+error_adsp_get:
+ mutex_unlock(&afe->lock);
+ return rc;
+}
+EXPORT_SYMBOL(afe_config_fm_calibration_gain);
+
+int afe_config_aux_codec(int pcm_ctl_value, int aux_codec_intf_value,
+ int data_format_pad)
+{
+ struct afe_cmd_aux_codec_config cmd;
+ struct msm_afe_state *afe = &the_afe_state;
+ int rc = 0;
+
+ MM_DBG(" configure aux codec \n");
+ mutex_lock(&afe->lock);
+ if (!afe->in_use && !afe->aux_conf_flag) {
+ /* enable afe */
+ rc = msm_adsp_get("AFETASK", &afe->mod, &afe->adsp_ops, afe);
+ if (rc < 0) {
+ MM_ERR("%s: failed to get AFETASK module\n", __func__);
+ goto error_adsp_get;
+ }
+ rc = msm_adsp_enable(afe->mod);
+ if (rc < 0)
+ goto error_adsp_enable;
+ }
+ afe->aux_conf_flag = 1;
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AFE_CMD_AUX_CODEC_CONFIG_CMD;
+ cmd.dma_path_ctl = 0;
+ cmd.pcm_ctl = pcm_ctl_value;
+ cmd.eight_khz_int_mode = 0;
+ cmd.aux_codec_intf_ctl = aux_codec_intf_value;
+ cmd.data_format_padding_info = data_format_pad;
+
+ afe_send_queue(afe, &cmd, sizeof(cmd));
+
+ mutex_unlock(&afe->lock);
+ return rc;
+error_adsp_enable:
+ msm_adsp_put(afe->mod);
+ afe->mod = NULL;
+error_adsp_get:
+ mutex_unlock(&afe->lock);
+ return rc;
+}
+EXPORT_SYMBOL(afe_config_aux_codec);
+
+int afe_config_rmc_block(struct acdb_rmc_block *acdb_rmc)
+{
+ struct afe_cmd_cfg_rmc cmd;
+ struct msm_afe_state *afe = &the_afe_state;
+ int rc = 0;
+ int i = 0;
+ unsigned short *ptrmem = (unsigned short *)&cmd;
+
+ MM_DBG(" configure rmc block\n");
+ mutex_lock(&afe->lock);
+ if (!afe->in_use && !afe->mod) {
+ /* enable afe */
+ rc = msm_adsp_get("AFETASK", &afe->mod, &afe->adsp_ops, afe);
+ if (rc < 0) {
+ MM_DBG("%s: failed to get AFETASK module\n", __func__);
+ goto error_adsp_get;
+ }
+ rc = msm_adsp_enable(afe->mod);
+ if (rc < 0)
+ goto error_adsp_enable;
+ }
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AFE_CMD_CFG_RMC_PARAMS;
+
+ cmd.rmc_mode = acdb_rmc->rmc_enable;
+ cmd.rmc_ipw_length_ms = acdb_rmc->rmc_ipw_length_ms;
+ cmd.rmc_peak_length_ms = acdb_rmc->rmc_peak_length_ms;
+ cmd.rmc_init_pulse_length_ms = acdb_rmc->rmc_init_pulse_length_ms;
+ cmd.rmc_total_int_length_ms = acdb_rmc->rmc_total_int_length_ms;
+ cmd.rmc_rampupdn_length_ms = acdb_rmc->rmc_rampupdn_length_ms;
+ cmd.rmc_delay_length_ms = acdb_rmc->rmc_delay_length_ms;
+ cmd.rmc_detect_start_threshdb = acdb_rmc->rmc_detect_start_threshdb;
+ cmd.rmc_init_pulse_threshdb = acdb_rmc->rmc_init_pulse_threshdb;
+
+ for (i = 0; i < sizeof(cmd)/2; i++, ++ptrmem)
+ MM_DBG("cmd[%d]=0x%04x\n", i, *ptrmem);
+ afe_send_queue(afe, &cmd, sizeof(cmd));
+
+ mutex_unlock(&afe->lock);
+ return rc;
+error_adsp_enable:
+ msm_adsp_put(afe->mod);
+ afe->mod = NULL;
+error_adsp_get:
+ mutex_unlock(&afe->lock);
+ return rc;
+}
+EXPORT_SYMBOL(afe_config_rmc_block);
+
+int afe_disable(u8 path_id)
+{
+ struct msm_afe_state *afe = &the_afe_state;
+ int rc;
+
+ mutex_lock(&afe->lock);
+
+ BUG_ON(!afe->in_use);
+ MM_DBG("%s() path_id:%d codec state:%d\n", __func__, path_id,
+ afe->codec_config[GETDEVICEID(path_id)]);
+ afe_dsp_codec_config(afe, path_id, 0, NULL);
+ rc = wait_event_timeout(afe->wait,
+ !afe->codec_config[GETDEVICEID(path_id)],
+ msecs_to_jiffies(AFE_MAX_TIMEOUT));
+ if (!rc) {
+ MM_ERR("AFE failed to respond within %d ms\n", AFE_MAX_TIMEOUT);
+ rc = -1;
+ } else
+ rc = 0;
+ afe->in_use--;
+ MM_DBG("%s() in_use:%d \n", __func__, afe->in_use);
+ if (!afe->in_use) {
+ msm_adsp_disable(afe->mod);
+ msm_adsp_put(afe->mod);
+ afe->aux_conf_flag = 0;
+ afe->mod = NULL;
+ }
+ mutex_unlock(&afe->lock);
+ return rc;
+}
+EXPORT_SYMBOL(afe_disable);
+
+
+#ifdef CONFIG_DEBUG_FS
+static int afe_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ MM_INFO("debug intf %s\n", (char *) file->private_data);
+ return 0;
+}
+
+static ssize_t afe_debug_write(struct file *filp,
+ const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+ char *lb_str = filp->private_data;
+ char cmd;
+
+ if (get_user(cmd, ubuf))
+ return -EFAULT;
+
+ MM_INFO("%s %c\n", lb_str, cmd);
+
+ if (!strcmp(lb_str, "afe_loopback")) {
+ switch (cmd) {
+ case '1':
+ afe_loopback(1);
+ break;
+ case '0':
+ afe_loopback(0);
+ break;
+ }
+ }
+
+ return cnt;
+}
+
+static const struct file_operations afe_debug_fops = {
+ .open = afe_debug_open,
+ .write = afe_debug_write
+};
+#endif
+
+static int __init afe_init(void)
+{
+ struct msm_afe_state *afe = &the_afe_state;
+
+ MM_INFO("AFE driver init\n");
+
+ memset(afe, 0, sizeof(struct msm_afe_state));
+ afe->adsp_ops.event = afe_dsp_event;
+ mutex_init(&afe->lock);
+ init_waitqueue_head(&afe->wait);
+
+#ifdef CONFIG_DEBUG_FS
+ debugfs_afelb = debugfs_create_file("afe_loopback",
+ S_IFREG | S_IWUGO, NULL, (void *) "afe_loopback",
+ &afe_debug_fops);
+#endif
+
+ return 0;
+}
+
+static void __exit afe_exit(void)
+{
+ MM_INFO("AFE driver exit\n");
+#ifdef CONFIG_DEBUG_FS
+ if (debugfs_afelb)
+ debugfs_remove(debugfs_afelb);
+#endif
+ if (the_afe_state.mod)
+ msm_adsp_put(the_afe_state.mod);
+ return;
+}
+
+module_init(afe_init);
+module_exit(afe_exit);
+
+MODULE_DESCRIPTION("MSM AFE driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_a2dp_in.c b/arch/arm/mach-msm/qdsp5v2/audio_a2dp_in.c
new file mode 100644
index 0000000..bb3404a7
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_a2dp_in.c
@@ -0,0 +1,977 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * sbc/pcm audio input driver
+ * Based on the pcm input driver in arch/arm/mach-msm/qdsp5v2/audio_pcm_in.c
+ *
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * All source code in this file is licensed under the following license except
+ * where indicated.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/msm_audio.h>
+#include <linux/msm_audio_sbc.h>
+#include <linux/android_pmem.h>
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+
+#include <mach/msm_adsp.h>
+#include <mach/qdsp5v2/qdsp5audreccmdi.h>
+#include <mach/qdsp5v2/qdsp5audrecmsg.h>
+#include <mach/qdsp5v2/audpreproc.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/debug_mm.h>
+
+/* FRAME_NUM must be a power of two */
+#define FRAME_NUM (8)
+#define FRAME_SIZE (2052 * 2)
+#define FRAME_SIZE_SBC (768 * 2)
+#define MONO_DATA_SIZE (2048)
+#define STEREO_DATA_SIZE (MONO_DATA_SIZE * 2)
+#define DMASZ (FRAME_SIZE * FRAME_NUM)
+
+struct buffer {
+ void *data;
+ uint32_t size;
+ uint32_t read;
+ uint32_t addr;
+ uint32_t frame_num;
+ uint32_t frame_len;
+};
+
+struct audio_a2dp_in {
+ struct buffer in[FRAME_NUM];
+
+ spinlock_t dsp_lock;
+
+ atomic_t in_bytes;
+ atomic_t in_samples;
+
+ struct mutex lock;
+ struct mutex read_lock;
+ wait_queue_head_t wait;
+ wait_queue_head_t wait_enable;
+
+ struct msm_adsp_module *audrec;
+
+ struct audrec_session_info session_info; /*audrec session info*/
+
+ /* configuration to use on next enable */
+ uint32_t samp_rate;
+ uint32_t channel_mode;
+ uint32_t buffer_size; /* 2048 for mono, 4096 for stereo */
+ uint32_t enc_type;
+ struct msm_audio_sbc_enc_config cfg;
+
+ uint32_t dsp_cnt;
+ uint32_t in_head; /* next buffer dsp will write */
+ uint32_t in_tail; /* next buffer read() will read */
+ uint32_t in_count; /* number of buffers available to read() */
+ uint32_t mode;
+
+ const char *module_name;
+ unsigned queue_ids;
+ uint16_t enc_id; /* Session Id */
+
+ uint16_t source; /* Encoding source bit mask */
+ uint32_t device_events; /* device events interested in */
+ uint32_t dev_cnt;
+ spinlock_t dev_lock;
+
+ /* data allocated for various buffers */
+ char *data;
+ dma_addr_t phys;
+
+ int opened;
+ int enabled;
+ int running;
+ int stopped; /* set when stopped, cleared on flush */
+ int abort; /* set when error, like sample rate mismatch */
+};
+
+static struct audio_a2dp_in the_audio_a2dp_in;
+
+struct wav_frame {
+ uint16_t frame_count_lsw;
+ uint16_t frame_count_msw;
+ uint16_t frame_length;
+ uint16_t erased_a2dp;
+ unsigned char raw_bitstream[]; /* samples */
+};
+
+struct sbc_frame {
+ uint16_t bit_rate_msw;
+ uint16_t bit_rate_lsw;
+ uint16_t frame_length;
+ uint16_t frame_num;
+ unsigned char raw_bitstream[]; /* samples */
+};
+
+struct audio_frame {
+ union {
+ struct wav_frame wav;
+ struct sbc_frame sbc;
+ } a2dp;
+} __attribute__((packed));
+
+/* Audrec Queue command sent macro's */
+#define audrec_send_bitstreamqueue(audio, cmd, len) \
+ msm_adsp_write(audio->audrec, ((audio->queue_ids & 0xFFFF0000) >> 16),\
+ cmd, len)
+
+#define audrec_send_audrecqueue(audio, cmd, len) \
+ msm_adsp_write(audio->audrec, (audio->queue_ids & 0x0000FFFF),\
+ cmd, len)
+
+/* DSP command send functions */
+static int auda2dp_in_enc_config(struct audio_a2dp_in *audio, int enable);
+static int auda2dp_in_param_config(struct audio_a2dp_in *audio);
+static int auda2dp_in_mem_config(struct audio_a2dp_in *audio);
+static int auda2dp_in_record_config(struct audio_a2dp_in *audio, int enable);
+static int auda2dp_dsp_read_buffer(struct audio_a2dp_in *audio,
+ uint32_t read_cnt);
+
+static void auda2dp_in_get_dsp_frames(struct audio_a2dp_in *audio);
+
+static void auda2dp_in_flush(struct audio_a2dp_in *audio);
+
+static void a2dp_in_listener(u32 evt_id, union auddev_evt_data *evt_payload,
+ void *private_data)
+{
+ struct audio_a2dp_in *audio = (struct audio_a2dp_in *) private_data;
+ unsigned long flags;
+
+ MM_DBG("evt_id = 0x%8x\n", evt_id);
+ switch (evt_id) {
+ case AUDDEV_EVT_DEV_RDY: {
+ MM_DBG("AUDDEV_EVT_DEV_RDY\n");
+ spin_lock_irqsave(&audio->dev_lock, flags);
+ audio->dev_cnt++;
+ audio->source |= (0x1 << evt_payload->routing_id);
+ spin_unlock_irqrestore(&audio->dev_lock, flags);
+
+ if ((audio->running == 1) && (audio->enabled == 1))
+ auda2dp_in_record_config(audio, 1);
+
+ break;
+ }
+ case AUDDEV_EVT_DEV_RLS: {
+ MM_DBG("AUDDEV_EVT_DEV_RLS\n");
+ spin_lock_irqsave(&audio->dev_lock, flags);
+ audio->dev_cnt--;
+ audio->source &= ~(0x1 << evt_payload->routing_id);
+ spin_unlock_irqrestore(&audio->dev_lock, flags);
+
+ if (!audio->running || !audio->enabled)
+ break;
+
+ /* Turn of as per source */
+ if (audio->source)
+ auda2dp_in_record_config(audio, 1);
+ else
+ /* Turn off all */
+ auda2dp_in_record_config(audio, 0);
+
+ break;
+ }
+ case AUDDEV_EVT_FREQ_CHG: {
+ MM_DBG("Encoder Driver got sample rate change event\n");
+ MM_DBG("sample rate %d\n", evt_payload->freq_info.sample_rate);
+ MM_DBG("dev_type %d\n", evt_payload->freq_info.dev_type);
+ MM_DBG("acdb_dev_id %d\n", evt_payload->freq_info.acdb_dev_id);
+ if (audio->running == 1) {
+ /* Stop Recording sample rate does not match
+ with device sample rate */
+ if (evt_payload->freq_info.sample_rate !=
+ audio->samp_rate) {
+ auda2dp_in_record_config(audio, 0);
+ audio->abort = 1;
+ wake_up(&audio->wait);
+ }
+ }
+ break;
+ }
+ default:
+ MM_ERR("wrong event %d\n", evt_id);
+ break;
+ }
+}
+
+/* ------------------- dsp preproc event handler--------------------- */
+static void audpreproc_dsp_event(void *data, unsigned id, void *msg)
+{
+ struct audio_a2dp_in *audio = data;
+
+ switch (id) {
+ case AUDPREPROC_ERROR_MSG: {
+ struct audpreproc_err_msg *err_msg = msg;
+
+ MM_ERR("ERROR_MSG: stream id %d err idx %d\n",
+ err_msg->stream_id, err_msg->aud_preproc_err_idx);
+ /* Error case */
+ wake_up(&audio->wait_enable);
+ break;
+ }
+ case AUDPREPROC_CMD_CFG_DONE_MSG: {
+ MM_DBG("CMD_CFG_DONE_MSG \n");
+ break;
+ }
+ case AUDPREPROC_CMD_ENC_CFG_DONE_MSG: {
+ struct audpreproc_cmd_enc_cfg_done_msg *enc_cfg_msg = msg;
+
+ MM_DBG("CMD_ENC_CFG_DONE_MSG: stream id %d enc type \
+ 0x%8x\n", enc_cfg_msg->stream_id,
+ enc_cfg_msg->rec_enc_type);
+ /* Encoder enable success */
+ if (enc_cfg_msg->rec_enc_type & ENCODE_ENABLE)
+ auda2dp_in_param_config(audio);
+ else { /* Encoder disable success */
+ audio->running = 0;
+ auda2dp_in_record_config(audio, 0);
+ }
+ break;
+ }
+ case AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG: {
+ MM_DBG("CMD_ENC_PARAM_CFG_DONE_MSG \n");
+ auda2dp_in_mem_config(audio);
+ break;
+ }
+ case AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG: {
+ MM_DBG("AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG \n");
+ wake_up(&audio->wait_enable);
+ break;
+ }
+ default:
+ MM_ERR("Unknown Event id %d\n", id);
+ }
+}
+
+/* ------------------- dsp audrec event handler--------------------- */
+static void audrec_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent)(void *ptr, size_t len))
+{
+ struct audio_a2dp_in *audio = data;
+
+ switch (id) {
+ case AUDREC_CMD_MEM_CFG_DONE_MSG: {
+ MM_DBG("CMD_MEM_CFG_DONE MSG DONE\n");
+ audio->running = 1;
+ if (audio->dev_cnt > 0)
+ auda2dp_in_record_config(audio, 1);
+ break;
+ }
+ case AUDREC_FATAL_ERR_MSG: {
+ struct audrec_fatal_err_msg fatal_err_msg;
+
+ getevent(&fatal_err_msg, AUDREC_FATAL_ERR_MSG_LEN);
+ MM_ERR("FATAL_ERR_MSG: err id %d\n",
+ fatal_err_msg.audrec_err_id);
+ /* Error stop the encoder */
+ audio->stopped = 1;
+ wake_up(&audio->wait);
+ break;
+ }
+ case AUDREC_UP_PACKET_READY_MSG: {
+ struct audrec_up_pkt_ready_msg pkt_ready_msg;
+
+ getevent(&pkt_ready_msg, AUDREC_UP_PACKET_READY_MSG_LEN);
+ MM_DBG("UP_PACKET_READY_MSG: write cnt lsw %d \
+ write cnt msw %d read cnt lsw %d read cnt msw %d \n",\
+ pkt_ready_msg.audrec_packet_write_cnt_lsw, \
+ pkt_ready_msg.audrec_packet_write_cnt_msw, \
+ pkt_ready_msg.audrec_up_prev_read_cnt_lsw, \
+ pkt_ready_msg.audrec_up_prev_read_cnt_msw);
+
+ auda2dp_in_get_dsp_frames(audio);
+ break;
+ }
+ case ADSP_MESSAGE_ID: {
+ MM_DBG("Received ADSP event: module audrectask\n");
+ break;
+ }
+ default:
+ MM_ERR("Unknown Event id %d\n", id);
+ }
+}
+
+static void auda2dp_in_get_dsp_frames(struct audio_a2dp_in *audio)
+{
+ struct audio_frame *frame;
+ uint32_t index;
+ unsigned long flags;
+
+ index = audio->in_head;
+
+ frame = (void *) (((char *)audio->in[index].data) - \
+ sizeof(*frame));
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (audio->enc_type == ENC_TYPE_WAV)
+ audio->in[index].size = frame->a2dp.wav.frame_length;
+ else if (audio->enc_type == ENC_TYPE_SBC) {
+ audio->in[index].size = frame->a2dp.sbc.frame_length *
+ frame->a2dp.sbc.frame_num;
+ audio->in[index].frame_num = frame->a2dp.sbc.frame_num;
+ audio->in[index].frame_len = frame->a2dp.sbc.frame_length;
+ }
+
+ /* statistics of read */
+ atomic_add(audio->in[index].size, &audio->in_bytes);
+ atomic_add(1, &audio->in_samples);
+
+ audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1);
+
+ /* If overflow, move the tail index foward. */
+ if (audio->in_head == audio->in_tail)
+ audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1);
+ else
+ audio->in_count++;
+
+ auda2dp_dsp_read_buffer(audio, audio->dsp_cnt++);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ wake_up(&audio->wait);
+}
+
+static struct msm_adsp_ops audrec_adsp_ops = {
+ .event = audrec_dsp_event,
+};
+
+static int auda2dp_in_enc_config(struct audio_a2dp_in *audio, int enable)
+{
+ struct audpreproc_audrec_cmd_enc_cfg cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ENC_CFG_2;
+ cmd.stream_id = audio->enc_id;
+
+ if (enable)
+ cmd.audrec_enc_type = audio->enc_type | ENCODE_ENABLE;
+ else
+ cmd.audrec_enc_type &= ~(ENCODE_ENABLE);
+
+ return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+static int auda2dp_in_param_config(struct audio_a2dp_in *audio)
+{
+ if (audio->enc_type == ENC_TYPE_WAV) {
+ struct audpreproc_audrec_cmd_parm_cfg_wav cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.cmd_id = AUDPREPROC_AUDREC_CMD_PARAM_CFG;
+ cmd.common.stream_id = audio->enc_id;
+
+ cmd.aud_rec_samplerate_idx = audio->samp_rate;
+ cmd.aud_rec_stereo_mode = audio->channel_mode;
+ return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+ } else if (audio->enc_type == ENC_TYPE_SBC) {
+ struct audpreproc_audrec_cmd_parm_cfg_sbc cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.cmd_id = AUDPREPROC_AUDREC_CMD_PARAM_CFG;
+ cmd.common.stream_id = audio->enc_id;
+ cmd.aud_rec_sbc_enc_param =
+ (audio->cfg.number_of_blocks <<
+ AUDREC_SBC_ENC_PARAM_NUM_SUB_BLOCKS_MASK) |
+ (audio->cfg.number_of_subbands <<
+ AUDREC_SBC_ENC_PARAM_NUM_SUB_BANDS_MASK) |
+ (audio->cfg.mode <<
+ AUDREC_SBC_ENC_PARAM_MODE_MASK) |
+ (audio->cfg.bit_allocation <<
+ AUDREC_SBC_ENC_PARAM_BIT_ALLOC_MASK);
+ cmd.aud_rec_sbc_bit_rate_msw =
+ (audio->cfg.bit_rate & 0xFFFF0000) >> 16;
+ cmd.aud_rec_sbc_bit_rate_lsw =
+ (audio->cfg.bit_rate & 0xFFFF);
+ return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+ }
+ return 0;
+}
+
+/* To Do: msm_snddev_route_enc(audio->enc_id); */
+static int auda2dp_in_record_config(struct audio_a2dp_in *audio, int enable)
+{
+ struct audpreproc_afe_cmd_audio_record_cfg cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG;
+ cmd.stream_id = audio->enc_id;
+ if (enable)
+ cmd.destination_activity = AUDIO_RECORDING_TURN_ON;
+ else
+ cmd.destination_activity = AUDIO_RECORDING_TURN_OFF;
+
+ cmd.source_mix_mask = audio->source;
+ if (audio->enc_id == 2) {
+ if ((cmd.source_mix_mask &
+ INTERNAL_CODEC_TX_SOURCE_MIX_MASK) ||
+ (cmd.source_mix_mask & AUX_CODEC_TX_SOURCE_MIX_MASK) ||
+ (cmd.source_mix_mask & VOICE_UL_SOURCE_MIX_MASK) ||
+ (cmd.source_mix_mask & VOICE_DL_SOURCE_MIX_MASK)) {
+ cmd.pipe_id = SOURCE_PIPE_1;
+ }
+ if (cmd.source_mix_mask &
+ AUDPP_A2DP_PIPE_SOURCE_MIX_MASK)
+ cmd.pipe_id |= SOURCE_PIPE_0;
+ }
+ return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+static int auda2dp_in_mem_config(struct audio_a2dp_in *audio)
+{
+ struct audrec_cmd_arecmem_cfg cmd;
+ uint16_t *data = (void *) audio->data;
+ int n;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDREC_CMD_MEM_CFG_CMD;
+ cmd.audrec_up_pkt_intm_count = 1;
+ cmd.audrec_ext_pkt_start_addr_msw = audio->phys >> 16;
+ cmd.audrec_ext_pkt_start_addr_lsw = audio->phys;
+ cmd.audrec_ext_pkt_buf_number = FRAME_NUM;
+
+ /* prepare buffer pointers:
+ * Wav:
+ * Mono: 1024 samples + 4 halfword header
+ * Stereo: 2048 samples + 4 halfword header
+ * SBC:
+ * 768 + 4 halfword header
+ */
+ if (audio->enc_type == ENC_TYPE_SBC) {
+ for (n = 0; n < FRAME_NUM; n++) {
+ audio->in[n].data = data + 4;
+ data += (4 + (FRAME_SIZE_SBC/2));
+ MM_DBG("0x%8x\n", (int)(audio->in[n].data - 8));
+ }
+ } else if (audio->enc_type == ENC_TYPE_WAV) {
+ for (n = 0; n < FRAME_NUM; n++) {
+ audio->in[n].data = data + 4;
+ data += (4 + (audio->channel_mode ? 2048 : 1024));
+ MM_DBG("0x%8x\n", (int)(audio->in[n].data - 8));
+ }
+ }
+
+ return audrec_send_audrecqueue(audio, &cmd, sizeof(cmd));
+}
+
+static int auda2dp_dsp_read_buffer(struct audio_a2dp_in *audio,
+ uint32_t read_cnt)
+{
+ struct up_audrec_packet_ext_ptr cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = UP_AUDREC_PACKET_EXT_PTR;
+ cmd.audrec_up_curr_read_count_msw = read_cnt >> 16;
+ cmd.audrec_up_curr_read_count_lsw = read_cnt;
+
+ return audrec_send_bitstreamqueue(audio, &cmd, sizeof(cmd));
+}
+
+/* must be called with audio->lock held */
+static int auda2dp_in_enable(struct audio_a2dp_in *audio)
+{
+ if (audio->enabled)
+ return 0;
+
+ if (audpreproc_enable(audio->enc_id, &audpreproc_dsp_event, audio)) {
+ MM_ERR("msm_adsp_enable(audpreproc) failed\n");
+ return -ENODEV;
+ }
+
+ if (msm_adsp_enable(audio->audrec)) {
+ MM_ERR("msm_adsp_enable(audrec) failed\n");
+ audpreproc_disable(audio->enc_id, audio);
+ return -ENODEV;
+ }
+ audio->enabled = 1;
+ auda2dp_in_enc_config(audio, 1);
+
+ return 0;
+}
+
+/* must be called with audio->lock held */
+static int auda2dp_in_disable(struct audio_a2dp_in *audio)
+{
+ if (audio->enabled) {
+ audio->enabled = 0;
+ auda2dp_in_enc_config(audio, 0);
+ wake_up(&audio->wait);
+ wait_event_interruptible_timeout(audio->wait_enable,
+ audio->running == 0, 1*HZ);
+ msm_adsp_disable(audio->audrec);
+ audpreproc_disable(audio->enc_id, audio);
+ }
+ return 0;
+}
+
+static void auda2dp_in_flush(struct audio_a2dp_in *audio)
+{
+ int i;
+
+ audio->dsp_cnt = 0;
+ audio->in_head = 0;
+ audio->in_tail = 0;
+ audio->in_count = 0;
+ for (i = 0; i < FRAME_NUM; i++) {
+ audio->in[i].size = 0;
+ audio->in[i].read = 0;
+ }
+ MM_DBG("in_bytes %d\n", atomic_read(&audio->in_bytes));
+ MM_DBG("in_samples %d\n", atomic_read(&audio->in_samples));
+ atomic_set(&audio->in_bytes, 0);
+ atomic_set(&audio->in_samples, 0);
+}
+
+/* ------------------- device --------------------- */
+static long auda2dp_in_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct audio_a2dp_in *audio = file->private_data;
+ int rc = 0;
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ stats.byte_count = atomic_read(&audio->in_bytes);
+ stats.sample_count = atomic_read(&audio->in_samples);
+ if (copy_to_user((void *) arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return rc;
+ }
+
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START: {
+ uint32_t freq;
+ /* Poll at 48KHz always */
+ freq = 48000;
+ MM_DBG("AUDIO_START\n");
+ rc = msm_snddev_request_freq(&freq, audio->enc_id,
+ SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+ MM_DBG("sample rate configured %d sample rate requested %d\n",
+ freq, audio->samp_rate);
+ if (rc < 0) {
+ MM_DBG("sample rate can not be set, return code %d\n",\
+ rc);
+ msm_snddev_withdraw_freq(audio->enc_id,
+ SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+ MM_DBG("msm_snddev_withdraw_freq\n");
+ break;
+ }
+ /*update aurec session info in audpreproc layer*/
+ audio->session_info.session_id = audio->enc_id;
+ audio->session_info.sampling_freq = audio->samp_rate;
+ audpreproc_update_audrec_info(&audio->session_info);
+ rc = auda2dp_in_enable(audio);
+ if (!rc) {
+ rc =
+ wait_event_interruptible_timeout(audio->wait_enable,
+ audio->running != 0, 1*HZ);
+ MM_DBG("state %d rc = %d\n", audio->running, rc);
+
+ if (audio->running == 0) {
+ rc = -ENODEV;
+ msm_snddev_withdraw_freq(audio->enc_id,
+ SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+ MM_DBG("msm_snddev_withdraw_freq\n");
+ } else
+ rc = 0;
+ }
+ audio->stopped = 0;
+ break;
+ }
+ case AUDIO_STOP: {
+ /*reset the sampling frequency information at audpreproc layer*/
+ audio->session_info.sampling_freq = 0;
+ audpreproc_update_audrec_info(&audio->session_info);
+ rc = auda2dp_in_disable(audio);
+ rc = msm_snddev_withdraw_freq(audio->enc_id,
+ SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+ MM_DBG("msm_snddev_withdraw_freq\n");
+ audio->stopped = 1;
+ audio->abort = 0;
+ break;
+ }
+ case AUDIO_FLUSH: {
+ if (audio->stopped) {
+ /* Make sure we're stopped and we wake any threads
+ * that might be blocked holding the read_lock.
+ * While audio->stopped read threads will always
+ * exit immediately.
+ */
+ wake_up(&audio->wait);
+ mutex_lock(&audio->read_lock);
+ auda2dp_in_flush(audio);
+ mutex_unlock(&audio->read_lock);
+ }
+ break;
+ }
+ case AUDIO_SET_STREAM_CONFIG: {
+ struct msm_audio_stream_config cfg;
+ if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) {
+ rc = -EFAULT;
+ break;
+ }
+ /* Allow only single frame */
+ if ((audio->enc_type == ENC_TYPE_SBC) &&
+ (cfg.buffer_size != FRAME_SIZE_SBC))
+ rc = -EINVAL;
+ else
+ audio->buffer_size = cfg.buffer_size;
+ break;
+ }
+ case AUDIO_GET_STREAM_CONFIG: {
+ struct msm_audio_stream_config cfg;
+ memset(&cfg, 0, sizeof(cfg));
+ if (audio->enc_type == ENC_TYPE_SBC)
+ cfg.buffer_size = FRAME_SIZE_SBC;
+ else
+ cfg.buffer_size = MONO_DATA_SIZE;
+ cfg.buffer_count = FRAME_NUM;
+ if (copy_to_user((void *) arg, &cfg, sizeof(cfg)))
+ rc = -EFAULT;
+ break;
+ }
+ case AUDIO_SET_SBC_ENC_CONFIG: {
+ if (copy_from_user(&audio->cfg, (void *) arg,
+ sizeof(audio->cfg))) {
+ rc = -EFAULT;
+ break;
+ }
+ audio->samp_rate = audio->cfg.sample_rate;
+ audio->channel_mode = audio->cfg.channels;
+ audio->enc_type = ENC_TYPE_SBC;
+ break;
+ }
+ case AUDIO_SET_CONFIG: {
+ struct msm_audio_config cfg;
+ if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (cfg.channel_count == 1) {
+ cfg.channel_count = AUDREC_CMD_MODE_MONO;
+ audio->buffer_size = MONO_DATA_SIZE;
+ } else if (cfg.channel_count == 2) {
+ cfg.channel_count = AUDREC_CMD_MODE_STEREO;
+ audio->buffer_size = STEREO_DATA_SIZE;
+ } else {
+ rc = -EINVAL;
+ break;
+ }
+ audio->samp_rate = cfg.sample_rate;
+ audio->channel_mode = cfg.channel_count;
+ audio->enc_type = ENC_TYPE_WAV;
+ break;
+ }
+ case AUDIO_GET_SBC_ENC_CONFIG: {
+ struct msm_audio_sbc_enc_config cfg;
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.bit_allocation = audio->cfg.bit_allocation;
+ cfg.mode = audio->cfg.mode;
+ cfg.number_of_subbands = audio->cfg.number_of_subbands;
+ cfg.number_of_blocks = audio->cfg.number_of_blocks;
+ cfg.sample_rate = audio->samp_rate;
+ cfg.channels = audio->channel_mode;
+ cfg.bit_rate = audio->cfg.bit_rate;
+ if (copy_to_user((void *) arg, &cfg, sizeof(cfg)))
+ rc = -EFAULT;
+ break;
+ }
+ case AUDIO_GET_CONFIG: {
+ struct msm_audio_config cfg;
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.buffer_count = FRAME_NUM;
+ cfg.sample_rate = audio->samp_rate;
+ if (audio->channel_mode == AUDREC_CMD_MODE_MONO) {
+ cfg.channel_count = 1;
+ cfg.buffer_size = MONO_DATA_SIZE;
+ } else {
+ cfg.channel_count = 2;
+ cfg.buffer_size = STEREO_DATA_SIZE;
+ }
+ cfg.type = ENC_TYPE_WAV;
+ if (copy_to_user((void *) arg, &cfg, sizeof(cfg)))
+ rc = -EFAULT;
+ break;
+ }
+ case AUDIO_GET_SESSION_ID: {
+ if (copy_to_user((void *) arg, &audio->enc_id,
+ sizeof(unsigned short))) {
+ rc = -EFAULT;
+ }
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static ssize_t auda2dp_in_read(struct file *file,
+ char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio_a2dp_in *audio = file->private_data;
+ unsigned long flags;
+ const char __user *start = buf;
+ void *data;
+ uint32_t index;
+ uint32_t size;
+ int rc = 0;
+ uint32_t f_len = 0, f_num = 0;
+ int i = 0;
+
+ mutex_lock(&audio->read_lock);
+ while (count > 0) {
+ rc = wait_event_interruptible(
+ audio->wait, (audio->in_count > 0) || audio->stopped ||
+ audio->abort);
+
+ if (rc < 0)
+ break;
+
+ if (audio->stopped && !audio->in_count) {
+ MM_DBG("Driver in stop state, No more buffer to read");
+ rc = 0;/* End of File */
+ break;
+ }
+
+ if (audio->abort) {
+ rc = -EPERM; /* Not permitted due to abort */
+ break;
+ }
+
+ index = audio->in_tail;
+ data = (uint8_t *) audio->in[index].data;
+ size = audio->in[index].size;
+ if (count >= size) {
+ if (audio->enc_type == ENC_TYPE_SBC &&
+ (audio->in[index].frame_len % 2)) {
+ f_len = audio->in[index].frame_len;
+ f_num = audio->in[index].frame_num;
+ for (i = 0; i < f_num; i++) {
+ if (copy_to_user(&buf[i * f_len],
+ (uint8_t *) (data + (i * (f_len + 1))),
+ f_len)) {
+ rc = -EFAULT;
+ break;
+ }
+ }
+ } else {
+ if (copy_to_user(buf, data, size)) {
+ rc = -EFAULT;
+ break;
+ }
+ }
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (index != audio->in_tail) {
+ /* overrun -- data is
+ * invalid and we need to retry */
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ continue;
+ }
+ audio->in[index].size = 0;
+ audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1);
+ audio->in_count--;
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ count -= size;
+ buf += size;
+ } else {
+ MM_ERR("short read\n");
+ break;
+ }
+ }
+ mutex_unlock(&audio->read_lock);
+ if (buf > start)
+ return buf - start;
+
+ return rc;
+}
+
+static ssize_t auda2dp_in_write(struct file *file,
+ const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ return -EINVAL;
+}
+
+static int auda2dp_in_release(struct inode *inode, struct file *file)
+{
+ struct audio_a2dp_in *audio = file->private_data;
+
+ mutex_lock(&audio->lock);
+ /* with draw frequency for session
+ incase not stopped the driver */
+ msm_snddev_withdraw_freq(audio->enc_id, SNDDEV_CAP_TX,
+ AUDDEV_CLNT_ENC);
+ auddev_unregister_evt_listner(AUDDEV_CLNT_ENC, audio->enc_id);
+ /*reset the sampling frequency information at audpreproc layer*/
+ audio->session_info.sampling_freq = 0;
+ audpreproc_update_audrec_info(&audio->session_info);
+ auda2dp_in_disable(audio);
+ auda2dp_in_flush(audio);
+ msm_adsp_put(audio->audrec);
+ audpreproc_aenc_free(audio->enc_id);
+ audio->audrec = NULL;
+ audio->opened = 0;
+ if (audio->data) {
+ iounmap(audio->data);
+ pmem_kfree(audio->phys);
+ audio->data = NULL;
+ }
+ mutex_unlock(&audio->lock);
+ return 0;
+}
+
+static int auda2dp_in_open(struct inode *inode, struct file *file)
+{
+ struct audio_a2dp_in *audio = &the_audio_a2dp_in;
+ int rc;
+ int encid;
+
+ mutex_lock(&audio->lock);
+ if (audio->opened) {
+ rc = -EBUSY;
+ goto done;
+ }
+
+ audio->phys = pmem_kalloc(DMASZ, PMEM_MEMTYPE_EBI1|
+ PMEM_ALIGNMENT_4K);
+ if (!IS_ERR((void *)audio->phys)) {
+ audio->data = ioremap(audio->phys, DMASZ);
+ if (!audio->data) {
+ MM_ERR("could not allocate DMA buffers\n");
+ rc = -ENOMEM;
+ pmem_kfree(audio->phys);
+ goto done;
+ }
+ } else {
+ MM_ERR("could not allocate DMA buffers\n");
+ rc = -ENOMEM;
+ goto done;
+ }
+ MM_DBG("Memory addr = 0x%8x phy addr = 0x%8x\n",\
+ (int) audio->data, (int) audio->phys);
+
+ if ((file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ rc = -EACCES;
+ MM_ERR("Non tunnel encoding is not supported\n");
+ goto done;
+ } else if (!(file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ audio->mode = MSM_AUD_ENC_MODE_TUNNEL;
+ MM_DBG("Opened for Tunnel mode encoding\n");
+ } else {
+ rc = -EACCES;
+ goto done;
+ }
+ /* Settings will be re-config at AUDIO_SET_CONFIG/SBC_ENC_CONFIG,
+ * but at least we need to have initial config
+ */
+ audio->channel_mode = AUDREC_CMD_MODE_MONO;
+ audio->buffer_size = FRAME_SIZE_SBC;
+ audio->samp_rate = 48000;
+ audio->enc_type = ENC_TYPE_SBC | audio->mode;
+ audio->cfg.bit_allocation = AUDIO_SBC_BA_SNR;
+ audio->cfg.mode = AUDIO_SBC_MODE_JSTEREO;
+ audio->cfg.number_of_subbands = AUDIO_SBC_BANDS_8;
+ audio->cfg.number_of_blocks = AUDIO_SBC_BLOCKS_16;
+ audio->cfg.bit_rate = 320000; /* max 512kbps(mono), 320kbs(others) */
+
+ encid = audpreproc_aenc_alloc(audio->enc_type, &audio->module_name,
+ &audio->queue_ids);
+ if (encid < 0) {
+ MM_ERR("No free encoder available\n");
+ rc = -ENODEV;
+ goto done;
+ }
+ audio->enc_id = encid;
+
+ rc = msm_adsp_get(audio->module_name, &audio->audrec,
+ &audrec_adsp_ops, audio);
+
+ if (rc) {
+ audpreproc_aenc_free(audio->enc_id);
+ goto done;
+ }
+
+ audio->stopped = 0;
+ audio->source = 0;
+ audio->abort = 0;
+ auda2dp_in_flush(audio);
+ audio->device_events = AUDDEV_EVT_DEV_RDY | AUDDEV_EVT_DEV_RLS |
+ AUDDEV_EVT_FREQ_CHG;
+
+ rc = auddev_register_evt_listner(audio->device_events,
+ AUDDEV_CLNT_ENC, audio->enc_id,
+ a2dp_in_listener, (void *) audio);
+ if (rc) {
+ MM_ERR("failed to register device event listener\n");
+ goto evt_error;
+ }
+ file->private_data = audio;
+ audio->opened = 1;
+ rc = 0;
+done:
+ mutex_unlock(&audio->lock);
+ return rc;
+evt_error:
+ msm_adsp_put(audio->audrec);
+ audpreproc_aenc_free(audio->enc_id);
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static const struct file_operations audio_a2dp_in_fops = {
+ .owner = THIS_MODULE,
+ .open = auda2dp_in_open,
+ .release = auda2dp_in_release,
+ .read = auda2dp_in_read,
+ .write = auda2dp_in_write,
+ .unlocked_ioctl = auda2dp_in_ioctl,
+};
+
+struct miscdevice audio_a2dp_in_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_a2dp_in",
+ .fops = &audio_a2dp_in_fops,
+};
+
+static int __init auda2dp_in_init(void)
+{
+ mutex_init(&the_audio_a2dp_in.lock);
+ mutex_init(&the_audio_a2dp_in.read_lock);
+ spin_lock_init(&the_audio_a2dp_in.dsp_lock);
+ spin_lock_init(&the_audio_a2dp_in.dev_lock);
+ init_waitqueue_head(&the_audio_a2dp_in.wait);
+ init_waitqueue_head(&the_audio_a2dp_in.wait_enable);
+ return misc_register(&audio_a2dp_in_misc);
+}
+
+device_initcall(auda2dp_in_init);
+
+MODULE_DESCRIPTION("MSM SBC encode driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_aac.c b/arch/arm/mach-msm/qdsp5v2/audio_aac.c
new file mode 100644
index 0000000..d75dc92
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_aac.c
@@ -0,0 +1,2025 @@
+/*
+ * aac audio decoder device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/earlysuspend.h>
+#include <linux/android_pmem.h>
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <linux/slab.h>
+#include <mach/msm_adsp.h>
+#include <linux/msm_audio_aac.h>
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/qdsp5audplaycmdi.h>
+#include <mach/qdsp5v2/qdsp5audplaymsg.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/qdsp5v2/audpp.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/debug_mm.h>
+
+#define BUFSZ 32768
+#define DMASZ (BUFSZ * 2)
+#define BUFSZ_MIN 4096
+#define DMASZ_MIN (BUFSZ_MIN * 2)
+
+#define AUDPLAY_INVALID_READ_PTR_OFFSET 0xFFFF
+#define AUDDEC_DEC_AAC 5
+
+#define PCM_BUFSZ_MIN 9600 /* Hold one stereo AAC frame */
+#define PCM_BUF_MAX_COUNT 5 /* DSP only accepts 5 buffers at most
+ but support 2 buffers currently */
+#define ROUTING_MODE_FTRT 1
+#define ROUTING_MODE_RT 2
+/* Decoder status received from AUDPPTASK */
+#define AUDPP_DEC_STATUS_SLEEP 0
+#define AUDPP_DEC_STATUS_INIT 1
+#define AUDPP_DEC_STATUS_CFG 2
+#define AUDPP_DEC_STATUS_PLAY 3
+
+#define AUDAAC_METAFIELD_MASK 0xFFFF0000
+#define AUDAAC_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */
+#define AUDAAC_EOS_FLG_MASK 0x01
+#define AUDAAC_EOS_NONE 0x0 /* No EOS detected */
+#define AUDAAC_EOS_SET 0x1 /* EOS set in meta field */
+
+#define AUDAAC_EVENT_NUM 10 /* Default number of pre-allocated event packets */
+
+#define BITSTREAM_ERROR_THRESHOLD_VALUE 0x1 /* DEFAULT THRESHOLD VALUE */
+
+struct buffer {
+ void *data;
+ unsigned size;
+ unsigned used; /* Input usage actual DSP produced PCM size */
+ unsigned addr;
+ unsigned short mfield_sz; /*only useful for data has meta field */
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+struct audaac_suspend_ctl {
+ struct early_suspend node;
+ struct audio *audio;
+};
+#endif
+
+struct audaac_event{
+ struct list_head list;
+ int event_type;
+ union msm_audio_event_payload payload;
+};
+
+struct audio {
+ struct buffer out[2];
+
+ spinlock_t dsp_lock;
+
+ uint8_t out_head;
+ uint8_t out_tail;
+ uint8_t out_needed; /* number of buffers the dsp is waiting for */
+ unsigned out_dma_sz;
+
+ atomic_t out_bytes;
+
+ struct mutex lock;
+ struct mutex write_lock;
+ wait_queue_head_t write_wait;
+
+ /* Host PCM section */
+ struct buffer in[PCM_BUF_MAX_COUNT];
+ struct mutex read_lock;
+ wait_queue_head_t read_wait; /* Wait queue for read */
+ char *read_data; /* pointer to reader buffer */
+ int32_t read_phys; /* physical address of reader buffer */
+ uint8_t read_next; /* index to input buffers to be read next */
+ uint8_t fill_next; /* index to buffer that DSP should be filling */
+ uint8_t pcm_buf_count; /* number of pcm buffer allocated */
+ /* ---- End of Host PCM section */
+
+ struct msm_adsp_module *audplay;
+
+ /* configuration to use on next enable */
+ uint32_t out_sample_rate;
+ uint32_t out_channel_mode;
+ struct msm_audio_aac_config aac_config;
+
+ /* AV sync Info */
+ int avsync_flag; /* Flag to indicate feedback from DSP */
+ wait_queue_head_t avsync_wait;/* Wait queue for AV Sync Message */
+ /* 48 bits sample/bytes counter per channel */
+ uint16_t avsync[AUDPP_AVSYNC_CH_COUNT * AUDPP_AVSYNC_NUM_WORDS + 1];
+
+ /* data allocated for various buffers */
+ char *data;
+ int32_t phys; /* physical address of write buffer */
+
+ int mfield; /* meta field embedded in data */
+ int rflush; /* Read flush */
+ int wflush; /* Write flush */
+ int opened;
+ int enabled;
+ int running;
+ int stopped; /* set when stopped, cleared on flush */
+ int pcm_feedback;
+ int buf_refresh;
+ int teos; /* valid only if tunnel mode & no data left for decoder */
+ enum msm_aud_decoder_state dec_state; /* Represents decoder state */
+ int reserved; /* A byte is being reserved */
+ char rsv_byte; /* Handle odd length user data */
+
+ const char *module_name;
+ unsigned queue_id;
+ uint16_t dec_id;
+ uint32_t read_ptr_offset;
+ int16_t source;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct audaac_suspend_ctl suspend_ctl;
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dentry;
+#endif
+
+ wait_queue_head_t wait;
+ struct list_head free_event_queue;
+ struct list_head event_queue;
+ wait_queue_head_t event_wait;
+ spinlock_t event_queue_lock;
+ struct mutex get_event_lock;
+ int event_abort;
+ uint32_t device_events;
+
+ struct msm_audio_bitstream_info stream_info;
+ struct msm_audio_bitstream_error_info bitstream_error_info;
+ uint32_t bitstream_error_threshold_value;
+
+ int eq_enable;
+ int eq_needs_commit;
+ struct audpp_cmd_cfg_object_params_eqalizer eq;
+ struct audpp_cmd_cfg_object_params_volume vol_pan;
+};
+
+static int auddec_dsp_config(struct audio *audio, int enable);
+static void audpp_cmd_cfg_adec_params(struct audio *audio);
+static void audpp_cmd_cfg_routing_mode(struct audio *audio);
+static void audplay_send_data(struct audio *audio, unsigned needed);
+static void audplay_error_threshold_config(struct audio *audio);
+static void audplay_config_hostpcm(struct audio *audio);
+static void audplay_buffer_refresh(struct audio *audio);
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg);
+static void audaac_post_event(struct audio *audio, int type,
+ union msm_audio_event_payload payload);
+
+/* must be called with audio->lock held */
+static int audio_enable(struct audio *audio)
+{
+ MM_DBG("\n"); /* Macro prints the file name and function */
+
+ if (audio->enabled)
+ return 0;
+
+ audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+ audio->out_tail = 0;
+ audio->out_needed = 0;
+
+ if (msm_adsp_enable(audio->audplay)) {
+ MM_ERR("msm_adsp_enable(audplay) failed\n");
+ return -ENODEV;
+ }
+
+ if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) {
+ MM_ERR("audpp_enable() failed\n");
+ msm_adsp_disable(audio->audplay);
+ return -ENODEV;
+ }
+ audio->enabled = 1;
+ return 0;
+}
+
+static void aac_listner(u32 evt_id, union auddev_evt_data *evt_payload,
+ void *private_data)
+{
+ struct audio *audio = (struct audio *) private_data;
+ switch (evt_id) {
+ case AUDDEV_EVT_DEV_RDY:
+ MM_DBG(":AUDDEV_EVT_DEV_RDY\n");
+ audio->source |= (0x1 << evt_payload->routing_id);
+ if (audio->running == 1 && audio->enabled == 1)
+ audpp_route_stream(audio->dec_id, audio->source);
+ break;
+ case AUDDEV_EVT_DEV_RLS:
+ MM_DBG(":AUDDEV_EVT_DEV_RLS\n");
+ audio->source &= ~(0x1 << evt_payload->routing_id);
+ if (audio->running == 1 && audio->enabled == 1)
+ audpp_route_stream(audio->dec_id, audio->source);
+ break;
+ case AUDDEV_EVT_STREAM_VOL_CHG:
+ audio->vol_pan.volume = evt_payload->session_vol;
+ MM_DBG(":AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n",
+ audio->vol_pan.volume);
+ if (audio->running)
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ break;
+ default:
+ MM_ERR(":ERROR:wrong event\n");
+ break;
+ }
+}
+/* must be called with audio->lock held */
+static int audio_disable(struct audio *audio)
+{
+ int rc = 0;
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ if (audio->enabled) {
+ audio->enabled = 0;
+ audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+ auddec_dsp_config(audio, 0);
+ rc = wait_event_interruptible_timeout(audio->wait,
+ audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+ msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE)
+ rc = -EFAULT;
+ else
+ rc = 0;
+ wake_up(&audio->write_wait);
+ wake_up(&audio->read_wait);
+ msm_adsp_disable(audio->audplay);
+ audpp_disable(audio->dec_id, audio);
+ audio->out_needed = 0;
+ }
+ return rc;
+}
+
+/* ------------------- dsp --------------------- */
+static void audio_update_pcm_buf_entry(struct audio *audio, uint32_t *payload)
+{
+ uint8_t index;
+ unsigned long flags;
+
+ if (audio->rflush)
+ return;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ for (index = 0; index < payload[1]; index++) {
+ if (audio->in[audio->fill_next].addr ==
+ payload[2 + index * 2]) {
+ MM_DBG("in[%d] ready\n", audio->fill_next);
+ audio->in[audio->fill_next].used =
+ payload[3 + index * 2];
+ if ((++audio->fill_next) == audio->pcm_buf_count)
+ audio->fill_next = 0;
+
+ } else {
+ MM_ERR("expected=%x ret=%x\n",
+ audio->in[audio->fill_next].addr,
+ payload[1 + index * 2]);
+ break;
+ }
+ }
+ if (audio->in[audio->fill_next].used == 0) {
+ audplay_buffer_refresh(audio);
+ } else {
+ MM_DBG("read cannot keep up\n");
+ audio->buf_refresh = 1;
+ }
+ wake_up(&audio->read_wait);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+
+}
+
+static void audaac_bitstream_error_info(struct audio *audio, uint32_t *payload)
+{
+ unsigned long flags;
+ union msm_audio_event_payload e_payload;
+
+ if (payload[0] != AUDDEC_DEC_AAC) {
+ MM_ERR("Unexpected bitstream error info from DSP:\
+ Invalid decoder\n");
+ return;
+ }
+
+ /* get stream info from DSP msg */
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+
+ audio->bitstream_error_info.dec_id = payload[0];
+ audio->bitstream_error_info.err_msg_indicator = payload[1];
+ audio->bitstream_error_info.err_type = payload[2];
+
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ MM_ERR("bit_stream_error_type=%d error_count=%d\n",
+ audio->bitstream_error_info.err_type, (0x0000FFFF &
+ audio->bitstream_error_info.err_msg_indicator));
+
+ /* send event to ARM to notify error info coming */
+ e_payload.error_info = audio->bitstream_error_info;
+ audaac_post_event(audio, AUDIO_EVENT_BITSTREAM_ERROR_INFO, e_payload);
+}
+
+static void audaac_update_stream_info(struct audio *audio, uint32_t *payload)
+{
+ unsigned long flags;
+ union msm_audio_event_payload e_payload;
+
+ /* get stream info from DSP msg */
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+
+ audio->stream_info.codec_type = AUDIO_CODEC_TYPE_AAC;
+ audio->stream_info.chan_info = (0x0000FFFF & payload[1]);
+ audio->stream_info.sample_rate = (0x0000FFFF & payload[2]);
+ audio->stream_info.bit_stream_info = (0x0000FFFF & payload[3]);
+ audio->stream_info.bit_rate = payload[4];
+
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ MM_DBG("chan_info=%d, sample_rate=%d, bit_stream_info=%d\n",
+ audio->stream_info.chan_info,
+ audio->stream_info.sample_rate,
+ audio->stream_info.bit_stream_info);
+
+ /* send event to ARM to notify steam info coming */
+ e_payload.stream_info = audio->stream_info;
+ audaac_post_event(audio, AUDIO_EVENT_STREAM_INFO, e_payload);
+}
+static void audplay_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent) (void *ptr, size_t len))
+{
+ struct audio *audio = data;
+ uint32_t msg[28];
+ getevent(msg, sizeof(msg));
+
+ MM_DBG("msg_id=%x\n", id);
+
+ switch (id) {
+ case AUDPLAY_MSG_DEC_NEEDS_DATA:
+ audplay_send_data(audio, 1);
+ break;
+
+ case AUDPLAY_MSG_BUFFER_UPDATE:
+ audio_update_pcm_buf_entry(audio, msg);
+ break;
+
+ case AUDPLAY_UP_STREAM_INFO:
+ if ((msg[1] & AUDPLAY_STREAM_INFO_MSG_MASK) ==
+ AUDPLAY_STREAM_INFO_MSG_MASK) {
+ audaac_bitstream_error_info(audio, msg);
+ } else {
+ audaac_update_stream_info(audio, msg);
+ }
+ break;
+
+ case AUDPLAY_UP_OUTPORT_FLUSH_ACK:
+ MM_DBG("OUTPORT_FLUSH_ACK\n");
+ audio->rflush = 0;
+ wake_up(&audio->read_wait);
+ if (audio->pcm_feedback)
+ audplay_buffer_refresh(audio);
+ break;
+
+ case ADSP_MESSAGE_ID:
+ MM_DBG("Received ADSP event: module enable(audplaytask)\n");
+ break;
+
+ default:
+ MM_ERR("unexpected message from decoder \n");
+ }
+}
+
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg)
+{
+ struct audio *audio = private;
+
+ switch (id) {
+ case AUDPP_MSG_STATUS_MSG:{
+ unsigned status = msg[1];
+
+ switch (status) {
+ case AUDPP_DEC_STATUS_SLEEP: {
+ uint16_t reason = msg[2];
+ MM_DBG("decoder status: sleep reason = \
+ 0x%04x\n", reason);
+ if ((reason == AUDPP_MSG_REASON_MEM)
+ || (reason ==
+ AUDPP_MSG_REASON_NODECODER)) {
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_FAILURE;
+ wake_up(&audio->wait);
+ } else if (reason == AUDPP_MSG_REASON_NONE) {
+ /* decoder is in disable state */
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_CLOSE;
+ wake_up(&audio->wait);
+ }
+ break;
+ }
+ case AUDPP_DEC_STATUS_INIT:
+ MM_DBG("decoder status: init \n");
+ if (audio->pcm_feedback)
+ audpp_cmd_cfg_routing_mode(audio);
+ else
+ audpp_cmd_cfg_adec_params(audio);
+ break;
+
+ case AUDPP_DEC_STATUS_CFG:
+ MM_DBG("decoder status: cfg \n");
+ break;
+ case AUDPP_DEC_STATUS_PLAY:
+ MM_DBG("decoder status: play \n");
+ /* send mixer command */
+ audpp_route_stream(audio->dec_id,
+ audio->source);
+ if (audio->pcm_feedback) {
+ audplay_error_threshold_config(audio);
+ audplay_config_hostpcm(audio);
+ audplay_buffer_refresh(audio);
+ }
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_SUCCESS;
+ wake_up(&audio->wait);
+ break;
+ default:
+ MM_ERR("unknown decoder status \n");
+ }
+ break;
+ }
+ case AUDPP_MSG_CFG_MSG:
+ if (msg[0] == AUDPP_MSG_ENA_ENA) {
+ MM_DBG("CFG_MSG ENABLE\n");
+ auddec_dsp_config(audio, 1);
+ audio->out_needed = 0;
+ audio->running = 1;
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ audpp_dsp_set_eq(audio->dec_id, audio->eq_enable,
+ &audio->eq, POPP);
+ } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+ MM_DBG("CFG_MSG DISABLE\n");
+ audio->running = 0;
+ } else {
+ MM_DBG("CFG_MSG %d?\n", msg[0]);
+ }
+ break;
+ case AUDPP_MSG_ROUTING_ACK:
+ MM_DBG("ROUTING_ACK mode=%d\n", msg[1]);
+ audpp_cmd_cfg_adec_params(audio);
+ break;
+
+ case AUDPP_MSG_FLUSH_ACK:
+ MM_DBG("FLUSH_ACK\n");
+ audio->wflush = 0;
+ audio->rflush = 0;
+ wake_up(&audio->write_wait);
+ if (audio->pcm_feedback)
+ audplay_buffer_refresh(audio);
+ break;
+
+ case AUDPP_MSG_PCMDMAMISSED:
+ MM_DBG("PCMDMAMISSED\n");
+ audio->teos = 1;
+ wake_up(&audio->write_wait);
+ break;
+
+ case AUDPP_MSG_AVSYNC_MSG:
+ MM_DBG("AUDPP_MSG_AVSYNC_MSG\n");
+ memcpy(&audio->avsync[0], msg, sizeof(audio->avsync));
+ audio->avsync_flag = 1;
+ wake_up(&audio->avsync_wait);
+ break;
+
+ default:
+ MM_ERR("UNKNOWN (%d)\n", id);
+ }
+
+}
+
+struct msm_adsp_ops audplay_adsp_ops_aac = {
+ .event = audplay_dsp_event,
+};
+
+#define audplay_send_queue0(audio, cmd, len) \
+ msm_adsp_write(audio->audplay, audio->queue_id,\
+ cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+ struct audpp_cmd_cfg_dec_type cfg_dec_cmd;
+
+ memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd));
+
+ cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+ if (enable)
+ cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_AAC;
+ else
+ cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_DIS_DEC_V;
+ cfg_dec_cmd.dm_mode = 0x0;
+ cfg_dec_cmd.stream_id = audio->dec_id;
+
+ return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd));
+}
+
+static void audpp_cmd_cfg_adec_params(struct audio *audio)
+{
+ struct audpp_cmd_cfg_adec_params_aac cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+ cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_AAC_LEN;
+ cmd.common.dec_id = audio->dec_id;
+ cmd.common.input_sampling_frequency = audio->out_sample_rate;
+ cmd.format = audio->aac_config.format;
+ cmd.audio_object = audio->aac_config.audio_object;
+ cmd.ep_config = audio->aac_config.ep_config;
+ cmd.aac_section_data_resilience_flag =
+ audio->aac_config.aac_section_data_resilience_flag;
+ cmd.aac_scalefactor_data_resilience_flag =
+ audio->aac_config.aac_scalefactor_data_resilience_flag;
+ cmd.aac_spectral_data_resilience_flag =
+ audio->aac_config.aac_spectral_data_resilience_flag;
+ cmd.sbr_on_flag = audio->aac_config.sbr_on_flag;
+ cmd.sbr_ps_on_flag = audio->aac_config.sbr_ps_on_flag;
+ cmd.channel_configuration = audio->aac_config.channel_configuration;
+
+ audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_routing_mode(struct audio *audio)
+{
+ struct audpp_cmd_routing_mode cmd;
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_ROUTING_MODE;
+ cmd.object_number = audio->dec_id;
+ if (audio->pcm_feedback)
+ cmd.routing_mode = ROUTING_MODE_FTRT;
+ else
+ cmd.routing_mode = ROUTING_MODE_RT;
+
+ audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static int audplay_dsp_send_data_avail(struct audio *audio,
+ unsigned idx, unsigned len)
+{
+ struct audplay_cmd_bitstream_data_avail_nt2 cmd;
+
+ cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2;
+ if (audio->mfield)
+ cmd.decoder_id = AUDAAC_METAFIELD_MASK |
+ (audio->out[idx].mfield_sz >> 1);
+ else
+ cmd.decoder_id = audio->dec_id;
+ cmd.buf_ptr = audio->out[idx].addr;
+ cmd.buf_size = len / 2;
+ cmd.partition_number = 0;
+ return audplay_send_queue0(audio, &cmd, sizeof(cmd));
+}
+
+static void audplay_buffer_refresh(struct audio *audio)
+{
+ struct audplay_cmd_buffer_refresh refresh_cmd;
+
+ refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH;
+ refresh_cmd.num_buffers = 1;
+ refresh_cmd.buf0_address = audio->in[audio->fill_next].addr;
+ /* AAC frame size */
+ refresh_cmd.buf0_length = audio->in[audio->fill_next].size -
+ (audio->in[audio->fill_next].size % 1024)
+ + (audio->mfield ? 24 : 0);
+ refresh_cmd.buf_read_count = 0;
+ MM_DBG("buf0_addr=%x buf0_len=%d\n", refresh_cmd.buf0_address,
+ refresh_cmd.buf0_length);
+ (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd));
+}
+
+static void audplay_outport_flush(struct audio *audio)
+{
+ struct audplay_cmd_outport_flush op_flush_cmd;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ op_flush_cmd.cmd_id = AUDPLAY_CMD_OUTPORT_FLUSH;
+ (void)audplay_send_queue0(audio, &op_flush_cmd, sizeof(op_flush_cmd));
+}
+
+static void audplay_error_threshold_config(struct audio *audio)
+{
+ union audplay_cmd_channel_info ch_cfg_cmd;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ ch_cfg_cmd.thr_update.cmd_id = AUDPLAY_CMD_CHANNEL_INFO;
+ ch_cfg_cmd.thr_update.threshold_update = AUDPLAY_ERROR_THRESHOLD_ENABLE;
+ ch_cfg_cmd.thr_update.threshold_value =
+ audio->bitstream_error_threshold_value;
+ (void)audplay_send_queue0(audio, &ch_cfg_cmd, sizeof(ch_cfg_cmd));
+}
+
+static void audplay_config_hostpcm(struct audio *audio)
+{
+ struct audplay_cmd_hpcm_buf_cfg cfg_cmd;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG;
+ cfg_cmd.max_buffers = audio->pcm_buf_count;
+ cfg_cmd.byte_swap = 0;
+ cfg_cmd.hostpcm_config = (0x8000) | (0x4000);
+ cfg_cmd.feedback_frequency = 1;
+ cfg_cmd.partition_number = 0;
+ (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd));
+
+}
+
+static void audplay_send_data(struct audio *audio, unsigned needed)
+{
+ struct buffer *frame;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (!audio->running)
+ goto done;
+
+ if (needed && !audio->wflush) {
+ /* We were called from the callback because the DSP
+ * requested more data. Note that the DSP does want
+ * more data, and if a buffer was in-flight, mark it
+ * as available (since the DSP must now be done with
+ * it).
+ */
+ audio->out_needed = 1;
+ frame = audio->out + audio->out_tail;
+ if (frame->used == 0xffffffff) {
+ MM_DBG("frame %d free\n", audio->out_tail);
+ frame->used = 0;
+ audio->out_tail ^= 1;
+ wake_up(&audio->write_wait);
+ }
+ }
+
+ if (audio->out_needed) {
+ /* If the DSP currently wants data and we have a
+ * buffer available, we will send it and reset
+ * the needed flag. We'll mark the buffer as in-flight
+ * so that it won't be recycled until the next buffer
+ * is requested
+ */
+
+ frame = audio->out + audio->out_tail;
+ if (frame->used) {
+ BUG_ON(frame->used == 0xffffffff);
+ MM_DBG("frame %d busy\n", audio->out_tail);
+ audplay_dsp_send_data_avail(audio, audio->out_tail,
+ frame->used);
+ frame->used = 0xffffffff;
+ audio->out_needed = 0;
+ }
+ }
+ done:
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+/* ------------------- device --------------------- */
+
+static void audio_flush(struct audio *audio)
+{
+ audio->out[0].used = 0;
+ audio->out[1].used = 0;
+ audio->out_head = 0;
+ audio->out_tail = 0;
+ audio->reserved = 0;
+ audio->out_needed = 0;
+ atomic_set(&audio->out_bytes, 0);
+}
+
+static void audio_flush_pcm_buf(struct audio *audio)
+{
+ uint8_t index;
+
+ for (index = 0; index < PCM_BUF_MAX_COUNT; index++)
+ audio->in[index].used = 0;
+ audio->buf_refresh = 0;
+ audio->read_next = 0;
+ audio->fill_next = 0;
+}
+
+static int audaac_validate_usr_config(struct msm_audio_aac_config *config)
+{
+ int ret_val = -1;
+
+ if (config->format != AUDIO_AAC_FORMAT_ADTS &&
+ config->format != AUDIO_AAC_FORMAT_RAW &&
+ config->format != AUDIO_AAC_FORMAT_PSUEDO_RAW &&
+ config->format != AUDIO_AAC_FORMAT_LOAS)
+ goto done;
+
+ if (config->audio_object != AUDIO_AAC_OBJECT_LC &&
+ config->audio_object != AUDIO_AAC_OBJECT_LTP &&
+ config->audio_object != AUDIO_AAC_OBJECT_BSAC &&
+ config->audio_object != AUDIO_AAC_OBJECT_ERLC)
+ goto done;
+
+ if (config->audio_object == AUDIO_AAC_OBJECT_ERLC) {
+ if (config->ep_config > 3)
+ goto done;
+ if (config->aac_scalefactor_data_resilience_flag !=
+ AUDIO_AAC_SCA_DATA_RES_OFF &&
+ config->aac_scalefactor_data_resilience_flag !=
+ AUDIO_AAC_SCA_DATA_RES_ON)
+ goto done;
+ if (config->aac_section_data_resilience_flag !=
+ AUDIO_AAC_SEC_DATA_RES_OFF &&
+ config->aac_section_data_resilience_flag !=
+ AUDIO_AAC_SEC_DATA_RES_ON)
+ goto done;
+ if (config->aac_spectral_data_resilience_flag !=
+ AUDIO_AAC_SPEC_DATA_RES_OFF &&
+ config->aac_spectral_data_resilience_flag !=
+ AUDIO_AAC_SPEC_DATA_RES_ON)
+ goto done;
+ } else {
+ config->aac_section_data_resilience_flag =
+ AUDIO_AAC_SEC_DATA_RES_OFF;
+ config->aac_scalefactor_data_resilience_flag =
+ AUDIO_AAC_SCA_DATA_RES_OFF;
+ config->aac_spectral_data_resilience_flag =
+ AUDIO_AAC_SPEC_DATA_RES_OFF;
+ }
+
+#ifndef CONFIG_AUDIO_AAC_PLUS
+ if (AUDIO_AAC_SBR_ON_FLAG_OFF != config->sbr_on_flag)
+ goto done;
+#else
+ if (config->sbr_on_flag != AUDIO_AAC_SBR_ON_FLAG_OFF &&
+ config->sbr_on_flag != AUDIO_AAC_SBR_ON_FLAG_ON)
+ goto done;
+#endif
+
+#ifndef CONFIG_AUDIO_ENHANCED_AAC_PLUS
+ if (AUDIO_AAC_SBR_PS_ON_FLAG_OFF != config->sbr_ps_on_flag)
+ goto done;
+#else
+ if (config->sbr_ps_on_flag != AUDIO_AAC_SBR_PS_ON_FLAG_OFF &&
+ config->sbr_ps_on_flag != AUDIO_AAC_SBR_PS_ON_FLAG_ON)
+ goto done;
+#endif
+
+ if (config->dual_mono_mode > AUDIO_AAC_DUAL_MONO_PL_SR)
+ goto done;
+
+ if (config->channel_configuration > 2)
+ goto done;
+
+ ret_val = 0;
+ done:
+ return ret_val;
+}
+
+static void audio_ioport_reset(struct audio *audio)
+{
+ /* Make sure read/write thread are free from
+ * sleep and knowing that system is not able
+ * to process io request at the moment
+ */
+ wake_up(&audio->write_wait);
+ mutex_lock(&audio->write_lock);
+ audio_flush(audio);
+ mutex_unlock(&audio->write_lock);
+ wake_up(&audio->read_wait);
+ mutex_lock(&audio->read_lock);
+ audio_flush_pcm_buf(audio);
+ mutex_unlock(&audio->read_lock);
+ audio->avsync_flag = 1;
+ wake_up(&audio->avsync_wait);
+
+}
+
+static int audaac_events_pending(struct audio *audio)
+{
+ unsigned long flags;
+ int empty;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ empty = !list_empty(&audio->event_queue);
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+ return empty || audio->event_abort;
+}
+
+static void audaac_reset_event_queue(struct audio *audio)
+{
+ unsigned long flags;
+ struct audaac_event *drv_evt;
+ struct list_head *ptr, *next;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ list_for_each_safe(ptr, next, &audio->event_queue) {
+ drv_evt = list_first_entry(&audio->event_queue,
+ struct audaac_event, list);
+ list_del(&drv_evt->list);
+ kfree(drv_evt);
+ }
+ list_for_each_safe(ptr, next, &audio->free_event_queue) {
+ drv_evt = list_first_entry(&audio->free_event_queue,
+ struct audaac_event, list);
+ list_del(&drv_evt->list);
+ kfree(drv_evt);
+ }
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+ return;
+}
+
+static long audaac_process_event_req(struct audio *audio, void __user *arg)
+{
+ long rc;
+ struct msm_audio_event usr_evt;
+ struct audaac_event *drv_evt = NULL;
+ int timeout;
+ unsigned long flags;
+
+ if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event)))
+ return -EFAULT;
+
+ timeout = (int) usr_evt.timeout_ms;
+
+ if (timeout > 0) {
+ rc = wait_event_interruptible_timeout(
+ audio->event_wait, audaac_events_pending(audio),
+ msecs_to_jiffies(timeout));
+ if (rc == 0)
+ return -ETIMEDOUT;
+ } else {
+ rc = wait_event_interruptible(
+ audio->event_wait, audaac_events_pending(audio));
+ }
+
+ if (rc < 0)
+ return rc;
+
+ if (audio->event_abort) {
+ audio->event_abort = 0;
+ return -ENODEV;
+ }
+
+ rc = 0;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ if (!list_empty(&audio->event_queue)) {
+ drv_evt = list_first_entry(&audio->event_queue,
+ struct audaac_event, list);
+ list_del(&drv_evt->list);
+ }
+ if (drv_evt) {
+ usr_evt.event_type = drv_evt->event_type;
+ usr_evt.event_payload = drv_evt->payload;
+ list_add_tail(&drv_evt->list, &audio->free_event_queue);
+ } else
+ rc = -1;
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+ if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt)))
+ rc = -EFAULT;
+
+ return rc;
+}
+
+static int audio_enable_eq(struct audio *audio, int enable)
+{
+ if (audio->eq_enable == enable && !audio->eq_needs_commit)
+ return 0;
+
+ audio->eq_enable = enable;
+
+ if (audio->running) {
+ audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq, POPP);
+ audio->eq_needs_commit = 0;
+ }
+ return 0;
+}
+
+static int audio_get_avsync_data(struct audio *audio,
+ struct msm_audio_stats *stats)
+{
+ int rc = -EINVAL;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) {
+ /* av_sync sample count */
+ stats->sample_count = (audio->avsync[2] << 16) |
+ (audio->avsync[3]);
+
+ /* av_sync byte_count */
+ stats->byte_count = (audio->avsync[5] << 16) |
+ (audio->avsync[6]);
+
+ audio->avsync_flag = 0;
+ rc = 0;
+ }
+ local_irq_restore(flags);
+ return rc;
+
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct audio *audio = file->private_data;
+ int rc = -EINVAL;
+ unsigned long flags = 0;
+ uint16_t enable_mask;
+ int enable;
+ int prev_state;
+
+ MM_DBG("cmd = %d\n", cmd);
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+
+ audio->avsync_flag = 0;
+ memset(&stats, 0, sizeof(stats));
+ if (audpp_query_avsync(audio->dec_id) < 0)
+ return rc;
+
+ rc = wait_event_interruptible_timeout(audio->avsync_wait,
+ (audio->avsync_flag == 1),
+ msecs_to_jiffies(AUDPP_AVSYNC_EVENT_TIMEOUT));
+
+ if (rc < 0)
+ return rc;
+ else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) {
+ if (audio_get_avsync_data(audio, &stats) < 0)
+ return rc;
+
+ if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ } else
+ return -EAGAIN;
+ }
+
+ switch (cmd) {
+ case AUDIO_ENABLE_AUDPP:
+ if (copy_from_user(&enable_mask, (void *) arg,
+ sizeof(enable_mask))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ enable = (enable_mask & EQ_ENABLE) ? 1 : 0;
+ audio_enable_eq(audio, enable);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ rc = 0;
+ break;
+ case AUDIO_SET_VOLUME:
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->vol_pan.volume = arg;
+ if (audio->running)
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ rc = 0;
+ break;
+
+ case AUDIO_SET_PAN:
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->vol_pan.pan = arg;
+ if (audio->running)
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ rc = 0;
+ break;
+
+ case AUDIO_SET_EQ:
+ prev_state = audio->eq_enable;
+ audio->eq_enable = 0;
+ if (copy_from_user(&audio->eq.num_bands, (void *) arg,
+ sizeof(audio->eq) -
+ (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) {
+ rc = -EFAULT;
+ break;
+ }
+ audio->eq_enable = prev_state;
+ audio->eq_needs_commit = 1;
+ rc = 0;
+ break;
+ }
+
+ if (-EINVAL != rc)
+ return rc;
+
+ if (cmd == AUDIO_GET_EVENT) {
+ MM_DBG("AUDIO_GET_EVENT\n");
+ if (mutex_trylock(&audio->get_event_lock)) {
+ rc = audaac_process_event_req(audio,
+ (void __user *) arg);
+ mutex_unlock(&audio->get_event_lock);
+ } else
+ rc = -EBUSY;
+ return rc;
+ }
+
+ if (cmd == AUDIO_ABORT_GET_EVENT) {
+ audio->event_abort = 1;
+ wake_up(&audio->event_wait);
+ return 0;
+ }
+
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START:
+ MM_DBG("AUDIO_START\n");
+ rc = audio_enable(audio);
+ if (!rc) {
+ rc = wait_event_interruptible_timeout(audio->wait,
+ audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+ msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+ MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc);
+
+ if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS)
+ rc = -ENODEV;
+ else
+ rc = 0;
+ }
+ break;
+ case AUDIO_STOP:
+ MM_DBG("AUDIO_STOP\n");
+ rc = audio_disable(audio);
+ audio->stopped = 1;
+ audio_ioport_reset(audio);
+ audio->stopped = 0;
+ break;
+ case AUDIO_FLUSH:
+ MM_DBG("AUDIO_FLUSH running=%d\n", audio->running);
+ audio->rflush = 1;
+ audio->wflush = 1;
+ audio_ioport_reset(audio);
+ if (audio->running) {
+ audpp_flush(audio->dec_id);
+ rc = wait_event_interruptible(audio->write_wait,
+ !audio->wflush);
+ if (rc < 0) {
+ MM_ERR("AUDIO_FLUSH interrupted\n");
+ rc = -EINTR;
+ }
+ } else {
+ audio->rflush = 0;
+ audio->wflush = 0;
+ }
+ break;
+
+ case AUDIO_OUTPORT_FLUSH:
+ MM_DBG("AUDIO_OUTPORT_FLUSH\n");
+ audio->rflush = 1;
+ wake_up(&audio->read_wait);
+ mutex_lock(&audio->read_lock);
+ audio_flush_pcm_buf(audio);
+ mutex_unlock(&audio->read_lock);
+ audplay_outport_flush(audio);
+ rc = wait_event_interruptible(audio->read_wait,
+ !audio->rflush);
+ if (rc < 0) {
+ MM_ERR("AUDPLAY_OUTPORT_FLUSH interrupted\n");
+ rc = -EINTR;
+ }
+ break;
+
+ case AUDIO_SET_CONFIG:{
+ struct msm_audio_config config;
+
+ if (copy_from_user
+ (&config, (void *)arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ if (config.channel_count == 1) {
+ config.channel_count =
+ AUDPP_CMD_PCM_INTF_MONO_V;
+ } else if (config.channel_count == 2) {
+ config.channel_count =
+ AUDPP_CMD_PCM_INTF_STEREO_V;
+ } else {
+ rc = -EINVAL;
+ break;
+ }
+
+ audio->out_sample_rate = config.sample_rate;
+ audio->out_channel_mode = config.channel_count;
+ audio->mfield = config.meta_field;
+ rc = 0;
+ break;
+ }
+ case AUDIO_GET_CONFIG:{
+ struct msm_audio_config config;
+ config.buffer_size = (audio->out_dma_sz >> 1);
+ config.buffer_count = 2;
+ config.sample_rate = audio->out_sample_rate;
+ if (audio->out_channel_mode ==
+ AUDPP_CMD_PCM_INTF_MONO_V) {
+ config.channel_count = 1;
+ } else {
+ config.channel_count = 2;
+ }
+ config.meta_field = 0;
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+ config.unused[2] = 0;
+ if (copy_to_user((void *)arg, &config,
+ sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+
+ break;
+ }
+ case AUDIO_GET_AAC_CONFIG:{
+ if (copy_to_user((void *)arg, &audio->aac_config,
+ sizeof(audio->aac_config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ case AUDIO_SET_AAC_CONFIG:{
+ struct msm_audio_aac_config usr_config;
+
+ if (copy_from_user
+ (&usr_config, (void *)arg,
+ sizeof(usr_config))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ if (audaac_validate_usr_config(&usr_config) == 0) {
+ audio->aac_config = usr_config;
+ rc = 0;
+ } else
+ rc = -EINVAL;
+
+ break;
+ }
+ case AUDIO_GET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+ config.pcm_feedback = audio->pcm_feedback;
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+ config.buffer_size = PCM_BUFSZ_MIN;
+ if (copy_to_user((void *)arg, &config,
+ sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ case AUDIO_SET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+ if (copy_from_user
+ (&config, (void *)arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (config.pcm_feedback != audio->pcm_feedback) {
+ MM_ERR("Not sufficient permission to"
+ "change the playback mode\n");
+ rc = -EACCES;
+ break;
+ }
+ if ((config.buffer_count > PCM_BUF_MAX_COUNT) ||
+ (config.buffer_count == 1))
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+
+ if (config.buffer_size < PCM_BUFSZ_MIN)
+ config.buffer_size = PCM_BUFSZ_MIN;
+
+ /* Check if pcm feedback is required */
+ if (config.pcm_feedback) {
+ audio->buf_refresh = 0;
+ audio->read_next = 0;
+ audio->fill_next = 0;
+ }
+ rc = 0;
+ break;
+ }
+ case AUDIO_PAUSE:
+ MM_DBG("AUDIO_PAUSE %ld\n", arg);
+ rc = audpp_pause(audio->dec_id, (int) arg);
+ break;
+ case AUDIO_GET_STREAM_INFO:{
+ if (audio->stream_info.sample_rate == 0) {
+ /* haven't received DSP stream event,
+ the stream info is not updated */
+ rc = -EPERM;
+ break;
+ }
+ if (copy_to_user((void *)arg, &audio->stream_info,
+ sizeof(struct msm_audio_bitstream_info)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ case AUDIO_GET_BITSTREAM_ERROR_INFO:{
+ if ((audio->bitstream_error_info.err_msg_indicator &
+ AUDPLAY_STREAM_INFO_MSG_MASK) ==
+ AUDPLAY_STREAM_INFO_MSG_MASK) {
+ /* haven't received bitstream error info event,
+ the bitstream error info is not updated */
+ rc = -EPERM;
+ break;
+ }
+ if (copy_to_user((void *)arg, &audio->bitstream_error_info,
+ sizeof(struct msm_audio_bitstream_error_info)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ case AUDIO_GET_SESSION_ID:
+ if (copy_to_user((void *) arg, &audio->dec_id,
+ sizeof(unsigned short)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ case AUDIO_SET_ERR_THRESHOLD_VALUE:
+ if (copy_from_user(&audio->bitstream_error_threshold_value,
+ (void *)arg, sizeof(uint32_t)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+/* Only useful in tunnel-mode */
+static int audaac_fsync(struct file *file, int datasync)
+{
+ struct audio *audio = file->private_data;
+ struct buffer *frame;
+ int rc = 0;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+
+ if (!audio->running || audio->pcm_feedback) {
+ rc = -EINVAL;
+ goto done_nolock;
+ }
+
+ mutex_lock(&audio->write_lock);
+
+ rc = wait_event_interruptible(audio->write_wait,
+ (!audio->out[0].used &&
+ !audio->out[1].used &&
+ audio->out_needed) || audio->wflush);
+
+ if (rc < 0)
+ goto done;
+ else if (audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+
+ if (audio->reserved) {
+ MM_DBG("send reserved byte\n");
+ frame = audio->out + audio->out_tail;
+ ((char *) frame->data)[0] = audio->rsv_byte;
+ ((char *) frame->data)[1] = 0;
+ frame->used = 2;
+ audplay_send_data(audio, 0);
+
+ rc = wait_event_interruptible(audio->write_wait,
+ (!audio->out[0].used &&
+ !audio->out[1].used &&
+ audio->out_needed) || audio->wflush);
+
+ if (rc < 0)
+ goto done;
+ else if (audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+ }
+
+ /* pcm dmamiss message is sent continously
+ * when decoder is starved so no race
+ * condition concern
+ */
+ audio->teos = 0;
+
+ rc = wait_event_interruptible(audio->write_wait,
+ audio->teos || audio->wflush);
+
+ if (audio->wflush)
+ rc = -EBUSY;
+
+done:
+ mutex_unlock(&audio->write_lock);
+done_nolock:
+ return rc;
+}
+
+static ssize_t audio_read(struct file *file, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ int rc = 0;
+
+ if (!audio->pcm_feedback)
+ return 0; /* PCM feedback is not enabled. Nothing to read */
+
+ mutex_lock(&audio->read_lock);
+ MM_DBG("to read %d \n", count);
+ while (count > 0) {
+ rc = wait_event_interruptible_timeout(audio->read_wait,
+ (audio->in[audio->read_next].
+ used > 0) || (audio->stopped)
+ || (audio->rflush),
+ msecs_to_jiffies(MSM_AUD_BUFFER_UPDATE_WAIT_MS));
+
+ if (rc == 0) {
+ rc = -ETIMEDOUT;
+ break;
+ } else if (rc < 0)
+ break;
+
+ if (audio->stopped || audio->rflush) {
+ rc = -EBUSY;
+ break;
+ }
+
+ if (count < audio->in[audio->read_next].used) {
+ /* Read must happen in frame boundary. Since driver
+ does not know frame size, read count must be greater
+ or equal to size of PCM samples */
+ MM_DBG("no partial frame done reading\n");
+ break;
+ } else {
+ MM_DBG("read from in[%d]\n", audio->read_next);
+ if (copy_to_user
+ (buf, audio->in[audio->read_next].data,
+ audio->in[audio->read_next].used)) {
+ MM_ERR("invalid addr %x\n", (unsigned int)buf);
+ rc = -EFAULT;
+ break;
+ }
+ count -= audio->in[audio->read_next].used;
+ buf += audio->in[audio->read_next].used;
+ audio->in[audio->read_next].used = 0;
+ if ((++audio->read_next) == audio->pcm_buf_count)
+ audio->read_next = 0;
+ break;
+ /*
+ * Force to exit while loop
+ * to prevent output thread
+ * sleep too long if data is not
+ * ready at this moment.
+ */
+ }
+ }
+
+ /* don't feed output buffer to HW decoder during flushing
+ * buffer refresh command will be sent once flush completes
+ * send buf refresh command here can confuse HW decoder
+ */
+ if (audio->buf_refresh && !audio->rflush) {
+ audio->buf_refresh = 0;
+ MM_DBG("kick start pcm feedback again\n");
+ audplay_buffer_refresh(audio);
+ }
+
+ mutex_unlock(&audio->read_lock);
+
+ if (buf > start)
+ rc = buf - start;
+
+ MM_DBG("read %d bytes\n", rc);
+ return rc;
+}
+
+static int audaac_process_eos(struct audio *audio,
+ const char __user *buf_start, unsigned short mfield_size)
+{
+ struct buffer *frame;
+ char *buf_ptr;
+ int rc = 0;
+
+ MM_DBG("signal input EOS reserved=%d\n", audio->reserved);
+ if (audio->reserved) {
+ MM_DBG("Pass reserve byte\n");
+ frame = audio->out + audio->out_head;
+ buf_ptr = frame->data;
+ rc = wait_event_interruptible(audio->write_wait,
+ (frame->used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+ if (rc < 0)
+ goto done;
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+ buf_ptr[0] = audio->rsv_byte;
+ buf_ptr[1] = 0;
+ audio->out_head ^= 1;
+ frame->mfield_sz = 0;
+ audio->reserved = 0;
+ frame->used = 2;
+ audplay_send_data(audio, 0);
+ }
+ MM_DBG("Now signal input EOS after reserved bytes %d %d %d\n",
+ audio->out[0].used, audio->out[1].used, audio->out_needed);
+ frame = audio->out + audio->out_head;
+
+ rc = wait_event_interruptible(audio->write_wait,
+ (audio->out_needed &&
+ audio->out[0].used == 0 &&
+ audio->out[1].used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+
+ if (rc < 0)
+ goto done;
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+
+ if (copy_from_user(frame->data, buf_start, mfield_size)) {
+ rc = -EFAULT;
+ goto done;
+ }
+
+ frame->mfield_sz = mfield_size;
+ audio->out_head ^= 1;
+ frame->used = mfield_size;
+ audplay_send_data(audio, 0);
+done:
+ return rc;
+}
+static ssize_t audio_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ struct buffer *frame;
+ size_t xfer;
+ char *cpy_ptr;
+ int rc = 0, eos_condition = AUDAAC_EOS_NONE;
+ unsigned dsize;
+
+ unsigned short mfield_size = 0;
+ MM_DBG("cnt=%d\n", count);
+ mutex_lock(&audio->write_lock);
+ while (count > 0) {
+ frame = audio->out + audio->out_head;
+ cpy_ptr = frame->data;
+ dsize = 0;
+ rc = wait_event_interruptible(audio->write_wait,
+ (frame->used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+ if (rc < 0)
+ break;
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ break;
+ }
+ if (audio->mfield) {
+ if (buf == start) {
+ /* Processing beginning of user buffer */
+ if (__get_user(mfield_size,
+ (unsigned short __user *) buf)) {
+ rc = -EFAULT;
+ break;
+ } else if (mfield_size > count) {
+ rc = -EINVAL;
+ break;
+ }
+ MM_DBG("mf offset_val %x\n", mfield_size);
+ if (copy_from_user(cpy_ptr, buf, mfield_size)) {
+ rc = -EFAULT;
+ break;
+ }
+ /* Check if EOS flag is set and buffer has
+ * contains just meta field
+ */
+ if (cpy_ptr[AUDAAC_EOS_FLG_OFFSET] &
+ AUDAAC_EOS_FLG_MASK) {
+ MM_DBG("eos set\n");
+ eos_condition = AUDAAC_EOS_SET;
+ if (mfield_size == count) {
+ buf += mfield_size;
+ break;
+ } else
+ cpy_ptr[AUDAAC_EOS_FLG_OFFSET] &=
+ ~AUDAAC_EOS_FLG_MASK;
+ }
+ /* Check EOS to see if */
+ cpy_ptr += mfield_size;
+ count -= mfield_size;
+ dsize += mfield_size;
+ buf += mfield_size;
+ } else {
+ mfield_size = 0;
+ MM_DBG("continuous buffer\n");
+ }
+ frame->mfield_sz = mfield_size;
+ }
+
+ if (audio->reserved) {
+ MM_DBG("append reserved byte %x\n",
+ audio->rsv_byte);
+ *cpy_ptr = audio->rsv_byte;
+ xfer = (count > ((frame->size - mfield_size) - 1)) ?
+ (frame->size - mfield_size) - 1 : count;
+ cpy_ptr++;
+ dsize += 1;
+ audio->reserved = 0;
+ } else
+ xfer = (count > (frame->size - mfield_size)) ?
+ (frame->size - mfield_size) : count;
+
+ if (copy_from_user(cpy_ptr, buf, xfer)) {
+ rc = -EFAULT;
+ break;
+ }
+
+ dsize += xfer;
+ if (dsize & 1) {
+ audio->rsv_byte = ((char *) frame->data)[dsize - 1];
+ MM_DBG("odd length buf reserve last byte %x\n",
+ audio->rsv_byte);
+ audio->reserved = 1;
+ dsize--;
+ }
+ count -= xfer;
+ buf += xfer;
+
+ if (dsize > 0) {
+ audio->out_head ^= 1;
+ frame->used = dsize;
+ audplay_send_data(audio, 0);
+ }
+ }
+ MM_DBG("eos_condition %x buf[0x%x] start[0x%x]\n", eos_condition,
+ (int) buf, (int) start);
+ if (eos_condition == AUDAAC_EOS_SET)
+ rc = audaac_process_eos(audio, start, mfield_size);
+ mutex_unlock(&audio->write_lock);
+ if (!rc) {
+ if (buf > start)
+ return buf - start;
+ }
+ return rc;
+}
+
+static int audio_release(struct inode *inode, struct file *file)
+{
+ struct audio *audio = file->private_data;
+
+ MM_INFO("audio instance 0x%08x freeing\n", (int)audio);
+
+ mutex_lock(&audio->lock);
+ auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id);
+ audio_disable(audio);
+ audio_flush(audio);
+ audio_flush_pcm_buf(audio);
+ msm_adsp_put(audio->audplay);
+ audpp_adec_free(audio->dec_id);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&audio->suspend_ctl.node);
+#endif
+ audio->event_abort = 1;
+ wake_up(&audio->event_wait);
+ audaac_reset_event_queue(audio);
+ iounmap(audio->data);
+ pmem_kfree(audio->phys);
+ iounmap(audio->read_data);
+ pmem_kfree(audio->read_phys);
+ mutex_unlock(&audio->lock);
+#ifdef CONFIG_DEBUG_FS
+ if (audio->dentry)
+ debugfs_remove(audio->dentry);
+#endif
+ kfree(audio);
+ return 0;
+}
+
+static void audaac_post_event(struct audio *audio, int type,
+ union msm_audio_event_payload payload)
+{
+ struct audaac_event *e_node = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+
+ if (!list_empty(&audio->free_event_queue)) {
+ e_node = list_first_entry(&audio->free_event_queue,
+ struct audaac_event, list);
+ list_del(&e_node->list);
+ } else {
+ e_node = kmalloc(sizeof(struct audaac_event), GFP_ATOMIC);
+ if (!e_node) {
+ MM_ERR("No mem to post event %d\n", type);
+ return;
+ }
+ }
+
+ e_node->event_type = type;
+ e_node->payload = payload;
+
+ list_add_tail(&e_node->list, &audio->event_queue);
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+ wake_up(&audio->event_wait);
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void audaac_suspend(struct early_suspend *h)
+{
+ struct audaac_suspend_ctl *ctl =
+ container_of(h, struct audaac_suspend_ctl, node);
+ union msm_audio_event_payload payload;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ audaac_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload);
+}
+
+static void audaac_resume(struct early_suspend *h)
+{
+ struct audaac_suspend_ctl *ctl =
+ container_of(h, struct audaac_suspend_ctl, node);
+ union msm_audio_event_payload payload;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ audaac_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload);
+}
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t audaac_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t audaac_debug_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ const int debug_bufmax = 1024;
+ static char buffer[1024];
+ int n = 0, i;
+ struct audio *audio = file->private_data;
+
+ mutex_lock(&audio->lock);
+ n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "enabled %d\n", audio->enabled);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "stopped %d\n", audio->stopped);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "pcm_feedback %d\n", audio->pcm_feedback);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_buf_sz %d\n", audio->out[0].size);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "pcm_buf_count %d \n", audio->pcm_buf_count);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "pcm_buf_sz %d \n", audio->in[0].size);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "volume %x \n", audio->vol_pan.volume);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "sample rate %d \n", audio->out_sample_rate);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "channel mode %d \n", audio->out_channel_mode);
+ mutex_unlock(&audio->lock);
+ /* Following variables are only useful for debugging when
+ * when playback halts unexpectedly. Thus, no mutual exclusion
+ * enforced
+ */
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "wflush %d\n", audio->wflush);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "rflush %d\n", audio->rflush);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "running %d \n", audio->running);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "dec state %d \n", audio->dec_state);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_needed %d \n", audio->out_needed);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_head %d \n", audio->out_head);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_tail %d \n", audio->out_tail);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out[0].used %d \n", audio->out[0].used);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out[1].used %d \n", audio->out[1].used);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "buffer_refresh %d \n", audio->buf_refresh);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "read_next %d \n", audio->read_next);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "fill_next %d \n", audio->fill_next);
+ for (i = 0; i < audio->pcm_buf_count; i++)
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "in[%d].used %d \n", i, audio->in[i].used);
+ buffer[n] = 0;
+ return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static const struct file_operations audaac_debug_fops = {
+ .read = audaac_debug_read,
+ .open = audaac_debug_open,
+};
+#endif
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct audio *audio = NULL;
+ int rc, dec_attrb, decid, index, offset = 0;
+ unsigned pmem_sz = DMASZ;
+ struct audaac_event *e_node = NULL;
+#ifdef CONFIG_DEBUG_FS
+ /* 4 bytes represents decoder number, 1 byte for terminate string */
+ char name[sizeof "msm_aac_" + 5];
+#endif
+
+ /* Allocate audio instance, set to zero */
+ audio = kzalloc(sizeof(struct audio), GFP_KERNEL);
+ if (!audio) {
+ MM_ERR("no memory to allocate audio instance \n");
+ rc = -ENOMEM;
+ goto done;
+ }
+ MM_INFO("audio instance 0x%08x created\n", (int)audio);
+
+ /* Allocate the decoder */
+ dec_attrb = AUDDEC_DEC_AAC;
+ if ((file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ dec_attrb |= MSM_AUD_MODE_NONTUNNEL;
+ audio->pcm_feedback = NON_TUNNEL_MODE_PLAYBACK;
+ } else if ((file->f_mode & FMODE_WRITE) &&
+ !(file->f_mode & FMODE_READ)) {
+ dec_attrb |= MSM_AUD_MODE_TUNNEL;
+ audio->pcm_feedback = TUNNEL_MODE_PLAYBACK;
+ } else {
+ kfree(audio);
+ rc = -EACCES;
+ goto done;
+ }
+ decid = audpp_adec_alloc(dec_attrb, &audio->module_name,
+ &audio->queue_id);
+
+ if (decid < 0) {
+ MM_ERR("No free decoder available, freeing instance 0x%08x\n",
+ (int)audio);
+ rc = -ENODEV;
+ kfree(audio);
+ goto done;
+ }
+ audio->dec_id = decid & MSM_AUD_DECODER_MASK;
+
+ while (pmem_sz >= DMASZ_MIN) {
+ MM_DBG("pmemsz = %d \n", pmem_sz);
+ audio->phys = pmem_kalloc(pmem_sz, PMEM_MEMTYPE_EBI1|
+ PMEM_ALIGNMENT_4K);
+ if (!IS_ERR((void *)audio->phys)) {
+ audio->data = ioremap(audio->phys, pmem_sz);
+ if (!audio->data) {
+ MM_ERR("could not allocate write buffers, \
+ freeing instance 0x%08x\n",
+ (int)audio);
+ rc = -ENOMEM;
+ pmem_kfree(audio->phys);
+ audpp_adec_free(audio->dec_id);
+ kfree(audio);
+ goto done;
+ }
+ MM_DBG("write buf: phy addr 0x%08x kernel addr \
+ 0x%08x\n", audio->phys, (int)audio->data);
+ break;
+ } else if (pmem_sz == DMASZ_MIN) {
+ MM_ERR("could not allocate write buffers, freeing \
+ instance 0x%08x\n", (int)audio);
+ rc = -ENOMEM;
+ audpp_adec_free(audio->dec_id);
+ kfree(audio);
+ goto done;
+ } else
+ pmem_sz >>= 1;
+ }
+ audio->out_dma_sz = pmem_sz;
+
+ audio->read_phys = pmem_kalloc(PCM_BUFSZ_MIN * PCM_BUF_MAX_COUNT,
+ PMEM_MEMTYPE_EBI1|PMEM_ALIGNMENT_4K);
+ if (IS_ERR((void *)audio->read_phys)) {
+ MM_ERR("could not allocate read buffers, freeing instance \
+ 0x%08x\n", (int)audio);
+ rc = -ENOMEM;
+ iounmap(audio->data);
+ pmem_kfree(audio->phys);
+ audpp_adec_free(audio->dec_id);
+ kfree(audio);
+ goto done;
+ }
+ audio->read_data = ioremap(audio->read_phys,
+ PCM_BUFSZ_MIN * PCM_BUF_MAX_COUNT);
+ if (!audio->read_data) {
+ MM_ERR("could not allocate read buffers, freeing instance \
+ 0x%08x\n", (int)audio);
+ rc = -ENOMEM;
+ iounmap(audio->data);
+ pmem_kfree(audio->phys);
+ pmem_kfree(audio->read_phys);
+ audpp_adec_free(audio->dec_id);
+ kfree(audio);
+ goto done;
+ }
+ MM_DBG("read buf: phy addr 0x%08x kernel addr 0x%08x\n",
+ audio->read_phys, (int)audio->read_data);
+
+ rc = msm_adsp_get(audio->module_name, &audio->audplay,
+ &audplay_adsp_ops_aac, audio);
+ if (rc) {
+ MM_ERR("failed to get %s module, freeing instance 0x%08x\n",
+ audio->module_name, (int)audio);
+ goto err;
+ }
+
+ mutex_init(&audio->lock);
+ mutex_init(&audio->write_lock);
+ mutex_init(&audio->read_lock);
+ mutex_init(&audio->get_event_lock);
+ spin_lock_init(&audio->dsp_lock);
+ spin_lock_init(&audio->event_queue_lock);
+ INIT_LIST_HEAD(&audio->free_event_queue);
+ INIT_LIST_HEAD(&audio->event_queue);
+ init_waitqueue_head(&audio->write_wait);
+ init_waitqueue_head(&audio->read_wait);
+ init_waitqueue_head(&audio->wait);
+ init_waitqueue_head(&audio->event_wait);
+ init_waitqueue_head(&audio->avsync_wait);
+
+ audio->out[0].data = audio->data + 0;
+ audio->out[0].addr = audio->phys + 0;
+ audio->out[0].size = audio->out_dma_sz >> 1;
+
+ audio->out[1].data = audio->data + audio->out[0].size;
+ audio->out[1].addr = audio->phys + audio->out[0].size;
+ audio->out[1].size = audio->out[0].size;
+
+ audio->pcm_buf_count = PCM_BUF_MAX_COUNT;
+ for (index = 0; index < PCM_BUF_MAX_COUNT; index++) {
+ audio->in[index].data = audio->read_data + offset;
+ audio->in[index].addr = audio->read_phys + offset;
+ audio->in[index].size = PCM_BUFSZ_MIN;
+ audio->in[index].used = 0;
+ offset += PCM_BUFSZ_MIN;
+ }
+
+ audio->out_sample_rate = 44100;
+ audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V;
+ audio->aac_config.format = AUDIO_AAC_FORMAT_ADTS;
+ audio->aac_config.audio_object = AUDIO_AAC_OBJECT_LC;
+ audio->aac_config.ep_config = 0;
+ audio->aac_config.aac_section_data_resilience_flag =
+ AUDIO_AAC_SEC_DATA_RES_OFF;
+ audio->aac_config.aac_scalefactor_data_resilience_flag =
+ AUDIO_AAC_SCA_DATA_RES_OFF;
+ audio->aac_config.aac_spectral_data_resilience_flag =
+ AUDIO_AAC_SPEC_DATA_RES_OFF;
+#ifdef CONFIG_AUDIO_AAC_PLUS
+ audio->aac_config.sbr_on_flag = AUDIO_AAC_SBR_ON_FLAG_ON;
+#else
+ audio->aac_config.sbr_on_flag = AUDIO_AAC_SBR_ON_FLAG_OFF;
+#endif
+#ifdef CONFIG_AUDIO_ENHANCED_AAC_PLUS
+ audio->aac_config.sbr_ps_on_flag = AUDIO_AAC_SBR_PS_ON_FLAG_ON;
+#else
+ audio->aac_config.sbr_ps_on_flag = AUDIO_AAC_SBR_PS_ON_FLAG_OFF;
+#endif
+ audio->aac_config.dual_mono_mode = AUDIO_AAC_DUAL_MONO_PL_SR;
+ audio->aac_config.channel_configuration = 2;
+ audio->vol_pan.volume = 0x2000;
+ audio->bitstream_error_threshold_value =
+ BITSTREAM_ERROR_THRESHOLD_VALUE;
+
+ audio_flush(audio);
+
+ file->private_data = audio;
+ audio->opened = 1;
+
+ audio->device_events = AUDDEV_EVT_DEV_RDY
+ |AUDDEV_EVT_DEV_RLS|
+ AUDDEV_EVT_STREAM_VOL_CHG;
+
+ rc = auddev_register_evt_listner(audio->device_events,
+ AUDDEV_CLNT_DEC,
+ audio->dec_id,
+ aac_listner,
+ (void *)audio);
+ if (rc) {
+ MM_ERR("%s: failed to register listner\n", __func__);
+ goto event_err;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ snprintf(name, sizeof name, "msm_aac_%04x", audio->dec_id);
+ audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *) audio,
+ &audaac_debug_fops);
+
+ if (IS_ERR(audio->dentry))
+ MM_DBG("debugfs_create_file failed\n");
+#endif
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+ audio->suspend_ctl.node.resume = audaac_resume;
+ audio->suspend_ctl.node.suspend = audaac_suspend;
+ audio->suspend_ctl.audio = audio;
+ register_early_suspend(&audio->suspend_ctl.node);
+#endif
+ for (index = 0; index < AUDAAC_EVENT_NUM; index++) {
+ e_node = kmalloc(sizeof(struct audaac_event), GFP_KERNEL);
+ if (e_node)
+ list_add_tail(&e_node->list, &audio->free_event_queue);
+ else {
+ MM_ERR("event pkt alloc failed\n");
+ break;
+ }
+ }
+ memset(&audio->stream_info, 0, sizeof(struct msm_audio_bitstream_info));
+ memset(&audio->bitstream_error_info, 0,
+ sizeof(struct msm_audio_bitstream_info));
+done:
+ return rc;
+event_err:
+ msm_adsp_put(audio->audplay);
+err:
+ iounmap(audio->data);
+ pmem_kfree(audio->phys);
+ iounmap(audio->read_data);
+ pmem_kfree(audio->read_phys);
+ audpp_adec_free(audio->dec_id);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_aac_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_release,
+ .read = audio_read,
+ .write = audio_write,
+ .unlocked_ioctl = audio_ioctl,
+ .fsync = audaac_fsync
+};
+
+struct miscdevice audio_aac_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_aac",
+ .fops = &audio_aac_fops,
+};
+
+static int __init audio_init(void)
+{
+ return misc_register(&audio_aac_misc);
+}
+
+static void __exit audio_exit(void)
+{
+ misc_deregister(&audio_aac_misc);
+}
+
+module_init(audio_init);
+module_exit(audio_exit);
+
+MODULE_DESCRIPTION("MSM AAC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_aac_in.c b/arch/arm/mach-msm/qdsp5v2/audio_aac_in.c
new file mode 100644
index 0000000..610dbae
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_aac_in.c
@@ -0,0 +1,1465 @@
+/*
+ * aac audio input device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/msm_audio_aac.h>
+#include <linux/android_pmem.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+
+#include <mach/msm_adsp.h>
+#include <mach/qdsp5v2/qdsp5audreccmdi.h>
+#include <mach/qdsp5v2/qdsp5audrecmsg.h>
+#include <mach/qdsp5v2/audpreproc.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/debug_mm.h>
+
+/* FRAME_NUM must be a power of two */
+#define FRAME_NUM (8)
+#define FRAME_SIZE (772 * 2) /* 1536 bytes data */
+#define NT_FRAME_SIZE (780 * 2) /* 1536 bytes data + 24 meta field*/
+#define AAC_FRAME_SIZE 1536
+#define DMASZ (FRAME_SIZE * FRAME_NUM)
+#define OUT_FRAME_NUM (2)
+#define META_OUT_SIZE (24)
+#define META_IN_SIZE (14)
+#define OUT_BUFFER_SIZE (32 * 1024 + META_OUT_SIZE)
+#define BUFFER_SIZE (OUT_BUFFER_SIZE * OUT_FRAME_NUM)
+
+#define AUDPREPROC_AAC_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */
+#define AUDPREPROC_AAC_EOS_FLG_MASK 0x01
+#define AUDPREPROC_AAC_EOS_NONE 0x0 /* No EOS detected */
+#define AUDPREPROC_AAC_EOS_SET 0x1 /* EOS set in meta field */
+
+#define PCM_CONFIG_UPDATE_FLAG_ENABLE -1
+#define PCM_CONFIG_UPDATE_FLAG_DISABLE 0
+
+#define ENABLE_FLAG_VALUE -1
+#define DISABLE_FLAG_VALUE 0
+
+struct buffer {
+ void *data;
+ uint32_t size;
+ uint32_t read;
+ uint32_t addr;
+ uint32_t used;
+ uint32_t mfield_sz;
+};
+
+struct audio_in {
+ struct buffer in[FRAME_NUM];
+
+ spinlock_t dsp_lock;
+
+ atomic_t in_bytes;
+ atomic_t in_samples;
+
+ struct mutex lock;
+ struct mutex read_lock;
+ wait_queue_head_t wait;
+ wait_queue_head_t wait_enable;
+ /*write section*/
+ struct buffer out[OUT_FRAME_NUM];
+
+ uint8_t out_head;
+ uint8_t out_tail;
+ uint8_t out_needed; /* number of buffers the dsp is waiting for */
+ uint32_t out_count;
+
+ struct mutex write_lock;
+ wait_queue_head_t write_wait;
+ int32_t out_phys; /* physical address of write buffer */
+ char *out_data;
+ int mfield; /* meta field embedded in data */
+ int wflush; /*write flush */
+ int rflush; /*read flush*/
+ int out_frame_cnt;
+
+ struct msm_adsp_module *audrec;
+
+ /* configuration to use on next enable */
+ uint32_t buffer_size; /* Frame size (36 bytes) */
+ uint32_t samp_rate;
+ uint32_t channel_mode;
+ uint32_t bit_rate; /* bit rate for AAC */
+ uint32_t record_quality; /* record quality (bits/sample/channel) */
+ uint32_t enc_type;
+
+ uint32_t dsp_cnt;
+ uint32_t in_head; /* next buffer dsp will write */
+ uint32_t in_tail; /* next buffer read() will read */
+ uint32_t in_count; /* number of buffers available to read() */
+ uint32_t mode;
+ uint32_t eos_ack;
+ uint32_t flush_ack;
+
+ const char *module_name;
+ unsigned queue_ids;
+ uint16_t enc_id;
+
+ struct audrec_session_info session_info; /*audrec session info*/
+ uint16_t source; /* Encoding source bit mask */
+ uint32_t device_events; /* device events interested in */
+ uint32_t dev_cnt;
+ spinlock_t dev_lock;
+
+ /* data allocated for various buffers */
+ char *data;
+ dma_addr_t phys;
+
+ int opened;
+ int enabled;
+ int running;
+ int stopped; /* set when stopped, cleared on flush */
+ int abort; /* set when error, like sample rate mismatch */
+};
+
+struct audio_frame {
+ uint16_t frame_count_lsw;
+ uint16_t frame_count_msw;
+ uint16_t frame_length;
+ uint16_t erased_pcm;
+ unsigned char raw_bitstream[]; /* samples */
+} __attribute__((packed));
+
+struct audio_frame_nt {
+ uint16_t metadata_len;
+ uint16_t frame_count_lsw;
+ uint16_t frame_count_msw;
+ uint16_t frame_length;
+ uint16_t erased_pcm;
+ uint16_t reserved;
+ uint16_t time_stamp_dword_lsw;
+ uint16_t time_stamp_dword_msw;
+ uint16_t time_stamp_lsw;
+ uint16_t time_stamp_msw;
+ uint16_t nflag_lsw;
+ uint16_t nflag_msw;
+ unsigned char raw_bitstream[]; /* samples */
+} __attribute__((packed));
+
+struct aac_encoded_meta_in {
+ uint16_t metadata_len;
+ uint16_t time_stamp_dword_lsw;
+ uint16_t time_stamp_dword_msw;
+ uint16_t time_stamp_lsw;
+ uint16_t time_stamp_msw;
+ uint16_t nflag_lsw;
+ uint16_t nflag_msw;
+};
+
+/* Audrec Queue command sent macro's */
+#define audrec_send_bitstreamqueue(audio, cmd, len) \
+ msm_adsp_write(audio->audrec, ((audio->queue_ids & 0xFFFF0000) >> 16),\
+ cmd, len)
+
+#define audrec_send_audrecqueue(audio, cmd, len) \
+ msm_adsp_write(audio->audrec, (audio->queue_ids & 0x0000FFFF),\
+ cmd, len)
+
+/* DSP command send functions */
+static int audaac_in_enc_config(struct audio_in *audio, int enable);
+static int audaac_in_param_config(struct audio_in *audio);
+static int audaac_in_mem_config(struct audio_in *audio);
+static int audaac_in_record_config(struct audio_in *audio, int enable);
+static int audaac_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt);
+
+static void audaac_in_get_dsp_frames(struct audio_in *audio);
+static int audpcm_config(struct audio_in *audio);
+static void audaac_out_flush(struct audio_in *audio);
+static int audpreproc_cmd_cfg_routing_mode(struct audio_in *audio);
+static void audpreproc_pcm_send_data(struct audio_in *audio, unsigned needed);
+static void audaac_nt_in_get_dsp_frames(struct audio_in *audio);
+
+static void audaac_in_flush(struct audio_in *audio);
+
+static void aac_in_listener(u32 evt_id, union auddev_evt_data *evt_payload,
+ void *private_data)
+{
+ struct audio_in *audio = (struct audio_in *) private_data;
+ unsigned long flags;
+
+ MM_DBG("evt_id = 0x%8x\n", evt_id);
+ switch (evt_id) {
+ case AUDDEV_EVT_DEV_RDY: {
+ MM_DBG("AUDDEV_EVT_DEV_RDY\n");
+ spin_lock_irqsave(&audio->dev_lock, flags);
+ audio->dev_cnt++;
+ audio->source |= (0x1 << evt_payload->routing_id);
+ spin_unlock_irqrestore(&audio->dev_lock, flags);
+
+ if ((audio->running == 1) && (audio->enabled == 1) &&
+ (audio->mode == MSM_AUD_ENC_MODE_TUNNEL))
+ audaac_in_record_config(audio, 1);
+
+ break;
+ }
+ case AUDDEV_EVT_DEV_RLS: {
+ MM_DBG("AUDDEV_EVT_DEV_RLS\n");
+ spin_lock_irqsave(&audio->dev_lock, flags);
+ audio->dev_cnt--;
+ audio->source &= ~(0x1 << evt_payload->routing_id);
+ spin_unlock_irqrestore(&audio->dev_lock, flags);
+
+ if ((!audio->running) || (!audio->enabled))
+ break;
+
+ if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+ /* Turn of as per source */
+ if (audio->source)
+ audaac_in_record_config(audio, 1);
+ else
+ /* Turn off all */
+ audaac_in_record_config(audio, 0);
+ }
+ break;
+ }
+ case AUDDEV_EVT_FREQ_CHG: {
+ MM_DBG("Encoder Driver got sample rate change event\n");
+ MM_DBG("sample rate %d\n", evt_payload->freq_info.sample_rate);
+ MM_DBG("dev_type %d\n", evt_payload->freq_info.dev_type);
+ MM_DBG("acdb_dev_id %d\n", evt_payload->freq_info.acdb_dev_id);
+ if ((audio->running == 1) && (audio->enabled == 1)) {
+ /* Stop Recording sample rate does not match
+ with device sample rate */
+ if (evt_payload->freq_info.sample_rate !=
+ audio->samp_rate) {
+ if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)
+ audaac_in_record_config(audio, 0);
+ audio->abort = 1;
+ wake_up(&audio->wait);
+ }
+ }
+ break;
+ }
+ default:
+ MM_ERR("wrong event %d\n", evt_id);
+ break;
+ }
+}
+
+/* Convert Bit Rate to Record Quality field of DSP */
+static unsigned int bitrate_to_record_quality(unsigned int sample_rate,
+ unsigned int channel, unsigned int bit_rate) {
+ unsigned int temp;
+
+ temp = sample_rate * channel;
+ MM_DBG(" sample rate * channel = %d \n", temp);
+ /* To represent in Q12 fixed format */
+ temp = (bit_rate * 4096) / temp;
+ MM_DBG(" Record Quality = 0x%8x \n", temp);
+ return temp;
+}
+
+/* ------------------- dsp preproc event handler--------------------- */
+static void audpreproc_dsp_event(void *data, unsigned id, void *msg)
+{
+ struct audio_in *audio = data;
+
+ switch (id) {
+ case AUDPREPROC_ERROR_MSG: {
+ struct audpreproc_err_msg *err_msg = msg;
+
+ MM_ERR("ERROR_MSG: stream id %d err idx %d\n",
+ err_msg->stream_id, err_msg->aud_preproc_err_idx);
+ /* Error case */
+ wake_up(&audio->wait_enable);
+ break;
+ }
+ case AUDPREPROC_CMD_CFG_DONE_MSG: {
+ MM_DBG("CMD_CFG_DONE_MSG \n");
+ break;
+ }
+ case AUDPREPROC_CMD_ENC_CFG_DONE_MSG: {
+ struct audpreproc_cmd_enc_cfg_done_msg *enc_cfg_msg = msg;
+
+ MM_DBG("CMD_ENC_CFG_DONE_MSG: stream id %d enc type \
+ 0x%8x\n", enc_cfg_msg->stream_id,
+ enc_cfg_msg->rec_enc_type);
+ /* Encoder enable success */
+ if (enc_cfg_msg->rec_enc_type & ENCODE_ENABLE) {
+ if(audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) {
+ MM_DBG("routing command\n");
+ audpreproc_cmd_cfg_routing_mode(audio);
+ } else {
+ audaac_in_param_config(audio);
+ }
+ } else { /* Encoder disable success */
+ audio->running = 0;
+ if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)
+ audaac_in_record_config(audio, 0);
+ else
+ wake_up(&audio->wait_enable);
+ }
+ break;
+ }
+ case AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG: {
+ MM_DBG("CMD_ENC_PARAM_CFG_DONE_MSG\n");
+ if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)
+ audaac_in_mem_config(audio);
+ else
+ audpcm_config(audio);
+ break;
+ }
+ case AUDPREPROC_CMD_ROUTING_MODE_DONE_MSG: {
+ struct audpreproc_cmd_routing_mode_done\
+ *routing_cfg_done_msg = msg;
+ if (routing_cfg_done_msg->configuration == 0) {
+ MM_INFO("routing configuration failed\n");
+ audio->running = 0;
+ } else
+ audaac_in_param_config(audio);
+ break;
+ }
+ case AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG: {
+ MM_DBG("AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG\n");
+ wake_up(&audio->wait_enable);
+ break;
+ }
+ default:
+ MM_ERR("Unknown Event id %d\n", id);
+ }
+}
+
+/* ------------------- dsp audrec event handler--------------------- */
+static void audrec_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent)(void *ptr, size_t len))
+{
+ struct audio_in *audio = data;
+
+ switch (id) {
+ case AUDREC_CMD_MEM_CFG_DONE_MSG: {
+ MM_DBG("CMD_MEM_CFG_DONE MSG DONE\n");
+ audio->running = 1;
+ if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+ if (audio->dev_cnt > 0)
+ audaac_in_record_config(audio, 1);
+ } else {
+ audpreproc_pcm_send_data(audio, 1);
+ wake_up(&audio->wait_enable);
+ }
+ break;
+ }
+ case AUDREC_FATAL_ERR_MSG: {
+ struct audrec_fatal_err_msg fatal_err_msg;
+
+ getevent(&fatal_err_msg, AUDREC_FATAL_ERR_MSG_LEN);
+ MM_ERR("FATAL_ERR_MSG: err id %d\n",
+ fatal_err_msg.audrec_err_id);
+ /* Error stop the encoder */
+ audio->stopped = 1;
+ wake_up(&audio->wait);
+ if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL)
+ wake_up(&audio->write_wait);
+ break;
+ }
+ case AUDREC_UP_PACKET_READY_MSG: {
+ struct audrec_up_pkt_ready_msg pkt_ready_msg;
+
+ getevent(&pkt_ready_msg, AUDREC_UP_PACKET_READY_MSG_LEN);
+ MM_DBG("UP_PACKET_READY_MSG: write cnt lsw %d \
+ write cnt msw %d read cnt lsw %d read cnt msw %d \n",\
+ pkt_ready_msg.audrec_packet_write_cnt_lsw, \
+ pkt_ready_msg.audrec_packet_write_cnt_msw, \
+ pkt_ready_msg.audrec_up_prev_read_cnt_lsw, \
+ pkt_ready_msg.audrec_up_prev_read_cnt_msw);
+
+ audaac_in_get_dsp_frames(audio);
+ break;
+ }
+ case AUDREC_CMD_PCM_BUFFER_PTR_UPDATE_ARM_TO_ENC_MSG: {
+ MM_DBG("ptr_update recieved from DSP\n");
+ audpreproc_pcm_send_data(audio, 1);
+ break;
+ }
+ case AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG: {
+ MM_ERR("AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG");
+ audaac_in_mem_config(audio);
+ break;
+ }
+ case AUDREC_UP_NT_PACKET_READY_MSG: {
+ struct audrec_up_nt_packet_ready_msg pkt_ready_msg;
+
+ getevent(&pkt_ready_msg, AUDREC_UP_NT_PACKET_READY_MSG_LEN);
+ MM_DBG("UP_NT_PACKET_READY_MSG: write cnt lsw %d \
+ write cnt msw %d read cnt lsw %d read cnt msw %d \n",\
+ pkt_ready_msg.audrec_packetwrite_cnt_lsw, \
+ pkt_ready_msg.audrec_packetwrite_cnt_msw, \
+ pkt_ready_msg.audrec_upprev_readcount_lsw, \
+ pkt_ready_msg.audrec_upprev_readcount_msw);
+
+ audaac_nt_in_get_dsp_frames(audio);
+ break;
+ }
+ case AUDREC_CMD_EOS_ACK_MSG: {
+ MM_DBG("eos ack recieved\n");
+ break;
+ }
+ case AUDREC_CMD_FLUSH_DONE_MSG: {
+ audio->wflush = 0;
+ audio->rflush = 0;
+ audio->flush_ack = 1;
+ wake_up(&audio->write_wait);
+ MM_DBG("flush ack recieved\n");
+ break;
+ }
+ case ADSP_MESSAGE_ID: {
+ MM_DBG("Received ADSP event:module audrectask\n");
+ break;
+ }
+ default:
+ MM_ERR("Unknown Event id %d\n", id);
+ }
+}
+
+static void audaac_in_get_dsp_frames(struct audio_in *audio)
+{
+ struct audio_frame *frame;
+ uint32_t index;
+ unsigned long flags;
+
+ MM_DBG("head = %d\n", audio->in_head);
+ index = audio->in_head;
+
+ frame = (void *) (((char *)audio->in[index].data) - \
+ sizeof(*frame));
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->in[index].size = frame->frame_length;
+
+ /* statistics of read */
+ atomic_add(audio->in[index].size, &audio->in_bytes);
+ atomic_add(1, &audio->in_samples);
+
+ audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1);
+
+ /* If overflow, move the tail index foward. */
+ if (audio->in_head == audio->in_tail) {
+ MM_ERR("Error! not able to keep up the read\n");
+ audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1);
+ } else
+ audio->in_count++;
+
+ audaac_dsp_read_buffer(audio, audio->dsp_cnt++);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+
+ wake_up(&audio->wait);
+}
+
+static void audaac_nt_in_get_dsp_frames(struct audio_in *audio)
+{
+ struct audio_frame_nt *nt_frame;
+ uint32_t index;
+ unsigned long flags;
+ MM_DBG("head = %d\n", audio->in_head);
+ index = audio->in_head;
+ nt_frame = (void *) (((char *)audio->in[index].data) - \
+ sizeof(struct audio_frame_nt));
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->in[index].size = nt_frame->frame_length;
+ /* statistics of read */
+ atomic_add(audio->in[index].size, &audio->in_bytes);
+ atomic_add(1, &audio->in_samples);
+
+ audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1);
+
+ /* If overflow, move the tail index foward. */
+ if (audio->in_head == audio->in_tail)
+ MM_DBG("Error! not able to keep up the read\n");
+ else
+ audio->in_count++;
+
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ wake_up(&audio->wait);
+}
+
+
+struct msm_adsp_ops audrec_aac_adsp_ops = {
+ .event = audrec_dsp_event,
+};
+
+static int audpreproc_pcm_buffer_ptr_refresh(struct audio_in *audio,
+ unsigned idx, unsigned len)
+{
+ struct audrec_cmd_pcm_buffer_ptr_refresh_arm_enc cmd;
+
+ if (len == META_OUT_SIZE)
+ len = len / 2;
+ else
+ len = (len + META_OUT_SIZE) / 2;
+ MM_DBG("len = %d\n", len);
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDREC_CMD_PCM_BUFFER_PTR_REFRESH_ARM_TO_ENC;
+ cmd.num_buffers = 1;
+ if (cmd.num_buffers == 1) {
+ cmd.buf_address_length[0] = (audio->out[idx].addr &
+ 0xffff0000) >> 16;
+ cmd.buf_address_length[1] = (audio->out[idx].addr &
+ 0x0000ffff);
+ cmd.buf_address_length[2] = (len & 0xffff0000) >> 16;
+ cmd.buf_address_length[3] = (len & 0x0000ffff);
+ }
+ audio->out_frame_cnt++;
+ return audrec_send_audrecqueue(audio, (void *)&cmd,
+ (unsigned int)sizeof(cmd));
+}
+
+
+static int audpcm_config(struct audio_in *audio)
+{
+ struct audrec_cmd_pcm_cfg_arm_to_enc cmd;
+ MM_DBG("\n");
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDREC_CMD_PCM_CFG_ARM_TO_ENC;
+ cmd.config_update_flag = PCM_CONFIG_UPDATE_FLAG_ENABLE;
+ cmd.enable_flag = ENABLE_FLAG_VALUE;
+ cmd.sampling_freq = audio->samp_rate;
+ if (!audio->channel_mode)
+ cmd.channels = 1;
+ else
+ cmd.channels = 2;
+ cmd.frequency_of_intimation = 1;
+ cmd.max_number_of_buffers = OUT_FRAME_NUM;
+ return audrec_send_audrecqueue(audio, (void *)&cmd,
+ (unsigned int)sizeof(cmd));
+}
+
+
+static int audpreproc_cmd_cfg_routing_mode(struct audio_in *audio)
+{
+ struct audpreproc_audrec_cmd_routing_mode cmd;
+
+ MM_DBG("\n");
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ROUTING_MODE;
+ cmd.stream_id = audio->enc_id;
+ if (audio->mode == MSM_ADSP_ENC_MODE_NON_TUNNEL)
+ cmd.routing_mode = 1;
+ return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+
+
+static int audaac_in_enc_config(struct audio_in *audio, int enable)
+{
+ struct audpreproc_audrec_cmd_enc_cfg cmd;
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ENC_CFG_2;
+ cmd.stream_id = audio->enc_id;
+
+ if (enable)
+ cmd.audrec_enc_type = audio->enc_type | ENCODE_ENABLE;
+ else
+ cmd.audrec_enc_type &= ~(ENCODE_ENABLE);
+
+ return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+static int audaac_in_param_config(struct audio_in *audio)
+{
+ struct audpreproc_audrec_cmd_parm_cfg_aac cmd;
+
+ MM_DBG("\n");
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.cmd_id = AUDPREPROC_AUDREC_CMD_PARAM_CFG;
+ cmd.common.stream_id = audio->enc_id;
+
+ cmd.aud_rec_samplerate_idx = audio->samp_rate;
+ cmd.aud_rec_stereo_mode = audio->channel_mode;
+ cmd.recording_quality = audio->record_quality;
+
+ return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+/* To Do: msm_snddev_route_enc(audio->enc_id); */
+static int audaac_in_record_config(struct audio_in *audio, int enable)
+{
+ struct audpreproc_afe_cmd_audio_record_cfg cmd;
+ MM_DBG("\n");
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG;
+ cmd.stream_id = audio->enc_id;
+ if (enable)
+ cmd.destination_activity = AUDIO_RECORDING_TURN_ON;
+ else
+ cmd.destination_activity = AUDIO_RECORDING_TURN_OFF;
+
+ cmd.source_mix_mask = audio->source;
+ if (audio->enc_id == 2) {
+ if ((cmd.source_mix_mask & INTERNAL_CODEC_TX_SOURCE_MIX_MASK) ||
+ (cmd.source_mix_mask & AUX_CODEC_TX_SOURCE_MIX_MASK) ||
+ (cmd.source_mix_mask & VOICE_UL_SOURCE_MIX_MASK) ||
+ (cmd.source_mix_mask & VOICE_DL_SOURCE_MIX_MASK)) {
+ cmd.pipe_id = SOURCE_PIPE_1;
+ }
+ if (cmd.source_mix_mask &
+ AUDPP_A2DP_PIPE_SOURCE_MIX_MASK)
+ cmd.pipe_id |= SOURCE_PIPE_0;
+ }
+ return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+static int audaac_in_mem_config(struct audio_in *audio)
+{
+ struct audrec_cmd_arecmem_cfg cmd;
+ uint16_t *data = (void *) audio->data;
+ int n;
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDREC_CMD_MEM_CFG_CMD;
+ cmd.audrec_up_pkt_intm_count = 1;
+ cmd.audrec_ext_pkt_start_addr_msw = audio->phys >> 16;
+ cmd.audrec_ext_pkt_start_addr_lsw = audio->phys;
+ cmd.audrec_ext_pkt_buf_number = FRAME_NUM;
+ MM_DBG("audio->phys = %x\n", audio->phys);
+ /* prepare buffer pointers:
+ * 1536 bytes aac packet + 4 halfword header
+ */
+ for (n = 0; n < FRAME_NUM; n++) {
+ if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+ audio->in[n].data = data + 4;
+ data += (FRAME_SIZE/2);
+ MM_DBG("0x%8x\n", (int)(audio->in[n].data - 8));
+ } else {
+ audio->in[n].data = data + 12;
+ data += ((AAC_FRAME_SIZE) / 2) + 12;
+ MM_DBG("0x%8x\n", (int)(audio->in[n].data - 24));
+ }
+ }
+ return audrec_send_audrecqueue(audio, &cmd, sizeof(cmd));
+}
+
+static int audaac_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt)
+{
+ struct up_audrec_packet_ext_ptr cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = UP_AUDREC_PACKET_EXT_PTR;
+ cmd.audrec_up_curr_read_count_msw = read_cnt >> 16;
+ cmd.audrec_up_curr_read_count_lsw = read_cnt;
+
+ return audrec_send_bitstreamqueue(audio, &cmd, sizeof(cmd));
+}
+static int audaac_flush_command(struct audio_in *audio)
+{
+ struct audrec_cmd_flush cmd;
+ MM_DBG("\n");
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDREC_CMD_FLUSH;
+ return audrec_send_audrecqueue(audio, &cmd, sizeof(cmd));
+}
+
+/* must be called with audio->lock held */
+static int audaac_in_enable(struct audio_in *audio)
+{
+ if (audio->enabled)
+ return 0;
+
+ if (audpreproc_enable(audio->enc_id, &audpreproc_dsp_event, audio)) {
+ MM_ERR("msm_adsp_enable(audpreproc) failed\n");
+ return -ENODEV;
+ }
+
+ if (msm_adsp_enable(audio->audrec)) {
+ MM_ERR("msm_adsp_enable(audrec) failed\n");
+ audpreproc_disable(audio->enc_id, audio);
+ return -ENODEV;
+ }
+ audio->enabled = 1;
+ audaac_in_enc_config(audio, 1);
+
+ return 0;
+}
+
+/* must be called with audio->lock held */
+static int audaac_in_disable(struct audio_in *audio)
+{
+ if (audio->enabled) {
+ audio->enabled = 0;
+ audaac_in_enc_config(audio, 0);
+ wake_up(&audio->wait);
+ wait_event_interruptible_timeout(audio->wait_enable,
+ audio->running == 0, 1*HZ);
+ msm_adsp_disable(audio->audrec);
+ audpreproc_disable(audio->enc_id, audio);
+ }
+ return 0;
+}
+
+static void audaac_ioport_reset(struct audio_in *audio)
+{
+ /* Make sure read/write thread are free from
+ * sleep and knowing that system is not able
+ * to process io request at the moment
+ */
+ wake_up(&audio->write_wait);
+ mutex_lock(&audio->write_lock);
+ audaac_in_flush(audio);
+ mutex_unlock(&audio->write_lock);
+ wake_up(&audio->wait);
+ mutex_lock(&audio->read_lock);
+ audaac_out_flush(audio);
+ mutex_unlock(&audio->read_lock);
+}
+
+static void audaac_in_flush(struct audio_in *audio)
+{
+ int i;
+
+ audio->dsp_cnt = 0;
+ audio->in_head = 0;
+ audio->in_tail = 0;
+ audio->in_count = 0;
+ audio->eos_ack = 0;
+ for (i = 0; i < FRAME_NUM; i++) {
+ audio->in[i].size = 0;
+ audio->in[i].read = 0;
+ }
+ MM_DBG("in_bytes %d\n", atomic_read(&audio->in_bytes));
+ MM_DBG("in_samples %d\n", atomic_read(&audio->in_samples));
+ atomic_set(&audio->in_bytes, 0);
+ atomic_set(&audio->in_samples, 0);
+}
+
+static void audaac_out_flush(struct audio_in *audio)
+{
+ int i;
+
+ audio->out_head = 0;
+ audio->out_tail = 0;
+ audio->out_count = 0;
+ for (i = 0; i < OUT_FRAME_NUM; i++) {
+ audio->out[i].size = 0;
+ audio->out[i].read = 0;
+ audio->out[i].used = 0;
+ }
+}
+
+/* ------------------- device --------------------- */
+static long audaac_in_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct audio_in *audio = file->private_data;
+ int rc = 0;
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ stats.byte_count = atomic_read(&audio->in_bytes);
+ stats.sample_count = atomic_read(&audio->in_samples);
+ if (copy_to_user((void *) arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return rc;
+ }
+
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START: {
+ uint32_t freq;
+ /* Poll at 48KHz always */
+ freq = 48000;
+ MM_DBG("AUDIO_START\n");
+ rc = msm_snddev_request_freq(&freq, audio->enc_id,
+ SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+ MM_DBG("sample rate configured %d sample rate requested %d\n",
+ freq, audio->samp_rate);
+ if (rc < 0) {
+ MM_DBG(" Sample rate can not be set, return code %d\n",
+ rc);
+ msm_snddev_withdraw_freq(audio->enc_id,
+ SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+ MM_DBG("msm_snddev_withdraw_freq\n");
+ break;
+ }
+ /*update aurec session info in audpreproc layer*/
+ audio->session_info.session_id = audio->enc_id;
+ audio->session_info.sampling_freq = audio->samp_rate;
+ audpreproc_update_audrec_info(&audio->session_info);
+ rc = audaac_in_enable(audio);
+ if (!rc) {
+ rc =
+ wait_event_interruptible_timeout(audio->wait_enable,
+ audio->running != 0, 1*HZ);
+ MM_DBG("state %d rc = %d\n", audio->running, rc);
+
+ if (audio->running == 0)
+ rc = -ENODEV;
+ else
+ rc = 0;
+ }
+ audio->stopped = 0;
+ break;
+ }
+ case AUDIO_STOP: {
+ audio->session_info.sampling_freq = 0;
+ audpreproc_update_audrec_info(&audio->session_info);
+ rc = audaac_in_disable(audio);
+ rc = msm_snddev_withdraw_freq(audio->enc_id,
+ SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+ MM_DBG("msm_snddev_withdraw_freq\n");
+ audio->stopped = 1;
+ audio->abort = 0;
+ break;
+ }
+ case AUDIO_FLUSH:
+ MM_DBG("AUDIO_FLUSH\n");
+ audio->rflush = 1;
+ audio->wflush = 1;
+ audaac_ioport_reset(audio);
+ if (audio->running) {
+ audaac_flush_command(audio);
+ rc = wait_event_interruptible(audio->write_wait,
+ !audio->wflush);
+ if (rc < 0) {
+ MM_ERR("AUDIO_FLUSH interrupted\n");
+ rc = -EINTR;
+ }
+ } else {
+ audio->rflush = 0;
+ audio->wflush = 0;
+ }
+ break;
+ case AUDIO_GET_STREAM_CONFIG: {
+ struct msm_audio_stream_config cfg;
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.buffer_size = audio->buffer_size;
+ cfg.buffer_count = FRAME_NUM;
+ if (copy_to_user((void *)arg, &cfg, sizeof(cfg)))
+ rc = -EFAULT;
+ break;
+ }
+ case AUDIO_SET_STREAM_CONFIG: {
+ struct msm_audio_stream_config cfg;
+ if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
+ rc = -EFAULT;
+ break;
+ }
+ /* Allow only single frame */
+ if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+ if (cfg.buffer_size != (FRAME_SIZE - 8)) {
+ rc = -EINVAL;
+ break;
+ }
+ } else {
+ if (cfg.buffer_size != (NT_FRAME_SIZE - 24)) {
+ rc = -EINVAL;
+ break;
+ }
+ }
+ audio->buffer_size = cfg.buffer_size;
+ break;
+ }
+ case AUDIO_GET_CONFIG: {
+ struct msm_audio_pcm_config cfg;
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.buffer_size = OUT_BUFFER_SIZE;
+ cfg.buffer_count = OUT_FRAME_NUM;
+ if (copy_to_user((void *)arg, &cfg, sizeof(cfg)))
+ rc = -EFAULT;
+ break;
+ }
+ case AUDIO_GET_AAC_ENC_CONFIG: {
+ struct msm_audio_aac_enc_config cfg;
+ if (audio->channel_mode == AUDREC_CMD_MODE_MONO)
+ cfg.channels = 1;
+ else
+ cfg.channels = 2;
+ cfg.sample_rate = audio->samp_rate;
+ cfg.bit_rate = audio->bit_rate;
+ cfg.stream_format = AUDIO_AAC_FORMAT_RAW;
+ if (copy_to_user((void *)arg, &cfg, sizeof(cfg)))
+ rc = -EFAULT;
+ break;
+ }
+ case AUDIO_SET_AAC_ENC_CONFIG: {
+ struct msm_audio_aac_enc_config cfg;
+ unsigned int record_quality;
+ if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (cfg.stream_format != AUDIO_AAC_FORMAT_RAW) {
+ MM_ERR("unsupported AAC format\n");
+ rc = -EINVAL;
+ break;
+ }
+ record_quality = bitrate_to_record_quality(cfg.sample_rate,
+ cfg.channels, cfg.bit_rate);
+ /* Range of Record Quality Supported by DSP, Q12 format */
+ if ((record_quality < 0x800) || (record_quality > 0x4000)) {
+ MM_ERR("Unsupported bit rate \n");
+ rc = -EINVAL;
+ break;
+ }
+ MM_DBG("channels = %d\n", cfg.channels);
+ if (cfg.channels == 1) {
+ cfg.channels = AUDREC_CMD_MODE_MONO;
+ } else if (cfg.channels == 2) {
+ cfg.channels = AUDREC_CMD_MODE_STEREO;
+ } else {
+ rc = -EINVAL;
+ break;
+ }
+ MM_DBG("channels = %d\n", cfg.channels);
+ audio->samp_rate = cfg.sample_rate;
+ audio->channel_mode = cfg.channels;
+ audio->bit_rate = cfg.bit_rate;
+ audio->record_quality = record_quality;
+ MM_DBG(" Record Quality = 0x%8x \n", audio->record_quality);
+ break;
+ }
+ case AUDIO_GET_SESSION_ID: {
+ if (copy_to_user((void *) arg, &audio->enc_id,
+ sizeof(unsigned short))) {
+ rc = -EFAULT;
+ }
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static ssize_t audaac_in_read(struct file *file,
+ char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio_in *audio = file->private_data;
+ unsigned long flags;
+ const char __user *start = buf;
+ void *data;
+ uint32_t index;
+ uint32_t size;
+ int rc = 0;
+ struct aac_encoded_meta_in meta_field;
+ struct audio_frame_nt *nt_frame;
+ MM_DBG(" count = %d\n", count);
+ mutex_lock(&audio->read_lock);
+ while (count > 0) {
+ rc = wait_event_interruptible(
+ audio->wait, (audio->in_count > 0) || audio->stopped ||
+ audio->abort || audio->rflush);
+
+ if (rc < 0)
+ break;
+
+ if (audio->rflush) {
+ rc = -EBUSY;
+ break;
+ }
+
+ if (audio->stopped && !audio->in_count) {
+ MM_DBG("Driver in stop state, No more buffer to read");
+ rc = 0;/* End of File */
+ break;
+ }
+
+ if (audio->abort) {
+ rc = -EPERM; /* Not permitted due to abort */
+ break;
+ }
+
+ index = audio->in_tail;
+ data = (uint8_t *) audio->in[index].data;
+ size = audio->in[index].size;
+
+ if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) {
+ nt_frame = (struct audio_frame_nt *)(data -
+ sizeof(struct audio_frame_nt));
+ memcpy((char *)&meta_field.time_stamp_dword_lsw,
+ (char *)&nt_frame->time_stamp_dword_lsw, 12);
+ meta_field.metadata_len =
+ sizeof(struct aac_encoded_meta_in);
+ if (copy_to_user((char *)start, (char *)&meta_field,
+ sizeof(struct aac_encoded_meta_in))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (nt_frame->nflag_lsw & 0x0001) {
+ MM_ERR("recieved EOS in read call\n");
+ audio->eos_ack = 1;
+ }
+ buf += sizeof(struct aac_encoded_meta_in);
+ count -= sizeof(struct aac_encoded_meta_in);
+ }
+ if (count >= size) {
+ if (copy_to_user(buf, data, size)) {
+ rc = -EFAULT;
+ break;
+ }
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (index != audio->in_tail) {
+ /* overrun -- data is
+ * invalid and we need to retry */
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ continue;
+ }
+ audio->in[index].size = 0;
+ audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1);
+ audio->in_count--;
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ count -= size;
+ buf += size;
+ if ((audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) &&
+ (!audio->eos_ack)) {
+ MM_DBG("sending read ptr command %d %d\n",
+ audio->dsp_cnt,
+ audio->in_tail);
+ audaac_dsp_read_buffer(audio,
+ audio->dsp_cnt++);
+ break;
+ }
+ } else {
+ MM_ERR("short read\n");
+ break;
+ }
+ break;
+ }
+ mutex_unlock(&audio->read_lock);
+ if (buf > start)
+ return buf - start;
+
+ return rc;
+}
+
+static void audpreproc_pcm_send_data(struct audio_in *audio, unsigned needed)
+{
+ struct buffer *frame;
+ unsigned long flags;
+ MM_DBG("\n");
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (!audio->running)
+ goto done;
+
+ if (needed && !audio->wflush) {
+ /* We were called from the callback because the DSP
+ * requested more data. Note that the DSP does want
+ * more data, and if a buffer was in-flight, mark it
+ * as available (since the DSP must now be done with
+ * it).
+ */
+ audio->out_needed = 1;
+ frame = audio->out + audio->out_tail;
+ if (frame->used == 0xffffffff) {
+ MM_DBG("frame %d free\n", audio->out_tail);
+ frame->used = 0;
+ audio->out_tail ^= 1;
+ wake_up(&audio->write_wait);
+ }
+ }
+
+ if (audio->out_needed) {
+ /* If the DSP currently wants data and we have a
+ * buffer available, we will send it and reset
+ * the needed flag. We'll mark the buffer as in-flight
+ * so that it won't be recycled until the next buffer
+ * is requested
+ */
+
+ frame = audio->out + audio->out_tail;
+ if (frame->used) {
+ BUG_ON(frame->used == 0xffffffff);
+ MM_DBG("frame %d busy\n", audio->out_tail);
+ audpreproc_pcm_buffer_ptr_refresh(audio,
+ audio->out_tail,
+ frame->used);
+ frame->used = 0xffffffff;
+ audio->out_needed = 0;
+ }
+ }
+ done:
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+
+static int audaac_in_fsync(struct file *file, int datasync)
+
+{
+ struct audio_in *audio = file->private_data;
+ int rc = 0;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ if (!audio->running || (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)) {
+ rc = -EINVAL;
+ goto done_nolock;
+ }
+
+ mutex_lock(&audio->write_lock);
+
+ rc = wait_event_interruptible(audio->write_wait,
+ audio->wflush);
+ MM_DBG("waked on by some event audio->wflush = %d\n", audio->wflush);
+
+ if (rc < 0)
+ goto done;
+ else if (audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+done:
+ mutex_unlock(&audio->write_lock);
+done_nolock:
+ return rc;
+
+}
+
+ int audpreproc_aac_process_eos(struct audio_in *audio,
+ const char __user *buf_start, unsigned short mfield_size)
+{
+ struct buffer *frame;
+ int rc = 0;
+
+ frame = audio->out + audio->out_head;
+
+ rc = wait_event_interruptible(audio->write_wait,
+ (audio->out_needed &&
+ audio->out[0].used == 0 &&
+ audio->out[1].used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+
+ if (rc < 0)
+ goto done;
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+ if (copy_from_user(frame->data, buf_start, mfield_size)) {
+ rc = -EFAULT;
+ goto done;
+ }
+
+ frame->mfield_sz = mfield_size;
+ audio->out_head ^= 1;
+ frame->used = mfield_size;
+ MM_DBG("copying meta_out frame->used = %d\n", frame->used);
+ audpreproc_pcm_send_data(audio, 0);
+done:
+ return rc;
+}
+
+static ssize_t audaac_in_write(struct file *file,
+ const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio_in *audio = file->private_data;
+ const char __user *start = buf;
+ struct buffer *frame;
+ char *cpy_ptr;
+ int rc = 0, eos_condition = AUDPREPROC_AAC_EOS_NONE;
+ unsigned short mfield_size = 0;
+ int write_count = count;
+ MM_DBG("cnt=%d\n", count);
+
+ if (count & 1)
+ return -EINVAL;
+
+ mutex_lock(&audio->write_lock);
+ frame = audio->out + audio->out_head;
+ cpy_ptr = frame->data;
+ rc = wait_event_interruptible(audio->write_wait,
+ (frame->used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+ if (rc < 0)
+ goto error;
+
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ goto error;
+ }
+ if (audio->mfield) {
+ if (buf == start) {
+ /* Processing beginning of user buffer */
+ if (__get_user(mfield_size,
+ (unsigned short __user *) buf)) {
+ rc = -EFAULT;
+ goto error;
+ } else if (mfield_size > count) {
+ rc = -EINVAL;
+ goto error;
+ }
+ MM_DBG("mf offset_val %x\n", mfield_size);
+ if (copy_from_user(cpy_ptr, buf, mfield_size)) {
+ rc = -EFAULT;
+ goto error;
+ }
+ /* Check if EOS flag is set and buffer has
+ * contains just meta field
+ */
+ if (cpy_ptr[AUDPREPROC_AAC_EOS_FLG_OFFSET] &
+ AUDPREPROC_AAC_EOS_FLG_MASK) {
+ MM_DBG("EOS SET\n");
+ eos_condition = AUDPREPROC_AAC_EOS_SET;
+ if (mfield_size == count) {
+ buf += mfield_size;
+ if (audio->mode ==
+ MSM_AUD_ENC_MODE_NONTUNNEL) {
+ eos_condition = 0;
+ goto exit;
+ }
+ goto error;
+ } else
+ cpy_ptr[AUDPREPROC_AAC_EOS_FLG_OFFSET] &=
+ ~AUDPREPROC_AAC_EOS_FLG_MASK;
+ }
+ cpy_ptr += mfield_size;
+ count -= mfield_size;
+ buf += mfield_size;
+ } else {
+ mfield_size = 0;
+ MM_DBG("continuous buffer\n");
+ }
+ frame->mfield_sz = mfield_size;
+ }
+ MM_DBG("copying the stream count = %d\n", count);
+ if (copy_from_user(cpy_ptr, buf, count)) {
+ rc = -EFAULT;
+ goto error;
+ }
+exit:
+ frame->used = count;
+ audio->out_head ^= 1;
+ if (!audio->flush_ack)
+ audpreproc_pcm_send_data(audio, 0);
+ else {
+ audpreproc_pcm_send_data(audio, 1);
+ audio->flush_ack = 0;
+ }
+ if (eos_condition == AUDPREPROC_AAC_EOS_SET)
+ rc = audpreproc_aac_process_eos(audio, start, mfield_size);
+ mutex_unlock(&audio->write_lock);
+ return write_count;
+error:
+ mutex_unlock(&audio->write_lock);
+ return rc;
+}
+
+static int audaac_in_release(struct inode *inode, struct file *file)
+{
+ struct audio_in *audio = file->private_data;
+
+ mutex_lock(&audio->lock);
+ /* with draw frequency for session
+ incase not stopped the driver */
+ msm_snddev_withdraw_freq(audio->enc_id, SNDDEV_CAP_TX,
+ AUDDEV_CLNT_ENC);
+ auddev_unregister_evt_listner(AUDDEV_CLNT_ENC, audio->enc_id);
+ /*reset the sampling frequency information at audpreproc layer*/
+ audio->session_info.sampling_freq = 0;
+ audpreproc_update_audrec_info(&audio->session_info);
+ audaac_in_disable(audio);
+ audaac_in_flush(audio);
+ msm_adsp_put(audio->audrec);
+ audpreproc_aenc_free(audio->enc_id);
+ audio->audrec = NULL;
+ audio->opened = 0;
+ if (audio->data) {
+ iounmap(audio->data);
+ pmem_kfree(audio->phys);
+ audio->data = NULL;
+ }
+ if (audio->out_data) {
+ iounmap(audio->out_data);
+ pmem_kfree(audio->out_phys);
+ audio->out_data = NULL;
+ }
+ mutex_unlock(&audio->lock);
+ return 0;
+}
+
+struct audio_in the_audio_aac_in;
+
+static int audaac_in_open(struct inode *inode, struct file *file)
+{
+ struct audio_in *audio = &the_audio_aac_in;
+ int rc;
+ int encid;
+
+ mutex_lock(&audio->lock);
+ if (audio->opened) {
+ rc = -EBUSY;
+ goto done;
+ }
+ audio->phys = pmem_kalloc(DMASZ, PMEM_MEMTYPE_EBI1|
+ PMEM_ALIGNMENT_4K);
+ if (!IS_ERR((void *)audio->phys)) {
+ audio->data = ioremap(audio->phys, DMASZ);
+ if (!audio->data) {
+ MM_ERR("could not allocate DMA buffers\n");
+ rc = -ENOMEM;
+ pmem_kfree(audio->phys);
+ goto done;
+ }
+ } else {
+ MM_ERR("could not allocate DMA buffers\n");
+ rc = -ENOMEM;
+ goto done;
+ }
+ MM_DBG("Memory addr = 0x%8x phy addr = 0x%8x\n",\
+ (int) audio->data, (int) audio->phys);
+ if ((file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ audio->mode = MSM_AUD_ENC_MODE_NONTUNNEL;
+ } else if (!(file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ audio->mode = MSM_AUD_ENC_MODE_TUNNEL;
+ MM_DBG("Opened for tunnel mode encoding\n");
+ } else {
+ rc = -EACCES;
+ goto done;
+ }
+
+ /* Settings will be re-config at AUDIO_SET_CONFIG,
+ * but at least we need to have initial config
+ */
+ if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL)
+ audio->buffer_size = (NT_FRAME_SIZE - 24);
+ else
+ audio->buffer_size = (FRAME_SIZE - 8);
+ audio->enc_type = ENC_TYPE_AAC | audio->mode;
+ audio->samp_rate = 8000;
+ audio->channel_mode = AUDREC_CMD_MODE_MONO;
+ /* For AAC, bit rate hard coded, default settings is
+ * sample rate (8000) x channel count (1) x recording quality (1.75)
+ * = 14000 bps */
+ audio->bit_rate = 14000;
+ audio->record_quality = 0x1c00;
+ MM_DBG("enc_type = %x\n", audio->enc_type);
+ encid = audpreproc_aenc_alloc(audio->enc_type, &audio->module_name,
+ &audio->queue_ids);
+ if (encid < 0) {
+ MM_ERR("No free encoder available\n");
+ rc = -ENODEV;
+ goto done;
+ }
+ audio->enc_id = encid;
+
+ rc = msm_adsp_get(audio->module_name, &audio->audrec,
+ &audrec_aac_adsp_ops, audio);
+
+ if (rc) {
+ audpreproc_aenc_free(audio->enc_id);
+ goto done;
+ }
+
+ audio->stopped = 0;
+ audio->source = 0;
+ audio->abort = 0;
+ audio->wflush = 0;
+ audio->rflush = 0;
+ audio->flush_ack = 0;
+
+ audaac_in_flush(audio);
+ audaac_out_flush(audio);
+
+ audio->out_phys = pmem_kalloc(BUFFER_SIZE,
+ PMEM_MEMTYPE_EBI1 | PMEM_ALIGNMENT_4K);
+ if (IS_ERR((void *)audio->out_phys)) {
+ MM_ERR("could not allocate write buffers\n");
+ rc = -ENOMEM;
+ goto evt_error;
+ } else {
+ audio->out_data = ioremap(audio->out_phys, BUFFER_SIZE);
+ if (!audio->out_data) {
+ MM_ERR("could not allocate write buffers\n");
+ rc = -ENOMEM;
+ pmem_kfree(audio->out_phys);
+ goto evt_error;
+ }
+ MM_DBG("write buf: phy addr 0x%08x kernel addr 0x%08x\n",
+ audio->out_phys, (int)audio->out_data);
+ }
+
+ /* Initialize buffer */
+ audio->out[0].data = audio->out_data + 0;
+ audio->out[0].addr = audio->out_phys + 0;
+ audio->out[0].size = OUT_BUFFER_SIZE;
+
+ audio->out[1].data = audio->out_data + OUT_BUFFER_SIZE;
+ audio->out[1].addr = audio->out_phys + OUT_BUFFER_SIZE;
+ audio->out[1].size = OUT_BUFFER_SIZE;
+
+ MM_DBG("audio->out[0].data = %d audio->out[1].data = %d",
+ (unsigned int)audio->out[0].data,
+ (unsigned int)audio->out[1].data);
+ audio->device_events = AUDDEV_EVT_DEV_RDY | AUDDEV_EVT_DEV_RLS |
+ AUDDEV_EVT_FREQ_CHG;
+
+ rc = auddev_register_evt_listner(audio->device_events,
+ AUDDEV_CLNT_ENC, audio->enc_id,
+ aac_in_listener, (void *) audio);
+ if (rc) {
+ MM_ERR("failed to register device event listener\n");
+ iounmap(audio->out_data);
+ pmem_kfree(audio->out_phys);
+ goto evt_error;
+ }
+ audio->mfield = META_OUT_SIZE;
+ file->private_data = audio;
+ audio->opened = 1;
+ audio->out_frame_cnt++;
+ rc = 0;
+done:
+ mutex_unlock(&audio->lock);
+ return rc;
+evt_error:
+ msm_adsp_put(audio->audrec);
+ audpreproc_aenc_free(audio->enc_id);
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static const struct file_operations audio_in_fops = {
+ .owner = THIS_MODULE,
+ .open = audaac_in_open,
+ .release = audaac_in_release,
+ .read = audaac_in_read,
+ .write = audaac_in_write,
+ .fsync = audaac_in_fsync,
+ .unlocked_ioctl = audaac_in_ioctl,
+};
+
+struct miscdevice audio_aac_in_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_aac_in",
+ .fops = &audio_in_fops,
+};
+
+static int __init audaac_in_init(void)
+{
+ mutex_init(&the_audio_aac_in.lock);
+ mutex_init(&the_audio_aac_in.read_lock);
+ spin_lock_init(&the_audio_aac_in.dsp_lock);
+ spin_lock_init(&the_audio_aac_in.dev_lock);
+ init_waitqueue_head(&the_audio_aac_in.wait);
+ init_waitqueue_head(&the_audio_aac_in.wait_enable);
+ mutex_init(&the_audio_aac_in.write_lock);
+ init_waitqueue_head(&the_audio_aac_in.write_wait);
+
+ return misc_register(&audio_aac_in_misc);
+}
+
+device_initcall(audaac_in_init);
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_acdb.c b/arch/arm/mach-msm/qdsp5v2/audio_acdb.c
new file mode 100644
index 0000000..84406cd
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_acdb.c
@@ -0,0 +1,3407 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+#include <linux/android_pmem.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/uaccess.h>
+#include <linux/msm_audio.h>
+#include <linux/slab.h>
+#include <mach/dal.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/qdsp5v2/audpp.h>
+#include <mach/qdsp5v2/audpreproc.h>
+#include <mach/qdsp5v2/qdsp5audppcmdi.h>
+#include <mach/qdsp5v2/qdsp5audpreproccmdi.h>
+#include <mach/qdsp5v2/qdsp5audpreprocmsg.h>
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/afe.h>
+#include <mach/qdsp5v2/audio_acdbi.h>
+#include <mach/qdsp5v2/acdb_commands.h>
+#include <mach/qdsp5v2/audio_acdb_def.h>
+#include <linux/mfd/marimba.h>
+#include <mach/debug_mm.h>
+#include <linux/debugfs.h>
+
+/* this is the ACDB device ID */
+#define DALDEVICEID_ACDB 0x02000069
+#define ACDB_PORT_NAME "DAL00"
+#define ACDB_CPU SMD_APPS_MODEM
+#define ACDB_BUF_SIZE 4096
+#define PBE_BUF_SIZE (33*1024)
+#define FLUENCE_BUF_SIZE 498
+
+#define ACDB_VALUES_NOT_FILLED 0
+#define ACDB_VALUES_FILLED 1
+#define MAX_RETRY 10
+
+/*below macro is used to align the session info received from
+Devctl driver with the state mentioned as not to alter the
+Existing code*/
+#define AUDREC_OFFSET 2
+/* rpc table index */
+enum {
+ ACDB_DalACDB_ioctl = DALDEVICE_FIRST_DEVICE_API_IDX
+};
+
+enum {
+ CAL_DATA_READY = 0x1,
+ AUDPP_READY = 0x2,
+ AUDREC0_READY = 0x4,
+ AUDREC1_READY = 0x8,
+ AUDREC2_READY = 0x10,
+};
+
+
+struct acdb_data {
+ void *handle;
+
+ u32 phys_addr;
+ u8 *virt_addr;
+
+ struct task_struct *cb_thread_task;
+ struct auddev_evt_audcal_info *device_info;
+
+ u32 acdb_state;
+ struct audpp_event_callback audpp_cb;
+ struct audpreproc_event_callback audpreproc_cb;
+
+ struct audpp_cmd_cfg_object_params_pcm *pp_iir;
+ struct audpp_cmd_cfg_cal_gain *calib_gain_rx;
+ struct audpp_cmd_cfg_pbe *pbe_block;
+ struct audpp_cmd_cfg_object_params_mbadrc *pp_mbadrc;
+ struct audpreproc_cmd_cfg_agc_params *preproc_agc;
+ struct audpreproc_cmd_cfg_iir_tuning_filter_params *preproc_iir;
+ struct audpreproc_cmd_cfg_cal_gain *calib_gain_tx;
+ struct acdb_mbadrc_block mbadrc_block;
+ struct audpreproc_cmd_cfg_lvnv_param preproc_lvnv;
+
+ wait_queue_head_t wait;
+ struct mutex acdb_mutex;
+ u32 device_cb_compl;
+ u32 audpp_cb_compl;
+ u32 preproc_cb_compl;
+ u8 preproc_stream_id;
+ u8 audrec_applied;
+ u32 multiple_sessions;
+ u32 cur_tx_session;
+ struct acdb_result acdb_result;
+ u16 *pbe_extbuff;
+ u16 *pbe_enable_flag;
+ u32 fluence_extbuff;
+ u8 *fluence_extbuff_virt;
+
+ struct acdb_pbe_block *pbe_blk;
+
+ spinlock_t dsp_lock;
+ int dec_id;
+ struct audpp_cmd_cfg_object_params_eqalizer eq;
+ /*status to enable or disable the fluence*/
+ int fleuce_feature_status[MAX_AUDREC_SESSIONS];
+ struct audrec_session_info session_info;
+ /*pmem info*/
+ int pmem_fd;
+ unsigned long paddr;
+ unsigned long kvaddr;
+ unsigned long pmem_len;
+ struct file *file;
+ /* pmem for get acdb blk */
+ unsigned long get_blk_paddr;
+ u8 *get_blk_kvaddr;
+};
+
+static struct acdb_data acdb_data;
+
+struct acdb_cache_node {
+ u32 node_status;
+ s32 stream_id;
+ u32 phys_addr_acdb_values;
+ u8 *virt_addr_acdb_values;
+ struct auddev_evt_audcal_info device_info;
+};
+
+/*for RX devices acdb values are applied based on copp ID so
+the depth of tx cache is MAX number of COPP supported in the system*/
+struct acdb_cache_node acdb_cache_rx[MAX_COPP_NODE_SUPPORTED];
+
+/*for TX devices acdb values are applied based on AUDREC session and
+the depth of the tx cache is define by number of AUDREC sessions supported*/
+struct acdb_cache_node acdb_cache_tx[MAX_AUDREC_SESSIONS];
+
+/*Audrec session info includes Attributes Sampling frequency and enc_id */
+struct audrec_session_info session_info[MAX_AUDREC_SESSIONS];
+#ifdef CONFIG_DEBUG_FS
+
+#define RTC_MAX_TIMEOUT 500 /* 500 ms */
+#define PMEM_RTC_ACDB_QUERY_MEM 4096
+#define EXTRACT_HIGH_WORD(x) ((x & 0xFFFF0000)>>16)
+#define EXTRACT_LOW_WORD(x) (0x0000FFFF & x)
+#define ACDB_RTC_TX 0xF1
+#define ACDB_RTC_RX 0x1F
+
+
+static u32 acdb_audpp_entry[][4] = {
+
+ { ABID_AUDIO_RTC_VOLUME_PAN_RX,\
+ IID_AUDIO_RTC_VOLUME_PAN_PARAMETERS,\
+ AUDPP_CMD_VOLUME_PAN,\
+ ACDB_RTC_RX
+ },
+ { ABID_AUDIO_IIR_RX,\
+ IID_AUDIO_IIR_COEFF,\
+ AUDPP_CMD_IIR_TUNING_FILTER,
+ ACDB_RTC_RX
+ },
+ { ABID_AUDIO_RTC_EQUALIZER_PARAMETERS,\
+ IID_AUDIO_RTC_EQUALIZER_PARAMETERS,\
+ AUDPP_CMD_EQUALIZER,\
+ ACDB_RTC_RX
+ },
+ { ABID_AUDIO_RTC_SPA,\
+ IID_AUDIO_RTC_SPA_PARAMETERS,\
+ AUDPP_CMD_SPECTROGRAM,
+ ACDB_RTC_RX
+ },
+ { ABID_AUDIO_STF_RX,\
+ IID_AUDIO_IIR_COEFF,\
+ AUDPP_CMD_SIDECHAIN_TUNING_FILTER,\
+ ACDB_RTC_RX
+ },
+ {
+ ABID_AUDIO_MBADRC_RX,\
+ IID_AUDIO_RTC_MBADRC_PARAMETERS,\
+ AUDPP_CMD_MBADRC,\
+ ACDB_RTC_RX
+ },
+ {
+ ABID_AUDIO_AGC_TX,\
+ IID_AUDIO_AGC_PARAMETERS,\
+ AUDPREPROC_CMD_CFG_AGC_PARAMS,\
+ ACDB_RTC_TX
+ },
+ {
+ ABID_AUDIO_AGC_TX,\
+ IID_AUDIO_RTC_AGC_PARAMETERS,\
+ AUDPREPROC_CMD_CFG_AGC_PARAMS,\
+ ACDB_RTC_TX
+ },
+ {
+ ABID_AUDIO_NS_TX,\
+ IID_NS_PARAMETERS,\
+ AUDPREPROC_CMD_CFG_NS_PARAMS,\
+ ACDB_RTC_TX
+ },
+ {
+ ABID_AUDIO_IIR_TX,\
+ IID_AUDIO_RTC_TX_IIR_COEFF,\
+ AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS,\
+ ACDB_RTC_TX
+ },
+ {
+ ABID_AUDIO_IIR_TX,\
+ IID_AUDIO_IIR_COEFF,\
+ AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS,\
+ ACDB_RTC_TX
+ }
+ /*Any new entries should be added here*/
+};
+
+static struct dentry *get_set_abid_dentry;
+static struct dentry *get_set_abid_data_dentry;
+
+struct rtc_acdb_pmem {
+ u8 *viraddr;
+ int32_t phys;
+};
+
+struct rtc_acdb_data {
+ u32 acdb_id;
+ u32 cmd_id;
+ u32 set_abid;
+ u32 set_iid;
+ u32 abid;
+ u32 err;
+ bool valid_abid;
+ u32 tx_rx_ctl;
+ struct rtc_acdb_pmem rtc_read;
+ struct rtc_acdb_pmem rtc_write;
+ wait_queue_head_t wait;
+};
+
+struct get_abid {
+ u32 cmd_id;
+ u32 acdb_id;
+ u32 set_abid;
+ u32 set_iid;
+};
+
+struct acdb_block_mbadrc_rtc {
+ u16 enable;
+ u16 num_bands;
+ u16 down_samp_level;
+ u16 adrc_delay;
+ u16 ext_buf_size;
+ u16 ext_partition;
+ u16 ext_buf_msw;
+ u16 ext_buf_lsw;
+ struct adrc_config adrc_band[AUDPP_MAX_MBADRC_BANDS];
+ signed int ExtBuff[196];
+} __attribute__((packed));
+
+enum {
+ ACDB_RTC_SUCCESS,
+ ACDB_RTC_ERR_INVALID_DEVICE,
+ ACDB_RTC_ERR_DEVICE_INACTIVE,
+ ACDB_RTC_ERR_INVALID_ABID,
+ ACDB_RTC_DSP_FAILURE,
+ ACDB_RTC_DSP_FEATURE_NOT_AVAILABLE,
+ ACDB_RTC_ERR_INVALID_LEN,
+ ACDB_RTC_ERR_UNKNOWN_FAILURE,
+ ACDB_RTC_PENDING_RESPONSE,
+ ACDB_RTC_INIT_FAILURE,
+};
+
+static struct rtc_acdb_data rtc_acdb;
+
+static int rtc_getsetabid_dbg_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ MM_INFO("GET-SET ABID Open debug intf %s\n",
+ (char *) file->private_data);
+ return 0;
+}
+
+static bool get_feature_id(u32 set_abid, u32 iid, unsigned short *feature_id)
+{
+ bool ret_value = false;
+ int i = 0;
+
+ for (; i < (sizeof(acdb_audpp_entry) / sizeof(acdb_audpp_entry[0]));\
+ i++) {
+ if (acdb_audpp_entry[i][0] == set_abid &&
+ acdb_audpp_entry[i][1] == iid) {
+ *feature_id = acdb_audpp_entry[i][2];
+ rtc_acdb.tx_rx_ctl = acdb_audpp_entry[i][3];
+ ret_value = true;
+ break;
+ }
+ }
+ return ret_value;
+}
+static ssize_t rtc_getsetabid_dbg_write(struct file *filp,
+ const char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ struct get_abid write_abid;
+ unsigned short feat_id = 0;
+ rtc_acdb.valid_abid = false;
+
+ if (copy_from_user(&write_abid, \
+ (void *)ubuf, sizeof(struct get_abid))) {
+ MM_ERR("ACDB DATA WRITE - INVALID READ LEN\n");
+ rtc_acdb.err = ACDB_RTC_ERR_INVALID_LEN;
+ return cnt;
+ }
+ MM_INFO("SET ABID : Cmd ID: %d Device:%d ABID:%d IID : %d cnt: %d\n",\
+ write_abid.cmd_id, write_abid.acdb_id,
+ write_abid.set_abid, write_abid.set_iid, cnt);
+ if (write_abid.acdb_id > ACDB_ID_MAX ||
+ write_abid.acdb_id < ACDB_ID_HANDSET_SPKR){
+ rtc_acdb.err = ACDB_RTC_ERR_INVALID_DEVICE;
+ return cnt;
+ }
+ if (!is_dev_opened(write_abid.acdb_id)) {
+ rtc_acdb.err = ACDB_RTC_ERR_DEVICE_INACTIVE;
+ return cnt;
+ }
+ rtc_acdb.err = ACDB_RTC_ERR_INVALID_ABID;
+ rtc_acdb.abid = write_abid.set_abid;
+ if (get_feature_id(write_abid.set_abid, \
+ write_abid.set_iid, &feat_id)) {
+ rtc_acdb.err = ACDB_RTC_SUCCESS;
+ rtc_acdb.cmd_id = write_abid.cmd_id;
+ rtc_acdb.acdb_id = write_abid.acdb_id;
+ rtc_acdb.set_abid = feat_id;
+ rtc_acdb.valid_abid = true;
+ rtc_acdb.set_iid = write_abid.set_iid;
+ }
+ return cnt;
+}
+static ssize_t rtc_getsetabid_dbg_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ static char buffer[1024];
+ int n = 0;
+ u32 msg = rtc_acdb.err;
+ memcpy(buffer, &rtc_acdb.cmd_id, sizeof(struct get_abid));
+ memcpy(buffer+16, &msg, 4);
+ n = 20;
+ MM_INFO("SET ABID : Cmd ID: %x Device:%x ABID:%x IID : %x Err: %d\n",\
+ rtc_acdb.cmd_id, rtc_acdb.acdb_id, rtc_acdb.set_abid,\
+ rtc_acdb.set_iid, rtc_acdb.err);
+ return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static int rtc_getsetabid_data_dbg_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ MM_INFO("GET-SET ABID DATA Open debug intf %s\n",
+ (char *) file->private_data);
+ return 0;
+}
+
+void acdb_rtc_set_err(u32 ErrCode)
+{
+ if (rtc_acdb.err == ACDB_RTC_PENDING_RESPONSE) {
+ if (ErrCode == 0xFFFF) {
+ rtc_acdb.err = ACDB_RTC_SUCCESS;
+ MM_INFO("RTC READ SUCCESS---\n");
+ } else if (ErrCode == 0) {
+ rtc_acdb.err = ACDB_RTC_DSP_FAILURE;
+ MM_INFO("RTC READ FAIL---\n");
+ } else if (ErrCode == 1) {
+ rtc_acdb.err = ACDB_RTC_DSP_FEATURE_NOT_AVAILABLE;
+ MM_INFO("RTC READ FEAT UNAVAILABLE---\n");
+ } else {
+ rtc_acdb.err = ACDB_RTC_DSP_FAILURE;
+ MM_ERR("RTC Err CODE---\n");
+ }
+ } else {
+ rtc_acdb.err = ACDB_RTC_DSP_FAILURE;
+ MM_ERR("RTC Err code Invalid State\n");
+ }
+ wake_up(&rtc_acdb.wait);
+}
+static ssize_t rtc_getsetabid_data_dbg_read(struct file *file,
+ char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ static char buffer[PMEM_RTC_ACDB_QUERY_MEM];
+ int rc, n = 0;
+ int counter = 0;
+ struct rtc_acdb_pmem *rtc_read = &rtc_acdb.rtc_read;
+ memset(&buffer, 0, PMEM_RTC_ACDB_QUERY_MEM);
+
+ if (rtc_acdb.valid_abid != true) {
+ MM_ERR("ACDB DATA READ ---INVALID ABID\n");
+ n = 0;
+ rtc_acdb.err = ACDB_RTC_ERR_INVALID_ABID;
+ } else {
+ if (PMEM_RTC_ACDB_QUERY_MEM < count) {
+ MM_ERR("ACDB DATA READ ---\
+ INVALID READ LEN %x\n", count);
+ n = 0;
+ rtc_acdb.err = ACDB_RTC_ERR_INVALID_LEN;
+ } else {
+ rtc_acdb.err = ACDB_RTC_PENDING_RESPONSE;
+ if (rtc_read->viraddr != NULL) {
+ memset(rtc_read->viraddr,
+ 0, PMEM_RTC_ACDB_QUERY_MEM);
+ }
+ if (rtc_acdb.tx_rx_ctl == ACDB_RTC_RX) {
+ struct rtc_audpp_read_data rtc_read_cmd;
+ rtc_read_cmd.cmd_id =
+ AUDPP_CMD_PP_FEAT_QUERY_PARAMS;
+ rtc_read_cmd.obj_id =
+ AUDPP_CMD_COPP_STREAM;
+ rtc_read_cmd.route_id =
+ acdb_data.device_info->dev_id;
+ rtc_read_cmd.feature_id = rtc_acdb.set_abid;
+ rtc_read_cmd.extbufsizemsw =
+ EXTRACT_HIGH_WORD(\
+ PMEM_RTC_ACDB_QUERY_MEM);
+ rtc_read_cmd.extbufsizelsw =
+ EXTRACT_LOW_WORD(\
+ PMEM_RTC_ACDB_QUERY_MEM);
+ rtc_read_cmd.extpart = 0x0000;
+ rtc_read_cmd.extbufstartmsw =
+ EXTRACT_HIGH_WORD(rtc_read->phys);
+ rtc_read_cmd.extbufstartlsw =
+ EXTRACT_LOW_WORD(rtc_read->phys);
+ rc = audpp_send_queue2(&rtc_read_cmd,
+ sizeof(rtc_read_cmd));
+ MM_INFO("ACDB READ Command RC --->%x\
+ Route ID=%x\n", rc,\
+ acdb_data.device_info->dev_id);
+ } else if (rtc_acdb.tx_rx_ctl == ACDB_RTC_TX) {
+ struct rtc_audpreproc_read_data rtc_audpreproc;
+ rtc_audpreproc.cmd_id =
+ AUDPREPROC_CMD_FEAT_QUERY_PARAMS;
+ rtc_audpreproc.stream_id =
+ acdb_data.preproc_stream_id;
+ rtc_audpreproc.feature_id = rtc_acdb.set_abid;
+ rtc_audpreproc.extbufsizemsw =
+ EXTRACT_HIGH_WORD(\
+ PMEM_RTC_ACDB_QUERY_MEM);
+ rtc_audpreproc.extbufsizelsw =
+ EXTRACT_LOW_WORD(\
+ PMEM_RTC_ACDB_QUERY_MEM);
+ rtc_audpreproc.extpart = 0x0000;
+ rtc_audpreproc.extbufstartmsw =
+ EXTRACT_HIGH_WORD(rtc_read->phys);
+ rtc_audpreproc.extbufstartlsw =
+ EXTRACT_LOW_WORD(rtc_read->phys);
+ rc = audpreproc_send_preproccmdqueue(
+ &rtc_audpreproc,\
+ sizeof(rtc_audpreproc));
+ MM_INFO("ACDB READ Command RC --->%x,\
+ stream_id %x\n", rc,\
+ acdb_data.preproc_stream_id);
+ }
+ rc = wait_event_timeout(rtc_acdb.wait,
+ (rtc_acdb.err !=
+ ACDB_RTC_PENDING_RESPONSE),
+ msecs_to_jiffies(RTC_MAX_TIMEOUT));
+ MM_INFO("ACDB READ ACK Count = %x Err = %x\n",
+ count, rtc_acdb.err);
+ {
+ if (rtc_acdb.err == ACDB_RTC_SUCCESS
+ && rtc_read->viraddr != NULL) {
+ memcpy(buffer, rtc_read->viraddr, count);
+ n = count;
+ while (counter < count) {
+ MM_DBG("%x", \
+ rtc_read->viraddr[counter]);
+ counter++;
+ }
+ }
+ }
+ }
+ }
+ return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static bool acdb_set_tx_rtc(const char *ubuf, size_t writecount)
+{
+ struct audpreproc_cmd_cfg_iir_tuning_filter_params *preproc_iir;
+ struct audpreproc_cmd_cfg_agc_params *preproc_agc;
+ struct audpreproc_cmd_cfg_ns_params *preproc_ns;
+ s32 result = 0;
+ bool retval = false;
+ unsigned short iircmdsize =
+ sizeof(struct audpreproc_cmd_cfg_iir_tuning_filter_params);
+ unsigned short iircmdid = AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS;
+
+ rtc_acdb.err = ACDB_RTC_ERR_UNKNOWN_FAILURE;
+
+ switch (rtc_acdb.set_abid) {
+
+ case AUDPREPROC_CMD_CFG_AGC_PARAMS:
+ case AUDPREPROC_CMD_CFG_AGC_PARAMS_2:
+ {
+ preproc_agc = kmalloc(sizeof(\
+ struct audpreproc_cmd_cfg_agc_params),\
+ GFP_KERNEL);
+ if ((sizeof(struct audpreproc_cmd_cfg_agc_params) -\
+ (2*sizeof(unsigned short)))
+ < writecount) {
+ MM_ERR("ACDB DATA WRITE --\
+ AGC TX writecount > DSP struct\n");
+ } else {
+ if (preproc_agc != NULL) {
+ char *base; unsigned short offset;
+ unsigned short *offset_addr;
+ base = (char *)preproc_agc;
+ offset = offsetof(struct \
+ audpreproc_cmd_cfg_agc_params,\
+ tx_agc_param_mask);
+ offset_addr = (unsigned short *)(base + offset);
+ if ((copy_from_user(offset_addr,\
+ (void *)ubuf, writecount)) == 0x00) {
+ preproc_agc->cmd_id =
+ AUDPREPROC_CMD_CFG_AGC_PARAMS;
+ preproc_agc->stream_id =
+ acdb_data.preproc_stream_id;
+ result = audpreproc_dsp_set_agc(
+ preproc_agc,
+ sizeof(struct \
+ audpreproc_cmd_cfg_agc_params));
+ if (result) {
+ MM_ERR("ACDB=> Failed to \
+ send AGC data to \
+ preproc)\n");
+ } else {
+ retval = true;
+ }
+ } else {
+ MM_ERR("ACDB DATA WRITE ---\
+ GC Tx copy_from_user Fail\n");
+ }
+ } else {
+ MM_ERR("ACDB DATA WRITE --\
+ AGC TX kalloc Failed LEN\n");
+ }
+ }
+ if (preproc_agc != NULL)
+ kfree(preproc_agc);
+ break;
+ }
+ case AUDPREPROC_CMD_CFG_NS_PARAMS:
+ {
+
+ preproc_ns = kmalloc(sizeof(struct \
+ audpreproc_cmd_cfg_ns_params),\
+ GFP_KERNEL);
+ if ((sizeof(struct audpreproc_cmd_cfg_ns_params) -\
+ (2 * sizeof(unsigned short)))
+ < writecount) {
+ MM_ERR("ACDB DATA WRITE --\
+ NS TX writecount > DSP struct\n");
+ } else {
+ if (preproc_ns != NULL) {
+ char *base; unsigned short offset;
+ unsigned short *offset_addr;
+ base = (char *)preproc_ns;
+ offset = offsetof(struct \
+ audpreproc_cmd_cfg_ns_params,\
+ ec_mode_new);
+ offset_addr = (unsigned short *)(base + offset);
+ if ((copy_from_user(offset_addr,\
+ (void *)ubuf, writecount)) == 0x00) {
+ preproc_ns->cmd_id =
+ AUDPREPROC_CMD_CFG_NS_PARAMS;
+ preproc_ns->stream_id =
+ acdb_data.preproc_stream_id;
+ result = audpreproc_dsp_set_ns(
+ preproc_ns,
+ sizeof(struct \
+ audpreproc_cmd_cfg_ns_params));
+ if (result) {
+ MM_ERR("ACDB=> Failed to send \
+ NS data to preproc\n");
+ } else {
+ retval = true;
+ }
+ } else {
+ MM_ERR("ACDB DATA WRITE ---NS Tx \
+ copy_from_user Fail\n");
+ }
+ } else {
+ MM_ERR("ACDB DATA WRITE --NS TX\
+ kalloc Failed LEN\n");
+ }
+ }
+ if (preproc_ns != NULL)
+ kfree(preproc_ns);
+ break;
+ }
+ case AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS:
+ {
+
+ preproc_iir = kmalloc(sizeof(struct \
+ audpreproc_cmd_cfg_iir_tuning_filter_params),\
+ GFP_KERNEL);
+ if ((sizeof(struct \
+ audpreproc_cmd_cfg_iir_tuning_filter_params)-\
+ (2 * sizeof(unsigned short)))
+ < writecount) {
+ MM_ERR("ACDB DATA WRITE --IIR TX writecount\
+ > DSP struct\n");
+ } else {
+ if (preproc_iir != NULL) {
+ char *base; unsigned short offset;
+ unsigned short *offset_addr;
+ base = (char *)preproc_iir;
+ offset = offsetof(struct \
+ audpreproc_cmd_cfg_iir_tuning_filter_params,\
+ active_flag);
+ offset_addr = (unsigned short *)(base + \
+ offset);
+ if ((copy_from_user(offset_addr,\
+ (void *)ubuf, writecount)) == 0x00) {
+ preproc_iir->cmd_id = iircmdid;
+ preproc_iir->stream_id =
+ acdb_data.preproc_stream_id;
+ result = audpreproc_dsp_set_iir(\
+ preproc_iir,
+ iircmdsize);
+ if (result) {
+ MM_ERR("ACDB=> Failed to send\
+ IIR data to preproc\n");
+ } else {
+ retval = true;
+ }
+ } else {
+ MM_ERR("ACDB DATA WRITE ---IIR Tx \
+ copy_from_user Fail\n");
+ }
+ } else {
+ MM_ERR("ACDB DATA WRITE --IIR TX kalloc \
+ Failed LEN\n");
+ }
+ }
+ if (preproc_iir != NULL)
+ kfree(preproc_iir);
+ break;
+ }
+ }
+ return retval;
+}
+
+static bool acdb_set_rx_rtc(const char *ubuf, size_t writecount)
+{
+
+ struct audpp_cmd_cfg_object_params_volpan *volpan_config;
+ struct audpp_cmd_cfg_object_params_mbadrc *mbadrc_config;
+ struct acdb_block_mbadrc_rtc *acdb_mbadrc_rtc;
+ struct audpp_cmd_cfg_object_params_sidechain *stf_config;
+ struct audpp_cmd_cfg_object_params_spectram *spa_config;
+ struct audpp_cmd_cfg_object_params_eqalizer *eq_config;
+ struct audpp_cmd_cfg_object_params_pcm *iir_config;
+ unsigned short temp_spa[34];
+ struct rtc_acdb_pmem *rtc_write = &rtc_acdb.rtc_write;
+ s32 result = 0;
+ bool retval = false;
+
+ switch (rtc_acdb.set_abid) {
+ case AUDPP_CMD_VOLUME_PAN:
+ {
+ volpan_config = kmalloc(sizeof(struct \
+ audpp_cmd_cfg_object_params_volpan),\
+ GFP_KERNEL);
+ if ((sizeof(struct audpp_cmd_cfg_object_params_volpan) -\
+ sizeof(struct audpp_cmd_cfg_object_params_common))
+ < writecount) {
+ MM_ERR("ACDB DATA WRITE --\
+ VolPan writecount > DSP struct\n");
+ } else {
+ if (volpan_config != NULL) {
+ char *base; unsigned short offset;
+ unsigned short *offset_addr;
+ base = (char *)volpan_config;
+ offset = offsetof(struct \
+ audpp_cmd_cfg_object_params_volpan,\
+ volume);
+ offset_addr = (unsigned short *)(base+offset);
+ if ((copy_from_user(offset_addr,\
+ (void *)ubuf, writecount)) == 0x00) {
+ MM_ERR("ACDB RX WRITE DATA:\
+ AUDPP_CMD_VOLUME_PAN\n");
+ result = audpp_set_volume_and_pan(
+ acdb_data.device_info->dev_id,\
+ volpan_config->volume,
+ volpan_config->pan,
+ COPP);
+ if (result) {
+ MM_ERR("ACDB=> Failed to \
+ send VOLPAN data to"
+ " postproc\n");
+ } else {
+ retval = true;
+ }
+ } else {
+ MM_ERR("ACDB DATA WRITE ---\
+ copy_from_user Fail\n");
+ }
+ } else {
+ MM_ERR("ACDB DATA WRITE --\
+ Vol Pan kalloc Failed LEN\n");
+ }
+ }
+ if (volpan_config != NULL)
+ kfree(volpan_config);
+ break;
+ }
+
+ case AUDPP_CMD_IIR_TUNING_FILTER:
+ {
+ iir_config = kmalloc(sizeof(struct \
+ audpp_cmd_cfg_object_params_pcm),\
+ GFP_KERNEL);
+ if ((sizeof(struct audpp_cmd_cfg_object_params_pcm) -\
+ sizeof(struct audpp_cmd_cfg_object_params_common))
+ < writecount) {
+ MM_ERR("ACDB DATA WRITE --\
+ IIR RX writecount > DSP struct\n");
+ } else {
+ if (iir_config != NULL) {
+ char *base; unsigned short offset;
+ unsigned short *offset_addr;
+ base = (char *)iir_config;
+ offset = offsetof(struct \
+ audpp_cmd_cfg_object_params_pcm,\
+ active_flag);
+ offset_addr = (unsigned short *)(base+offset);
+ if ((copy_from_user(offset_addr,\
+ (void *)ubuf, writecount)) == 0x00) {
+
+ iir_config->common.cmd_id =
+ AUDPP_CMD_CFG_OBJECT_PARAMS;
+ iir_config->common.stream =
+ AUDPP_CMD_COPP_STREAM;
+ iir_config->common.stream_id = 0;
+ iir_config->common.obj_cfg =
+ AUDPP_CMD_OBJ0_UPDATE;
+ iir_config->common.command_type = 0;
+ MM_ERR("ACDB RX WRITE DATA:\
+ AUDPP_CMD_IIR_TUNING_FILTER\n");
+ result = audpp_dsp_set_rx_iir(
+ acdb_data.device_info->dev_id,
+ iir_config->active_flag,\
+ iir_config, COPP);
+ if (result) {
+ MM_ERR("ACDB=> Failed to send\
+ IIR data to\
+ postproc\n");
+ } else {
+ retval = true;
+ }
+ } else {
+ MM_ERR("ACDB DATA WRITE ---\
+ IIR Rx copy_from_user Fail\n");
+ }
+ } else {
+ MM_ERR("ACDB DATA WRITE --\
+ acdb_iir_block kalloc Failed LEN\n");
+ }
+ }
+ if (iir_config != NULL)
+ kfree(iir_config);
+ break;
+ }
+ case AUDPP_CMD_EQUALIZER:
+ {
+ eq_config = kmalloc(sizeof(struct \
+ audpp_cmd_cfg_object_params_eqalizer),\
+ GFP_KERNEL);
+ if ((sizeof(struct audpp_cmd_cfg_object_params_eqalizer) -\
+ sizeof(struct audpp_cmd_cfg_object_params_common))
+ < writecount) {
+ MM_ERR("ACDB DATA WRITE --\
+ EQ RX writecount > DSP struct\n");
+ } else {
+ if (eq_config != NULL) {
+ char *base; unsigned short offset;
+ unsigned short *offset_addr;
+ base = (char *)eq_config;
+ offset = offsetof(struct \
+ audpp_cmd_cfg_object_params_eqalizer,\
+ eq_flag);
+ offset_addr = (unsigned short *)(base+offset);
+ if ((copy_from_user(offset_addr,\
+ (void *)ubuf, writecount)) == 0x00) {
+ eq_config->common.cmd_id =
+ AUDPP_CMD_CFG_OBJECT_PARAMS;
+ eq_config->common.stream =
+ AUDPP_CMD_COPP_STREAM;
+ eq_config->common.stream_id = 0;
+ eq_config->common.obj_cfg =
+ AUDPP_CMD_OBJ0_UPDATE;
+ eq_config->common.command_type = 0;
+ MM_ERR("ACDB RX WRITE\
+ DATA:AUDPP_CMD_EQUALIZER\n");
+ result = audpp_dsp_set_eq(
+ acdb_data.device_info->dev_id,
+ eq_config->eq_flag,\
+ eq_config,
+ COPP);
+ if (result) {
+ MM_ERR("ACDB=> Failed to \
+ send EQ data to postproc\n");
+ } else {
+ retval = true;
+ }
+ } else {
+ MM_ERR("ACDB DATA WRITE ---\
+ EQ Rx copy_from_user Fail\n");
+ }
+ } else {
+ MM_ERR("ACDB DATA WRITE --\
+ EQ kalloc Failed LEN\n");
+ }
+ }
+ if (eq_config != NULL)
+ kfree(eq_config);
+ break;
+ }
+
+ case AUDPP_CMD_SPECTROGRAM:
+ {
+ spa_config = kmalloc(sizeof(struct \
+ audpp_cmd_cfg_object_params_spectram),\
+ GFP_KERNEL);
+ if ((sizeof(struct audpp_cmd_cfg_object_params_spectram)-\
+ sizeof(struct \
+ audpp_cmd_cfg_object_params_common))
+ < (2 * sizeof(unsigned short))) {
+ MM_ERR("ACDB DATA WRITE --SPA \
+ RX writecount > DSP struct\n");
+ } else {
+ if (spa_config != NULL) {
+ if ((copy_from_user(&temp_spa[0],\
+ (void *)ubuf,
+ (34 * sizeof(unsigned short))))
+ == 0x00) {
+ spa_config->common.cmd_id =
+ AUDPP_CMD_CFG_OBJECT_PARAMS;
+ spa_config->common.stream =
+ AUDPP_CMD_COPP_STREAM;
+ spa_config->common.stream_id = 0;
+ spa_config->common.obj_cfg =
+ AUDPP_CMD_OBJ0_UPDATE;
+ spa_config->common.command_type = 0;
+ spa_config->sample_interval =
+ temp_spa[0];
+ spa_config->num_coeff = temp_spa[1];
+ MM_ERR("ACDB RX WRITE DATA:\
+ AUDPP_CMD_SPECTROGRAM\n");
+ result = audpp_dsp_set_spa(
+ acdb_data.device_info->dev_id,\
+ spa_config, COPP);
+ if (result) {
+ MM_ERR("ACDB=> Failed to \
+ send SPA data \
+ to postproc\n");
+ } else {
+ retval = true;
+ }
+ } else {
+ MM_ERR("ACDB DATA WRITE \
+ ---SPA Rx copy_from_user\
+ Fail\n");
+ }
+ } else {
+ MM_ERR("ACDB DATA WRITE --\
+ SPA kalloc Failed LEN\n");
+ }
+ }
+ if (spa_config != NULL)
+ kfree(spa_config);
+ break;
+ }
+ case AUDPP_CMD_MBADRC:
+ {
+ acdb_mbadrc_rtc = kmalloc(sizeof(struct \
+ acdb_block_mbadrc_rtc),\
+ GFP_KERNEL);
+ mbadrc_config = kmalloc(sizeof(struct \
+ audpp_cmd_cfg_object_params_mbadrc),\
+ GFP_KERNEL);
+ if (mbadrc_config != NULL && acdb_mbadrc_rtc != NULL) {
+ if ((copy_from_user(acdb_mbadrc_rtc,\
+ (void *)ubuf,
+ sizeof(struct acdb_block_mbadrc_rtc)))
+ == 0x00) {
+ mbadrc_config->common.cmd_id =
+ AUDPP_CMD_CFG_OBJECT_PARAMS;
+ mbadrc_config->common.stream =
+ AUDPP_CMD_COPP_STREAM;
+ mbadrc_config->common.stream_id = 0;
+ mbadrc_config->common.obj_cfg =
+ AUDPP_CMD_OBJ0_UPDATE;
+ mbadrc_config->common.command_type = 0;
+ mbadrc_config->enable =
+ acdb_mbadrc_rtc->enable;
+ mbadrc_config->num_bands =
+ acdb_mbadrc_rtc->num_bands;
+ mbadrc_config->down_samp_level =
+ acdb_mbadrc_rtc->down_samp_level;
+ mbadrc_config->adrc_delay =
+ acdb_mbadrc_rtc->adrc_delay;
+ memcpy(mbadrc_config->adrc_band,\
+ acdb_mbadrc_rtc->adrc_band,\
+ AUDPP_MAX_MBADRC_BANDS *\
+ sizeof(struct adrc_config));
+ if (mbadrc_config->num_bands > 1) {
+ mbadrc_config->ext_buf_size =
+ (97 * 2) + (33 * 2 * \
+ (mbadrc_config->num_bands - 2));
+ }
+ mbadrc_config->ext_partition = 0;
+ mbadrc_config->ext_buf_lsw =
+ (u16) EXTRACT_LOW_WORD(\
+ rtc_write->phys);
+ mbadrc_config->ext_buf_msw =
+ (u16) EXTRACT_HIGH_WORD(\
+ rtc_write->phys);
+ memcpy(rtc_write->viraddr,
+ acdb_mbadrc_rtc->ExtBuff,
+ (196*sizeof(signed int)));
+ result = audpp_dsp_set_mbadrc(
+ acdb_data.device_info->dev_id,
+ mbadrc_config->enable,
+ mbadrc_config, COPP);
+ if (result) {
+ MM_ERR("ACDB=> Failed to \
+ Send MBADRC data \
+ to postproc\n");
+ } else {
+ retval = true;
+ }
+ } else {
+ MM_ERR("ACDB DATA WRITE ---\
+ MBADRC Rx copy_from_user Fail\n");
+ }
+ } else {
+ MM_ERR("ACDB DATA WRITE --MBADRC kalloc Failed LEN\n");
+ }
+ if (mbadrc_config != NULL)
+ kfree(mbadrc_config);
+ if (acdb_mbadrc_rtc != NULL)
+ kfree(acdb_mbadrc_rtc);
+ break;
+ }
+ case AUDPP_CMD_SIDECHAIN_TUNING_FILTER:
+ {
+ stf_config = kmalloc(sizeof(struct \
+ audpp_cmd_cfg_object_params_sidechain),\
+ GFP_KERNEL);
+ if ((sizeof(struct audpp_cmd_cfg_object_params_sidechain) -\
+ sizeof(struct audpp_cmd_cfg_object_params_common))
+ < writecount) {
+ MM_ERR("ACDB DATA WRITE --\
+ STF RX writecount > DSP struct\n");
+ } else {
+ if (stf_config != NULL) {
+ char *base; unsigned short offset;
+ unsigned short *offset_addr;
+ base = (char *)stf_config;
+ offset = offsetof(struct \
+ audpp_cmd_cfg_object_params_sidechain,\
+ active_flag);
+ offset_addr = (unsigned short *)(base+offset);
+ if ((copy_from_user(offset_addr,\
+ (void *)ubuf, writecount)) == 0x00) {
+ stf_config->common.cmd_id =
+ AUDPP_CMD_CFG_OBJECT_PARAMS;
+ stf_config->common.stream =
+ AUDPP_CMD_COPP_STREAM;
+ stf_config->common.stream_id = 0;
+ stf_config->common.obj_cfg =
+ AUDPP_CMD_OBJ0_UPDATE;
+ stf_config->common.command_type = 0;
+ MM_ERR("ACDB RX WRITE DATA:\
+ AUDPP_CMD_SIDECHAIN_TUNING_FILTER\n");
+ result = audpp_dsp_set_stf(
+ acdb_data.device_info->dev_id,\
+ stf_config->active_flag,\
+ stf_config, COPP);
+ if (result) {
+ MM_ERR("ACDB=> Failed to send \
+ STF data to postproc\n");
+ } else {
+ retval = true;
+ }
+ } else {
+ MM_ERR("ACDB DATA WRITE ---\
+ STF Rx copy_from_user Fail\n");
+ }
+ } else {
+ MM_ERR("ACDB DATA WRITE \
+ STF kalloc Failed LEN\n");
+ }
+ }
+ if (stf_config != NULL)
+ kfree(stf_config);
+ break;
+ }
+ }
+ return retval;
+}
+static ssize_t rtc_getsetabid_data_dbg_write(struct file *filp,
+ const char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ if (rtc_acdb.valid_abid != true) {
+ MM_INFO("ACDB DATA READ ---INVALID ABID\n");
+ rtc_acdb.err = ACDB_RTC_ERR_INVALID_ABID;
+ } else {
+ if (rtc_acdb.tx_rx_ctl == ACDB_RTC_RX) {
+ if (acdb_set_rx_rtc(ubuf, cnt)) {
+ rtc_acdb.err = ACDB_RTC_SUCCESS;
+ } else {
+ rtc_acdb.err = ACDB_RTC_ERR_UNKNOWN_FAILURE;
+ cnt = 0;
+ }
+ } else if (rtc_acdb.tx_rx_ctl == ACDB_RTC_TX) {
+ if (acdb_set_tx_rtc(ubuf, cnt)) {
+ rtc_acdb.err = ACDB_RTC_SUCCESS;
+ } else {
+ rtc_acdb.err = ACDB_RTC_ERR_UNKNOWN_FAILURE;
+ cnt = 0;
+ }
+ }
+ }
+ return cnt;
+}
+
+
+static const struct file_operations rtc_acdb_data_debug_fops = {
+ .open = rtc_getsetabid_data_dbg_open,
+ .write = rtc_getsetabid_data_dbg_write,
+ .read = rtc_getsetabid_data_dbg_read
+};
+
+static const struct file_operations rtc_acdb_debug_fops = {
+ .open = rtc_getsetabid_dbg_open,
+ .write = rtc_getsetabid_dbg_write,
+ .read = rtc_getsetabid_dbg_read
+};
+
+static void rtc_acdb_deinit(void)
+{
+ struct rtc_acdb_pmem *rtc_read = &rtc_acdb.rtc_read;
+ struct rtc_acdb_pmem *rtc_write = &rtc_acdb.rtc_write;
+ if (get_set_abid_dentry) {
+ MM_DBG("GetSet ABID remove debugfs\n");
+ debugfs_remove(get_set_abid_dentry);
+ }
+
+ if (get_set_abid_data_dentry) {
+ MM_DBG("GetSet ABID remove debugfs\n");
+ debugfs_remove(get_set_abid_data_dentry);
+ }
+ rtc_acdb.abid = 0;
+ rtc_acdb.acdb_id = 0;
+ rtc_acdb.cmd_id = 0;
+ rtc_acdb.err = 1;
+ rtc_acdb.set_abid = 0;
+ rtc_acdb.set_iid = 0;
+ rtc_acdb.tx_rx_ctl = 0;
+ rtc_acdb.valid_abid = false;
+
+ if (rtc_read->viraddr != NULL || ((void *)rtc_read->phys) != NULL) {
+ iounmap(rtc_read->viraddr);
+ pmem_kfree(rtc_read->phys);
+ }
+ if (rtc_write->viraddr != NULL || ((void *)rtc_write->phys) != NULL) {
+ iounmap(rtc_write->viraddr);
+ pmem_kfree(rtc_write->phys);
+ }
+}
+
+static bool rtc_acdb_init(void)
+{
+ struct rtc_acdb_pmem *rtc_read = &rtc_acdb.rtc_read;
+ struct rtc_acdb_pmem *rtc_write = &rtc_acdb.rtc_write;
+ s32 result = 0;
+ char name[sizeof "get_set_abid"+1];
+ char name1[sizeof "get_set_abid_data"+1];
+ rtc_acdb.abid = 0;
+ rtc_acdb.acdb_id = 0;
+ rtc_acdb.cmd_id = 0;
+ rtc_acdb.err = 1;
+ rtc_acdb.set_abid = 0;
+ rtc_acdb.set_iid = 0;
+ rtc_acdb.valid_abid = false;
+ rtc_acdb.tx_rx_ctl = 0;
+ snprintf(name, sizeof name, "get_set_abid");
+ get_set_abid_dentry = debugfs_create_file(name,
+ S_IFREG | S_IRUGO | S_IWUGO,
+ NULL, NULL, &rtc_acdb_debug_fops);
+ if (IS_ERR(get_set_abid_dentry)) {
+ MM_ERR("SET GET ABID debugfs_create_file failed\n");
+ return false;
+ }
+
+ snprintf(name1, sizeof name1, "get_set_abid_data");
+ get_set_abid_data_dentry = debugfs_create_file(name1,
+ S_IFREG | S_IRUGO | S_IWUGO,
+ NULL, NULL,
+ &rtc_acdb_data_debug_fops);
+ if (IS_ERR(get_set_abid_data_dentry)) {
+ MM_ERR("SET GET ABID DATA debugfs_create_file failed\n");
+ return false;
+ }
+
+ rtc_read->phys = pmem_kalloc(PMEM_RTC_ACDB_QUERY_MEM,
+ PMEM_MEMTYPE_EBI1|PMEM_ALIGNMENT_4K);
+
+ if (IS_ERR((void *)rtc_read->phys)) {
+ MM_ERR("ACDB Cannot allocate physical memory\n");
+ result = -ENOMEM;
+ goto error;
+ }
+ rtc_read->viraddr = ioremap(rtc_read->phys, PMEM_RTC_ACDB_QUERY_MEM);
+
+ if (rtc_read->viraddr == NULL) {
+ MM_ERR("ACDB Could not map physical address\n");
+ result = -ENOMEM;
+ goto error;
+ }
+ memset(rtc_read->viraddr, 0, PMEM_RTC_ACDB_QUERY_MEM);
+
+ rtc_write->phys = pmem_kalloc(PMEM_RTC_ACDB_QUERY_MEM,
+ PMEM_MEMTYPE_EBI1|PMEM_ALIGNMENT_4K);
+
+ if (IS_ERR((void *)rtc_write->phys)) {
+ MM_ERR("ACDB Cannot allocate physical memory\n");
+ result = -ENOMEM;
+ goto error;
+ }
+ rtc_write->viraddr = ioremap(rtc_write->phys, PMEM_RTC_ACDB_QUERY_MEM);
+
+ if (rtc_write->viraddr == NULL) {
+ MM_ERR("ACDB Could not map physical address\n");
+ result = -ENOMEM;
+ goto error;
+ }
+ memset(rtc_write->viraddr, 0, PMEM_RTC_ACDB_QUERY_MEM);
+ init_waitqueue_head(&rtc_acdb.wait);
+ return true;
+error:
+ MM_DBG("INIT RTC FAILED REMOVING RTC DEBUG FS\n");
+ if (get_set_abid_dentry) {
+ MM_DBG("GetSet ABID remove debugfs\n");
+ debugfs_remove(get_set_abid_dentry);
+ }
+
+ if (get_set_abid_data_dentry) {
+ MM_DBG("GetSet ABID remove debugfs\n");
+ debugfs_remove(get_set_abid_data_dentry);
+ }
+ if (rtc_read->viraddr != NULL || ((void *)rtc_read->phys) != NULL) {
+ iounmap(rtc_read->viraddr);
+ pmem_kfree(rtc_read->phys);
+ }
+ if (rtc_write->viraddr != NULL || ((void *)rtc_write->phys) != NULL) {
+ iounmap(rtc_write->viraddr);
+ pmem_kfree(rtc_write->phys);
+ }
+ return false;
+}
+#endif /*CONFIG_DEBUG_FS*/
+static s32 acdb_set_calibration_blk(unsigned long arg)
+{
+ struct acdb_cmd_device acdb_cmd;
+ s32 result = 0;
+
+ MM_DBG("acdb_set_calibration_blk\n");
+ if (copy_from_user(&acdb_cmd, (struct acdb_cmd_device *)arg,
+ sizeof(acdb_cmd))) {
+ MM_ERR("Failed copy command struct from user in"
+ "acdb_set_calibration_blk\n");
+ return -EFAULT;
+ }
+ acdb_cmd.phys_buf = (u32 *)acdb_data.paddr;
+
+ MM_DBG("acdb_cmd.phys_buf %x\n", (u32)acdb_cmd.phys_buf);
+
+ result = dalrpc_fcn_8(ACDB_DalACDB_ioctl, acdb_data.handle,
+ (const void *)&acdb_cmd, sizeof(acdb_cmd),
+ &acdb_data.acdb_result,
+ sizeof(acdb_data.acdb_result));
+
+ if (result < 0) {
+ MM_ERR("ACDB=> Device Set RPC failure"
+ " result = %d\n", result);
+ return -EINVAL;
+ } else {
+ MM_ERR("ACDB=> Device Set RPC success\n");
+ if (acdb_data.acdb_result.result == ACDB_RES_SUCCESS)
+ MM_DBG("ACDB_SET_DEVICE Success\n");
+ else if (acdb_data.acdb_result.result == ACDB_RES_FAILURE)
+ MM_ERR("ACDB_SET_DEVICE Failure\n");
+ else if (acdb_data.acdb_result.result == ACDB_RES_BADPARM)
+ MM_ERR("ACDB_SET_DEVICE BadParams\n");
+ else
+ MM_ERR("Unknown error\n");
+ }
+ return result;
+}
+
+static s32 acdb_get_calibration_blk(unsigned long arg)
+{
+ s32 result = 0;
+ struct acdb_cmd_device acdb_cmd;
+
+ MM_DBG("acdb_get_calibration_blk\n");
+
+ if (copy_from_user(&acdb_cmd, (struct acdb_cmd_device *)arg,
+ sizeof(acdb_cmd))) {
+ MM_ERR("Failed copy command struct from user in"
+ "acdb_get_calibration_blk\n");
+ return -EFAULT;
+ }
+ acdb_cmd.phys_buf = (u32 *)acdb_data.paddr;
+ MM_ERR("acdb_cmd.phys_buf %x\n", (u32)acdb_cmd.phys_buf);
+
+ result = dalrpc_fcn_8(ACDB_DalACDB_ioctl, acdb_data.handle,
+ (const void *)&acdb_cmd, sizeof(acdb_cmd),
+ &acdb_data.acdb_result,
+ sizeof(acdb_data.acdb_result));
+
+ if (result < 0) {
+ MM_ERR("ACDB=> Device Get RPC failure"
+ " result = %d\n", result);
+ return -EINVAL;
+ } else {
+ MM_ERR("ACDB=> Device Get RPC Success\n");
+ if (acdb_data.acdb_result.result == ACDB_RES_SUCCESS)
+ MM_DBG("ACDB_GET_DEVICE Success\n");
+ else if (acdb_data.acdb_result.result == ACDB_RES_FAILURE)
+ MM_ERR("ACDB_GET_DEVICE Failure\n");
+ else if (acdb_data.acdb_result.result == ACDB_RES_BADPARM)
+ MM_ERR("ACDB_GET_DEVICE BadParams\n");
+ else
+ MM_ERR("Unknown error\n");
+ }
+ return result;
+}
+
+static int audio_acdb_open(struct inode *inode, struct file *file)
+{
+ MM_DBG("%s\n", __func__);
+ return 0;
+}
+static int audio_acdb_release(struct inode *inode, struct file *file)
+{
+ MM_DBG("%s\n", __func__);
+ return 0;
+}
+
+static long audio_acdb_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int rc = 0;
+ unsigned long flags = 0;
+ struct msm_audio_pmem_info info;
+
+ MM_DBG("%s\n", __func__);
+
+ switch (cmd) {
+ case AUDIO_SET_EQ:
+ MM_DBG("IOCTL SET_EQ_CONFIG\n");
+ if (copy_from_user(&acdb_data.eq.num_bands, (void *) arg,
+ sizeof(acdb_data.eq) -
+ (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) {
+ rc = -EFAULT;
+ break;
+ }
+ spin_lock_irqsave(&acdb_data.dsp_lock, flags);
+ acdb_data.dec_id = 0;
+ rc = audpp_dsp_set_eq(acdb_data.dec_id, 1,
+ &acdb_data.eq, COPP);
+ if (rc < 0)
+ MM_ERR("AUDPP returned err =%d\n", rc);
+ spin_unlock_irqrestore(&acdb_data.dsp_lock, flags);
+ break;
+ case AUDIO_REGISTER_PMEM:
+ MM_DBG("AUDIO_REGISTER_PMEM\n");
+ if (copy_from_user(&info, (void *) arg, sizeof(info))) {
+ MM_ERR("Cannot copy from user\n");
+ return -EFAULT;
+ }
+ rc = get_pmem_file(info.fd, &acdb_data.paddr,
+ &acdb_data.kvaddr,
+ &acdb_data.pmem_len,
+ &acdb_data.file);
+ if (rc == 0)
+ acdb_data.pmem_fd = info.fd;
+ break;
+ case AUDIO_DEREGISTER_PMEM:
+ if (acdb_data.pmem_fd)
+ put_pmem_file(acdb_data.file);
+ break;
+ case AUDIO_SET_ACDB_BLK:
+ MM_DBG("IOCTL AUDIO_SET_ACDB_BLK\n");
+ rc = acdb_set_calibration_blk(arg);
+ break;
+ case AUDIO_GET_ACDB_BLK:
+ MM_DBG("IOiCTL AUDIO_GET_ACDB_BLK\n");
+ rc = acdb_get_calibration_blk(arg);
+ break;
+ default:
+ MM_DBG("Unknown IOCTL%d\n", cmd);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+static const struct file_operations acdb_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_acdb_open,
+ .release = audio_acdb_release,
+ .llseek = no_llseek,
+ .unlocked_ioctl = audio_acdb_ioctl
+};
+
+struct miscdevice acdb_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_acdb",
+ .fops = &acdb_fops,
+};
+
+static s32 acdb_get_calibration(void)
+{
+ struct acdb_cmd_get_device_table acdb_cmd;
+ s32 result = 0;
+ u32 iterations = 0;
+
+ MM_DBG("acdb state = %d\n", acdb_data.acdb_state);
+
+ acdb_cmd.command_id = ACDB_GET_DEVICE_TABLE;
+ acdb_cmd.device_id = acdb_data.device_info->acdb_id;
+ acdb_cmd.network_id = 0x0108B153;
+ acdb_cmd.sample_rate_id = acdb_data.device_info->sample_rate;
+ acdb_cmd.total_bytes = ACDB_BUF_SIZE;
+ acdb_cmd.phys_buf = (u32 *)acdb_data.phys_addr;
+ MM_DBG("device_id = %d, sampling_freq = %d\n",
+ acdb_cmd.device_id, acdb_cmd.sample_rate_id);
+
+ do {
+ result = dalrpc_fcn_8(ACDB_DalACDB_ioctl, acdb_data.handle,
+ (const void *)&acdb_cmd, sizeof(acdb_cmd),
+ &acdb_data.acdb_result,
+ sizeof(acdb_data.acdb_result));
+
+ if (result < 0) {
+ MM_ERR("ACDB=> Device table RPC failure"
+ " result = %d\n", result);
+ goto error;
+ }
+ /*following check is introduced to handle boot up race
+ condition between AUDCAL SW peers running on apps
+ and modem (ACDB_RES_BADSTATE indicates modem AUDCAL SW is
+ not in initialized sate) we need to retry to get ACDB
+ values*/
+ if (acdb_data.acdb_result.result == ACDB_RES_BADSTATE) {
+ msleep(500);
+ iterations++;
+ } else if (acdb_data.acdb_result.result == ACDB_RES_SUCCESS) {
+ MM_DBG("Modem query for acdb values is successful"
+ " (iterations = %d)\n", iterations);
+ acdb_data.acdb_state |= CAL_DATA_READY;
+ return result;
+ } else {
+ MM_ERR("ACDB=> modem failed to fill acdb values,"
+ " reuslt = %d, (iterations = %d)\n",
+ acdb_data.acdb_result.result,
+ iterations);
+ goto error;
+ }
+ } while (iterations < MAX_RETRY);
+ MM_ERR("ACDB=> AUDCAL SW on modem is not in intiailized state (%d)\n",
+ acdb_data.acdb_result.result);
+error:
+ result = -EINVAL;
+ return result;
+}
+
+s32 acdb_get_calibration_data(struct acdb_get_block *get_block)
+{
+ s32 result = -EINVAL;
+ struct acdb_cmd_device acdb_cmd;
+ struct acdb_result acdb_result;
+
+ MM_DBG("acdb_get_calibration_data\n");
+
+ acdb_cmd.command_id = ACDB_GET_DEVICE;
+ acdb_cmd.network_id = 0x0108B153;
+ acdb_cmd.device_id = get_block->acdb_id;
+ acdb_cmd.sample_rate_id = get_block->sample_rate_id;
+ acdb_cmd.interface_id = get_block->interface_id;
+ acdb_cmd.algorithm_block_id = get_block->algorithm_block_id;
+ acdb_cmd.total_bytes = get_block->total_bytes;
+ acdb_cmd.phys_buf = (u32 *)acdb_data.get_blk_paddr;
+
+ result = dalrpc_fcn_8(ACDB_DalACDB_ioctl, acdb_data.handle,
+ (const void *)&acdb_cmd, sizeof(acdb_cmd),
+ &acdb_result,
+ sizeof(acdb_result));
+
+ if (result < 0) {
+ MM_ERR("ACDB=> Device Get RPC failure"
+ " result = %d\n", result);
+ goto err_state;
+ } else {
+ MM_DBG("ACDB=> Device Get RPC Success\n");
+ if (acdb_result.result == ACDB_RES_SUCCESS) {
+ MM_DBG("ACDB_GET_DEVICE Success\n");
+ result = 0;
+ memcpy(get_block->buf_ptr, acdb_data.get_blk_kvaddr,
+ get_block->total_bytes);
+ } else if (acdb_result.result == ACDB_RES_FAILURE)
+ MM_ERR("ACDB_GET_DEVICE Failure\n");
+ else if (acdb_result.result == ACDB_RES_BADPARM)
+ MM_ERR("ACDB_GET_DEVICE BadParams\n");
+ else
+ MM_ERR("Unknown error\n");
+ }
+err_state:
+ return result;
+}
+EXPORT_SYMBOL(acdb_get_calibration_data);
+
+static u8 check_device_info_already_present(
+ struct auddev_evt_audcal_info audcal_info,
+ struct acdb_cache_node *acdb_cache_free_node)
+{
+ if ((audcal_info.dev_id ==
+ acdb_cache_free_node->device_info.dev_id) &&
+ (audcal_info.sample_rate ==
+ acdb_cache_free_node->device_info.\
+ sample_rate) &&
+ (audcal_info.acdb_id ==
+ acdb_cache_free_node->device_info.acdb_id)) {
+ MM_DBG("acdb values are already present\n");
+ /*if acdb state is not set for CAL_DATA_READY and node status
+ is filled, acdb state should be updated with CAL_DATA_READY
+ state*/
+ acdb_data.acdb_state |= CAL_DATA_READY;
+ /*checking for cache node status if it is not filled then the
+ acdb values are not cleaned from node so update node status
+ with acdb value filled*/
+ if ((acdb_cache_free_node->node_status != ACDB_VALUES_FILLED) &&
+ ((acdb_data.device_info->dev_type & RX_DEVICE) == 1)) {
+ MM_DBG("device was released earlier\n");
+ acdb_cache_free_node->node_status = ACDB_VALUES_FILLED;
+ return 2; /*node is presnet but status as not filled*/
+ }
+ return 1; /*node is present but status as filled*/
+ }
+ MM_DBG("copying device info into node\n");
+ /*as device information is not present in cache copy
+ the current device information into the node*/
+ memcpy(&acdb_cache_free_node->device_info,
+ &audcal_info, sizeof(audcal_info));
+ return 0; /*cant find the node*/
+}
+
+static struct acdb_iir_block *get_audpp_irr_block(void)
+{
+ struct header *prs_hdr;
+ u32 index = 0;
+
+ while (index < acdb_data.acdb_result.used_bytes) {
+ prs_hdr = (struct header *)(acdb_data.virt_addr + index);
+ if (prs_hdr->dbor_signature == DBOR_SIGNATURE) {
+ if (prs_hdr->abid == ABID_AUDIO_IIR_RX) {
+ if (prs_hdr->iid == IID_AUDIO_IIR_COEFF)
+ return (struct acdb_iir_block *)
+ (acdb_data.virt_addr + index
+ + sizeof(struct header));
+ } else {
+ index += prs_hdr->data_len +
+ sizeof(struct header);
+ }
+ } else {
+ break;
+ }
+ }
+ return NULL;
+}
+
+
+static s32 acdb_fill_audpp_iir(void)
+{
+ struct acdb_iir_block *acdb_iir;
+ s32 i = 0;
+
+ acdb_iir = get_audpp_irr_block();
+ if (acdb_iir == NULL) {
+ MM_ERR("unable to find audpp iir block returning\n");
+ return -1;
+ }
+ memset(acdb_data.pp_iir, 0, sizeof(*acdb_data.pp_iir));
+
+ acdb_data.pp_iir->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS;
+ acdb_data.pp_iir->common.stream = AUDPP_CMD_COPP_STREAM;
+ acdb_data.pp_iir->common.stream_id = 0;
+ acdb_data.pp_iir->common.obj_cfg = AUDPP_CMD_OBJ0_UPDATE;
+ acdb_data.pp_iir->common.command_type = 0;
+
+ acdb_data.pp_iir->active_flag = acdb_iir->enable_flag;
+ acdb_data.pp_iir->num_bands = acdb_iir->stage_count;
+ for (; i < acdb_iir->stage_count; i++) {
+ acdb_data.pp_iir->params_filter.filter_4_params.
+ numerator_filter[i].numerator_b0_filter_lsw =
+ acdb_iir->stages[i].b0_lo;
+ acdb_data.pp_iir->params_filter.filter_4_params.
+ numerator_filter[i].numerator_b0_filter_msw =
+ acdb_iir->stages[i].b0_hi;
+ acdb_data.pp_iir->params_filter.filter_4_params.
+ numerator_filter[i].numerator_b1_filter_lsw =
+ acdb_iir->stages[i].b1_lo;
+ acdb_data.pp_iir->params_filter.filter_4_params.
+ numerator_filter[i].numerator_b1_filter_msw =
+ acdb_iir->stages[i].b1_hi;
+ acdb_data.pp_iir->params_filter.filter_4_params.
+ numerator_filter[i].numerator_b2_filter_lsw =
+ acdb_iir->stages[i].b2_lo;
+ acdb_data.pp_iir->params_filter.filter_4_params.
+ numerator_filter[i].numerator_b2_filter_msw =
+ acdb_iir->stages[i].b2_hi;
+ acdb_data.pp_iir->params_filter.filter_4_params.
+ denominator_filter[i].denominator_a0_filter_lsw =
+ acdb_iir->stages_a[i].a1_lo;
+ acdb_data.pp_iir->params_filter.filter_4_params.
+ denominator_filter[i].denominator_a0_filter_msw =
+ acdb_iir->stages_a[i].a1_hi;
+ acdb_data.pp_iir->params_filter.filter_4_params.
+ denominator_filter[i].denominator_a1_filter_lsw =
+ acdb_iir->stages_a[i].a2_lo;
+ acdb_data.pp_iir->params_filter.filter_4_params.
+ denominator_filter[i].denominator_a1_filter_msw =
+ acdb_iir->stages_a[i].a2_hi;
+ acdb_data.pp_iir->params_filter.filter_4_params.
+ shift_factor_filter[i].shift_factor_0 =
+ acdb_iir->shift_factor[i];
+ acdb_data.pp_iir->params_filter.filter_4_params.pan_filter[i].
+ pan_filter_0 = acdb_iir->pan[i];
+ }
+ return 0;
+}
+
+static void extract_mbadrc(u32 *phy_addr, struct header *prs_hdr, u32 *index)
+{
+ if (prs_hdr->iid == IID_MBADRC_EXT_BUFF) {
+ MM_DBG("Got IID = IID_MBADRC_EXT_BUFF\n");
+ *phy_addr = acdb_data.phys_addr + *index +
+ sizeof(struct header);
+ memcpy(acdb_data.mbadrc_block.ext_buf,
+ (acdb_data.virt_addr + *index +
+ sizeof(struct header)), 196*2);
+ MM_DBG("phy_addr = %x\n", *phy_addr);
+ *index += prs_hdr->data_len + sizeof(struct header);
+ } else if (prs_hdr->iid == IID_MBADRC_BAND_CONFIG) {
+ MM_DBG("Got IID == IID_MBADRC_BAND_CONFIG\n");
+ memcpy(acdb_data.mbadrc_block.band_config, (acdb_data.virt_addr
+ + *index + sizeof(struct header)),
+ sizeof(struct mbadrc_band_config_type) *
+ acdb_data.mbadrc_block.parameters.\
+ mbadrc_num_bands);
+ *index += prs_hdr->data_len + sizeof(struct header);
+ } else if (prs_hdr->iid == IID_MBADRC_PARAMETERS) {
+ struct mbadrc_parameter *tmp;
+ tmp = (struct mbadrc_parameter *)(acdb_data.virt_addr + *index
+ + sizeof(struct header));
+ MM_DBG("Got IID == IID_MBADRC_PARAMETERS\n");
+ acdb_data.mbadrc_block.parameters.mbadrc_enable =
+ tmp->mbadrc_enable;
+ acdb_data.mbadrc_block.parameters.mbadrc_num_bands =
+ tmp->mbadrc_num_bands;
+ acdb_data.mbadrc_block.parameters.mbadrc_down_sample_level =
+ tmp->mbadrc_down_sample_level;
+ acdb_data.mbadrc_block.parameters.mbadrc_delay =
+ tmp->mbadrc_delay;
+ *index += prs_hdr->data_len + sizeof(struct header);
+ }
+}
+
+static void get_audpp_mbadrc_block(u32 *phy_addr)
+{
+ struct header *prs_hdr;
+ u32 index = 0;
+
+ while (index < acdb_data.acdb_result.used_bytes) {
+ prs_hdr = (struct header *)(acdb_data.virt_addr + index);
+
+ if (prs_hdr->dbor_signature == DBOR_SIGNATURE) {
+ if (prs_hdr->abid == ABID_AUDIO_MBADRC_RX) {
+ if ((prs_hdr->iid == IID_MBADRC_EXT_BUFF)
+ || (prs_hdr->iid ==
+ IID_MBADRC_BAND_CONFIG)
+ || (prs_hdr->iid ==
+ IID_MBADRC_PARAMETERS)) {
+ extract_mbadrc(phy_addr, prs_hdr,
+ &index);
+ }
+ } else {
+ index += prs_hdr->data_len +
+ sizeof(struct header);
+ }
+ } else {
+ break;
+ }
+ }
+}
+
+static s32 acdb_fill_audpp_mbadrc(void)
+{
+ u32 mbadrc_phys_addr = -1;
+ get_audpp_mbadrc_block(&mbadrc_phys_addr);
+ if (IS_ERR_VALUE(mbadrc_phys_addr)) {
+ MM_ERR("failed to get mbadrc block\n");
+ return -1;
+ }
+
+ memset(acdb_data.pp_mbadrc, 0, sizeof(*acdb_data.pp_mbadrc));
+
+ acdb_data.pp_mbadrc->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS;
+ acdb_data.pp_mbadrc->common.stream = AUDPP_CMD_COPP_STREAM;
+ acdb_data.pp_mbadrc->common.stream_id = 0;
+ acdb_data.pp_mbadrc->common.obj_cfg = AUDPP_CMD_OBJ0_UPDATE;
+ acdb_data.pp_mbadrc->common.command_type = 0;
+
+ acdb_data.pp_mbadrc->enable = acdb_data.mbadrc_block.\
+ parameters.mbadrc_enable;
+ acdb_data.pp_mbadrc->num_bands =
+ acdb_data.mbadrc_block.\
+ parameters.mbadrc_num_bands;
+ acdb_data.pp_mbadrc->down_samp_level =
+ acdb_data.mbadrc_block.parameters.\
+ mbadrc_down_sample_level;
+ acdb_data.pp_mbadrc->adrc_delay =
+ acdb_data.mbadrc_block.parameters.\
+ mbadrc_delay;
+
+ if (acdb_data.mbadrc_block.parameters.mbadrc_num_bands > 1)
+ acdb_data.pp_mbadrc->ext_buf_size = (97 * 2) +
+ (33 * 2 * (acdb_data.mbadrc_block.parameters.\
+ mbadrc_num_bands - 2));
+
+ acdb_data.pp_mbadrc->ext_partition = 0;
+ acdb_data.pp_mbadrc->ext_buf_lsw = (u16)(mbadrc_phys_addr\
+ & 0xFFFF);
+ acdb_data.pp_mbadrc->ext_buf_msw = (u16)((mbadrc_phys_addr\
+ & 0xFFFF0000) >> 16);
+ memcpy(acdb_data.pp_mbadrc->adrc_band, acdb_data.mbadrc_block.\
+ band_config,
+ sizeof(struct mbadrc_band_config_type) *
+ acdb_data.mbadrc_block.parameters.mbadrc_num_bands);
+ return 0;
+}
+
+static struct acdb_calib_gain_rx *get_audpp_cal_gain(void)
+{
+ struct header *prs_hdr;
+ u32 index = 0;
+
+ while (index < acdb_data.acdb_result.used_bytes) {
+ prs_hdr = (struct header *)(acdb_data.virt_addr + index);
+ if (prs_hdr->dbor_signature == DBOR_SIGNATURE) {
+ if (prs_hdr->abid == ABID_AUDIO_CALIBRATION_GAIN_RX) {
+ if (prs_hdr->iid ==
+ IID_AUDIO_CALIBRATION_GAIN_RX) {
+ MM_DBG("Got audpp_calib_gain_rx"
+ " block\n");
+ return (struct acdb_calib_gain_rx *)
+ (acdb_data.virt_addr + index
+ + sizeof(struct header));
+ }
+ } else {
+ index += prs_hdr->data_len +
+ sizeof(struct header);
+ }
+ } else {
+ break;
+ }
+ }
+ return NULL;
+}
+
+static s32 acdb_fill_audpp_cal_gain(void)
+{
+ struct acdb_calib_gain_rx *acdb_calib_gain_rx = NULL;
+
+ acdb_calib_gain_rx = get_audpp_cal_gain();
+ if (acdb_calib_gain_rx == NULL) {
+ MM_ERR("unable to find audpp"
+ " calibration gain block returning\n");
+ return -1;
+ }
+ MM_DBG("Calibration value"
+ " for calib_gain_rx %d\n", acdb_calib_gain_rx->audppcalgain);
+ memset(acdb_data.calib_gain_rx, 0, sizeof(*acdb_data.calib_gain_rx));
+
+ acdb_data.calib_gain_rx->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS;
+ acdb_data.calib_gain_rx->common.stream = AUDPP_CMD_COPP_STREAM;
+ acdb_data.calib_gain_rx->common.stream_id = 0;
+ acdb_data.calib_gain_rx->common.obj_cfg = AUDPP_CMD_OBJ0_UPDATE;
+ acdb_data.calib_gain_rx->common.command_type = 0;
+
+ acdb_data.calib_gain_rx->audppcalgain =
+ acdb_calib_gain_rx->audppcalgain;
+ return 0;
+}
+
+static void extract_pbe_block(struct header *prs_hdr, u32 *index)
+{
+ if (prs_hdr->iid == IID_AUDIO_PBE_RX_ENABLE_FLAG) {
+ MM_DBG("Got IID = IID_AUDIO_PBE_RX_ENABLE\n");
+ acdb_data.pbe_enable_flag = (u16 *)(acdb_data.virt_addr +
+ *index +
+ sizeof(struct header));
+ *index += prs_hdr->data_len + sizeof(struct header);
+ } else if (prs_hdr->iid == IID_PBE_CONFIG_PARAMETERS) {
+ MM_DBG("Got IID == IID_PBE_CONFIG_PARAMETERS\n");
+ acdb_data.pbe_blk = (struct acdb_pbe_block *)
+ (acdb_data.virt_addr + *index
+ + sizeof(struct header));
+ *index += prs_hdr->data_len + sizeof(struct header);
+ }
+}
+
+static s32 get_audpp_pbe_block(void)
+{
+ struct header *prs_hdr;
+ u32 index = 0;
+ s32 result = -1;
+
+ while (index < acdb_data.acdb_result.used_bytes) {
+ prs_hdr = (struct header *)(acdb_data.virt_addr + index);
+
+ if (prs_hdr->dbor_signature == DBOR_SIGNATURE) {
+ if (prs_hdr->abid == ABID_AUDIO_PBE_RX) {
+ if ((prs_hdr->iid == IID_PBE_CONFIG_PARAMETERS)
+ || (prs_hdr->iid ==
+ IID_AUDIO_PBE_RX_ENABLE_FLAG)) {
+ extract_pbe_block(prs_hdr, &index);
+ result = 0;
+ }
+ } else {
+ index += prs_hdr->data_len +
+ sizeof(struct header);
+ }
+ } else {
+ break;
+ }
+ }
+ return result;
+}
+
+static s32 acdb_fill_audpp_pbe(void)
+{
+ s32 result = -1;
+
+ result = get_audpp_pbe_block();
+ if (IS_ERR_VALUE(result))
+ return result;
+ memset(acdb_data.pbe_block, 0, sizeof(*acdb_data.pbe_block));
+
+ acdb_data.pbe_block->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS;
+ acdb_data.pbe_block->common.stream = AUDPP_CMD_COPP_STREAM;
+ acdb_data.pbe_block->common.stream_id = 0;
+ acdb_data.pbe_block->common.obj_cfg = AUDPP_CMD_OBJ0_UPDATE;
+ acdb_data.pbe_block->common.command_type = 0;
+ acdb_data.pbe_block->pbe_enable = *acdb_data.pbe_enable_flag;
+
+ acdb_data.pbe_block->realbassmix = acdb_data.pbe_blk->realbassmix;
+ acdb_data.pbe_block->basscolorcontrol =
+ acdb_data.pbe_blk->basscolorcontrol;
+ acdb_data.pbe_block->mainchaindelay = acdb_data.pbe_blk->mainchaindelay;
+ acdb_data.pbe_block->xoverfltorder = acdb_data.pbe_blk->xoverfltorder;
+ acdb_data.pbe_block->bandpassfltorder =
+ acdb_data.pbe_blk->bandpassfltorder;
+ acdb_data.pbe_block->adrcdelay = acdb_data.pbe_blk->adrcdelay;
+ acdb_data.pbe_block->downsamplelevel =
+ acdb_data.pbe_blk->downsamplelevel;
+ acdb_data.pbe_block->comprmstav = acdb_data.pbe_blk->comprmstav;
+ acdb_data.pbe_block->expthreshold = acdb_data.pbe_blk->expthreshold;
+ acdb_data.pbe_block->expslope = acdb_data.pbe_blk->expslope;
+ acdb_data.pbe_block->compthreshold = acdb_data.pbe_blk->compthreshold;
+ acdb_data.pbe_block->compslope = acdb_data.pbe_blk->compslope;
+ acdb_data.pbe_block->cpmpattack_lsw = acdb_data.pbe_blk->cpmpattack_lsw;
+ acdb_data.pbe_block->compattack_msw = acdb_data.pbe_blk->compattack_msw;
+ acdb_data.pbe_block->comprelease_lsw =
+ acdb_data.pbe_blk->comprelease_lsw;
+ acdb_data.pbe_block->comprelease_msw =
+ acdb_data.pbe_blk->comprelease_msw;
+ acdb_data.pbe_block->compmakeupgain = acdb_data.pbe_blk->compmakeupgain;
+ acdb_data.pbe_block->baselimthreshold =
+ acdb_data.pbe_blk->baselimthreshold;
+ acdb_data.pbe_block->highlimthreshold =
+ acdb_data.pbe_blk->highlimthreshold;
+ acdb_data.pbe_block->basslimmakeupgain =
+ acdb_data.pbe_blk->basslimmakeupgain;
+ acdb_data.pbe_block->highlimmakeupgain =
+ acdb_data.pbe_blk->highlimmakeupgain;
+ acdb_data.pbe_block->limbassgrc = acdb_data.pbe_blk->limbassgrc;
+ acdb_data.pbe_block->limhighgrc = acdb_data.pbe_blk->limhighgrc;
+ acdb_data.pbe_block->limdelay = acdb_data.pbe_blk->limdelay;
+ memcpy(acdb_data.pbe_block->filter_coeffs,
+ acdb_data.pbe_blk->filter_coeffs, sizeof(u16)*90);
+ acdb_data.pbe_block->extpartition = 0;
+ acdb_data.pbe_block->extbuffsize_lsw = PBE_BUF_SIZE;
+ acdb_data.pbe_block->extbuffsize_msw = 0;
+ acdb_data.pbe_block->extbuffstart_lsw = ((u32)acdb_data.pbe_extbuff
+ & 0xFFFF);
+ acdb_data.pbe_block->extbuffstart_msw = (((u32)acdb_data.pbe_extbuff
+ & 0xFFFF0000) >> 16);
+ return 0;
+}
+
+
+static s32 acdb_calibrate_audpp(void)
+{
+ s32 result = 0;
+
+ result = acdb_fill_audpp_iir();
+ if (!IS_ERR_VALUE(result)) {
+ result = audpp_dsp_set_rx_iir(acdb_data.device_info->dev_id,
+ acdb_data.pp_iir->active_flag,
+ acdb_data.pp_iir, COPP);
+ if (result) {
+ MM_ERR("ACDB=> Failed to send IIR data to postproc\n");
+ result = -EINVAL;
+ goto done;
+ } else
+ MM_DBG("AUDPP is calibrated with IIR parameters"
+ " for COPP ID %d\n",
+ acdb_data.device_info->dev_id);
+ }
+ result = acdb_fill_audpp_mbadrc();
+ if (!IS_ERR_VALUE(result)) {
+ result = audpp_dsp_set_mbadrc(acdb_data.device_info->dev_id,
+ acdb_data.pp_mbadrc->enable,
+ acdb_data.pp_mbadrc, COPP);
+ if (result) {
+ MM_ERR("ACDB=> Failed to send MBADRC data to"
+ " postproc\n");
+ result = -EINVAL;
+ goto done;
+ } else
+ MM_DBG("AUDPP is calibrated with MBADRC parameters"
+ " for COPP ID %d\n",
+ acdb_data.device_info->dev_id);
+ }
+ result = acdb_fill_audpp_cal_gain();
+ if (!(IS_ERR_VALUE(result))) {
+ result = audpp_dsp_set_gain_rx(acdb_data.device_info->dev_id,
+ acdb_data.calib_gain_rx, COPP);
+ if (result) {
+ MM_ERR("ACDB=> Failed to send gain_rx"
+ " data to postproc\n");
+ result = -EINVAL;
+ goto done;
+ } else
+ MM_DBG("AUDPP is calibrated with calib_gain_rx\n");
+ }
+ result = acdb_fill_audpp_pbe();
+ if (!(IS_ERR_VALUE(result))) {
+ result = audpp_dsp_set_pbe(acdb_data.device_info->dev_id,
+ acdb_data.pbe_block->pbe_enable,
+ acdb_data.pbe_block, COPP);
+ if (result) {
+ MM_ERR("ACDB=> Failed to send pbe block"
+ "data to postproc\n");
+ result = -EINVAL;
+ goto done;
+ }
+ MM_DBG("AUDPP is calibarted with PBE\n");
+ }
+done:
+ return result;
+}
+
+static struct acdb_agc_block *get_audpreproc_agc_block(void)
+{
+ struct header *prs_hdr;
+ u32 index = 0;
+
+ while (index < acdb_data.acdb_result.used_bytes) {
+ prs_hdr = (struct header *)(acdb_data.virt_addr + index);
+ if (prs_hdr->dbor_signature == DBOR_SIGNATURE) {
+ if (prs_hdr->abid == ABID_AUDIO_AGC_TX) {
+ if (prs_hdr->iid == IID_AUDIO_AGC_PARAMETERS) {
+ MM_DBG("GOT ABID_AUDIO_AGC_TX\n");
+ return (struct acdb_agc_block *)
+ (acdb_data.virt_addr + index
+ + sizeof(struct header));
+ }
+ } else {
+ index += prs_hdr->data_len +
+ sizeof(struct header);
+ }
+ } else {
+ break;
+ }
+ }
+ return NULL;
+}
+
+static s32 acdb_fill_audpreproc_agc(void)
+{
+ struct acdb_agc_block *acdb_agc;
+
+ acdb_agc = get_audpreproc_agc_block();
+ if (!acdb_agc) {
+ MM_DBG("unable to find preproc agc parameters winding up\n");
+ return -1;
+ }
+ memset(acdb_data.preproc_agc, 0, sizeof(*acdb_data.preproc_agc));
+ acdb_data.preproc_agc->cmd_id = AUDPREPROC_CMD_CFG_AGC_PARAMS;
+ acdb_data.preproc_agc->stream_id = acdb_data.preproc_stream_id;
+ /* 0xFE00 to configure all parameters */
+ acdb_data.preproc_agc->tx_agc_param_mask = 0xFFFF;
+
+ if (acdb_agc->enable_status)
+ acdb_data.preproc_agc->tx_agc_enable_flag =
+ AUDPREPROC_CMD_TX_AGC_ENA_FLAG_ENA;
+ else
+ acdb_data.preproc_agc->tx_agc_enable_flag =
+ AUDPREPROC_CMD_TX_AGC_ENA_FLAG_DIS;
+
+ acdb_data.preproc_agc->comp_rlink_static_gain =
+ acdb_agc->comp_rlink_static_gain;
+ acdb_data.preproc_agc->comp_rlink_aig_flag =
+ acdb_agc->comp_rlink_aig_flag;
+ acdb_data.preproc_agc->expander_rlink_th =
+ acdb_agc->exp_rlink_threshold;
+ acdb_data.preproc_agc->expander_rlink_slope =
+ acdb_agc->exp_rlink_slope;
+ acdb_data.preproc_agc->compressor_rlink_th =
+ acdb_agc->comp_rlink_threshold;
+ acdb_data.preproc_agc->compressor_rlink_slope =
+ acdb_agc->comp_rlink_slope;
+
+ /* 0xFFF0 to configure all parameters */
+ acdb_data.preproc_agc->tx_adc_agc_param_mask = 0xFFFF;
+
+ acdb_data.preproc_agc->comp_rlink_aig_attackk =
+ acdb_agc->comp_rlink_aig_attack_k;
+ acdb_data.preproc_agc->comp_rlink_aig_leak_down =
+ acdb_agc->comp_rlink_aig_leak_down;
+ acdb_data.preproc_agc->comp_rlink_aig_leak_up =
+ acdb_agc->comp_rlink_aig_leak_up;
+ acdb_data.preproc_agc->comp_rlink_aig_max =
+ acdb_agc->comp_rlink_aig_max;
+ acdb_data.preproc_agc->comp_rlink_aig_min =
+ acdb_agc->comp_rlink_aig_min;
+ acdb_data.preproc_agc->comp_rlink_aig_releasek =
+ acdb_agc->comp_rlink_aig_release_k;
+ acdb_data.preproc_agc->comp_rlink_aig_leakrate_fast =
+ acdb_agc->comp_rlink_aig_sm_leak_rate_fast;
+ acdb_data.preproc_agc->comp_rlink_aig_leakrate_slow =
+ acdb_agc->comp_rlink_aig_sm_leak_rate_slow;
+ acdb_data.preproc_agc->comp_rlink_attackk_msw =
+ acdb_agc->comp_rlink_attack_k_msw;
+ acdb_data.preproc_agc->comp_rlink_attackk_lsw =
+ acdb_agc->comp_rlink_attack_k_lsw;
+ acdb_data.preproc_agc->comp_rlink_delay =
+ acdb_agc->comp_rlink_delay;
+ acdb_data.preproc_agc->comp_rlink_releasek_msw =
+ acdb_agc->comp_rlink_release_k_msw;
+ acdb_data.preproc_agc->comp_rlink_releasek_lsw =
+ acdb_agc->comp_rlink_release_k_lsw;
+ acdb_data.preproc_agc->comp_rlink_rms_tav =
+ acdb_agc->comp_rlink_rms_trav;
+ return 0;
+}
+
+static struct acdb_iir_block *get_audpreproc_irr_block(void)
+{
+
+ struct header *prs_hdr;
+ u32 index = 0;
+
+ while (index < acdb_data.acdb_result.used_bytes) {
+ prs_hdr = (struct header *)(acdb_data.virt_addr + index);
+
+ if (prs_hdr->dbor_signature == DBOR_SIGNATURE) {
+ if (prs_hdr->abid == ABID_AUDIO_IIR_TX) {
+ if (prs_hdr->iid == IID_AUDIO_IIR_COEFF)
+ return (struct acdb_iir_block *)
+ (acdb_data.virt_addr + index
+ + sizeof(struct header));
+ } else {
+ index += prs_hdr->data_len +
+ sizeof(struct header);
+ }
+ } else {
+ break;
+ }
+ }
+ return NULL;
+}
+
+
+static s32 acdb_fill_audpreproc_iir(void)
+{
+ struct acdb_iir_block *acdb_iir;
+
+
+ acdb_iir = get_audpreproc_irr_block();
+ if (!acdb_iir) {
+ MM_DBG("unable to find preproc iir parameters winding up\n");
+ return -1;
+ }
+ memset(acdb_data.preproc_iir, 0, sizeof(*acdb_data.preproc_iir));
+
+ acdb_data.preproc_iir->cmd_id =
+ AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS;
+ acdb_data.preproc_iir->stream_id = acdb_data.preproc_stream_id;
+ acdb_data.preproc_iir->active_flag = acdb_iir->enable_flag;
+ acdb_data.preproc_iir->num_bands = acdb_iir->stage_count;
+
+ acdb_data.preproc_iir->numerator_coeff_b0_filter0_lsw =
+ acdb_iir->stages[0].b0_lo;
+ acdb_data.preproc_iir->numerator_coeff_b0_filter0_msw =
+ acdb_iir->stages[0].b0_hi;
+ acdb_data.preproc_iir->numerator_coeff_b1_filter0_lsw =
+ acdb_iir->stages[0].b1_lo;
+ acdb_data.preproc_iir->numerator_coeff_b1_filter0_msw =
+ acdb_iir->stages[0].b1_hi;
+ acdb_data.preproc_iir->numerator_coeff_b2_filter0_lsw =
+ acdb_iir->stages[0].b2_lo;
+ acdb_data.preproc_iir->numerator_coeff_b2_filter0_msw =
+ acdb_iir->stages[0].b2_hi;
+
+ acdb_data.preproc_iir->numerator_coeff_b0_filter1_lsw =
+ acdb_iir->stages[1].b0_lo;
+ acdb_data.preproc_iir->numerator_coeff_b0_filter1_msw =
+ acdb_iir->stages[1].b0_hi;
+ acdb_data.preproc_iir->numerator_coeff_b1_filter1_lsw =
+ acdb_iir->stages[1].b1_lo;
+ acdb_data.preproc_iir->numerator_coeff_b1_filter1_msw =
+ acdb_iir->stages[1].b1_hi;
+ acdb_data.preproc_iir->numerator_coeff_b2_filter1_lsw =
+ acdb_iir->stages[1].b2_lo;
+ acdb_data.preproc_iir->numerator_coeff_b2_filter1_msw =
+ acdb_iir->stages[1].b2_hi;
+
+ acdb_data.preproc_iir->numerator_coeff_b0_filter2_lsw =
+ acdb_iir->stages[2].b0_lo;
+ acdb_data.preproc_iir->numerator_coeff_b0_filter2_msw =
+ acdb_iir->stages[2].b0_hi;
+ acdb_data.preproc_iir->numerator_coeff_b1_filter2_lsw =
+ acdb_iir->stages[2].b1_lo;
+ acdb_data.preproc_iir->numerator_coeff_b1_filter2_msw =
+ acdb_iir->stages[2].b1_hi;
+ acdb_data.preproc_iir->numerator_coeff_b2_filter2_lsw =
+ acdb_iir->stages[2].b2_lo;
+ acdb_data.preproc_iir->numerator_coeff_b2_filter2_msw =
+ acdb_iir->stages[2].b2_hi;
+
+ acdb_data.preproc_iir->numerator_coeff_b0_filter3_lsw =
+ acdb_iir->stages[3].b0_lo;
+ acdb_data.preproc_iir->numerator_coeff_b0_filter3_msw =
+ acdb_iir->stages[3].b0_hi;
+ acdb_data.preproc_iir->numerator_coeff_b1_filter3_lsw =
+ acdb_iir->stages[3].b1_lo;
+ acdb_data.preproc_iir->numerator_coeff_b1_filter3_msw =
+ acdb_iir->stages[3].b1_hi;
+ acdb_data.preproc_iir->numerator_coeff_b2_filter3_lsw =
+ acdb_iir->stages[3].b2_lo;
+ acdb_data.preproc_iir->numerator_coeff_b2_filter3_msw =
+ acdb_iir->stages[3].b2_hi;
+
+ acdb_data.preproc_iir->denominator_coeff_a0_filter0_lsw =
+ acdb_iir->stages_a[0].a1_lo;
+ acdb_data.preproc_iir->denominator_coeff_a0_filter0_msw =
+ acdb_iir->stages_a[0].a1_hi;
+ acdb_data.preproc_iir->denominator_coeff_a1_filter0_lsw =
+ acdb_iir->stages_a[0].a2_lo;
+ acdb_data.preproc_iir->denominator_coeff_a1_filter0_msw =
+ acdb_iir->stages_a[0].a2_hi;
+
+ acdb_data.preproc_iir->denominator_coeff_a0_filter1_lsw =
+ acdb_iir->stages_a[1].a1_lo;
+ acdb_data.preproc_iir->denominator_coeff_a0_filter1_msw =
+ acdb_iir->stages_a[1].a1_hi;
+ acdb_data.preproc_iir->denominator_coeff_a1_filter1_lsw =
+ acdb_iir->stages_a[1].a2_lo;
+ acdb_data.preproc_iir->denominator_coeff_a1_filter1_msw =
+ acdb_iir->stages_a[1].a2_hi;
+
+ acdb_data.preproc_iir->denominator_coeff_a0_filter2_lsw =
+ acdb_iir->stages_a[2].a1_lo;
+ acdb_data.preproc_iir->denominator_coeff_a0_filter2_msw =
+ acdb_iir->stages_a[2].a1_hi;
+ acdb_data.preproc_iir->denominator_coeff_a1_filter2_lsw =
+ acdb_iir->stages_a[2].a2_lo;
+ acdb_data.preproc_iir->denominator_coeff_a1_filter2_msw =
+ acdb_iir->stages_a[2].a2_hi;
+
+ acdb_data.preproc_iir->denominator_coeff_a0_filter3_lsw =
+ acdb_iir->stages_a[3].a1_lo;
+ acdb_data.preproc_iir->denominator_coeff_a0_filter3_msw =
+ acdb_iir->stages_a[3].a1_hi;
+ acdb_data.preproc_iir->denominator_coeff_a1_filter3_lsw =
+ acdb_iir->stages_a[3].a2_lo;
+ acdb_data.preproc_iir->denominator_coeff_a1_filter3_msw =
+ acdb_iir->stages_a[3].a2_hi;
+
+ acdb_data.preproc_iir->shift_factor_filter0 =
+ acdb_iir->shift_factor[0];
+ acdb_data.preproc_iir->shift_factor_filter1 =
+ acdb_iir->shift_factor[1];
+ acdb_data.preproc_iir->shift_factor_filter2 =
+ acdb_iir->shift_factor[2];
+ acdb_data.preproc_iir->shift_factor_filter3 =
+ acdb_iir->shift_factor[3];
+
+ acdb_data.preproc_iir->pan_of_filter0 =
+ acdb_iir->pan[0];
+ acdb_data.preproc_iir->pan_of_filter1 =
+ acdb_iir->pan[1];
+ acdb_data.preproc_iir->pan_of_filter2 =
+ acdb_iir->pan[2];
+ acdb_data.preproc_iir->pan_of_filter3 =
+ acdb_iir->pan[3];
+ return 0;
+}
+
+static struct acdb_calib_gain_tx *get_audpreproc_cal_gain(void)
+{
+ struct header *prs_hdr;
+ u32 index = 0;
+
+ while (index < acdb_data.acdb_result.used_bytes) {
+ prs_hdr = (struct header *)(acdb_data.virt_addr + index);
+ if (prs_hdr->dbor_signature == DBOR_SIGNATURE) {
+ if (prs_hdr->abid == ABID_AUDIO_CALIBRATION_GAIN_TX) {
+ if (prs_hdr->iid ==
+ IID_AUDIO_CALIBRATION_GAIN_TX) {
+ MM_DBG("Got audpreproc_calib_gain_tx"
+ " block\n");
+ return (struct acdb_calib_gain_tx *)
+ (acdb_data.virt_addr + index
+ + sizeof(struct header));
+ }
+ } else {
+ index += prs_hdr->data_len +
+ sizeof(struct header);
+ }
+ } else {
+ break;
+ }
+ }
+ return NULL;
+}
+
+static s32 acdb_fill_audpreproc_cal_gain(void)
+{
+ struct acdb_calib_gain_tx *acdb_calib_gain_tx = NULL;
+
+ acdb_calib_gain_tx = get_audpreproc_cal_gain();
+ if (acdb_calib_gain_tx == NULL) {
+ MM_ERR("unable to find audpreproc"
+ " calibration block returning\n");
+ return -1;
+ }
+ MM_DBG("Calibration value"
+ " for calib_gain_tx %d\n", acdb_calib_gain_tx->audprecalgain);
+ memset(acdb_data.calib_gain_tx, 0, sizeof(*acdb_data.calib_gain_tx));
+
+ acdb_data.calib_gain_tx->cmd_id =
+ AUDPREPROC_CMD_CFG_CAL_GAIN_PARAMS;
+ acdb_data.calib_gain_tx->stream_id = acdb_data.preproc_stream_id;
+ acdb_data.calib_gain_tx->audprecalgain =
+ acdb_calib_gain_tx->audprecalgain;
+ return 0;
+}
+
+static struct acdb_rmc_block *get_rmc_blk(void)
+{
+ struct header *prs_hdr;
+ u32 index = 0;
+
+ while (index < acdb_data.acdb_result.used_bytes) {
+ prs_hdr = (struct header *)(acdb_data.virt_addr + index);
+ if (prs_hdr->dbor_signature == DBOR_SIGNATURE) {
+ if (prs_hdr->abid == ABID_AUDIO_RMC_TX) {
+ if (prs_hdr->iid ==
+ IID_AUDIO_RMC_PARAM) {
+ MM_DBG("Got afe_rmc block\n");
+ return (struct acdb_rmc_block *)
+ (acdb_data.virt_addr + index
+ + sizeof(struct header));
+ }
+ } else {
+ index += prs_hdr->data_len +
+ sizeof(struct header);
+ }
+ } else {
+ break;
+ }
+ }
+ return NULL;
+}
+
+struct acdb_fluence_block *get_audpp_fluence_block(void)
+{
+ struct header *prs_hdr;
+ u32 index = 0;
+
+ while (index < acdb_data.acdb_result.used_bytes) {
+ prs_hdr = (struct header *)(acdb_data.virt_addr + index);
+
+ if (prs_hdr->dbor_signature == DBOR_SIGNATURE) {
+ if (prs_hdr->abid == ABID_AUDIO_FLUENCE_TX) {
+ if (prs_hdr->iid == IID_AUDIO_FLUENCE_TX) {
+ MM_DBG("got fluence block\n");
+ return (struct acdb_fluence_block *)
+ (acdb_data.virt_addr + index
+ + sizeof(struct header));
+ }
+ } else {
+ index += prs_hdr->data_len +
+ sizeof(struct header);
+ }
+ } else {
+ break;
+ }
+ }
+ return NULL;
+}
+
+static s32 acdb_fill_audpreproc_fluence(void)
+{
+ struct acdb_fluence_block *fluence_block = NULL;
+ fluence_block = get_audpp_fluence_block();
+ if (!fluence_block) {
+ MM_ERR("error in finding fluence block\n");
+ return -EPERM;
+ }
+ memset(&acdb_data.preproc_lvnv, 0, sizeof(
+ struct audpreproc_cmd_cfg_lvnv_param));
+ memcpy(acdb_data.fluence_extbuff_virt,
+ &fluence_block->cs_tuningMode,
+ (sizeof(struct acdb_fluence_block) -
+ sizeof(fluence_block->csmode)));
+ acdb_data.preproc_lvnv.cmd_id = AUDPREPROC_CMD_CFG_LVNV_PARMS;
+ acdb_data.preproc_lvnv.stream_id = acdb_data.preproc_stream_id;
+ acdb_data.preproc_lvnv.cs_mode = fluence_block->csmode;
+ acdb_data.preproc_lvnv.lvnv_ext_buf_size = FLUENCE_BUF_SIZE;
+ acdb_data.preproc_lvnv.lvnv_ext_buf_start_lsw =\
+ ((u32)(acdb_data.fluence_extbuff)\
+ & 0x0000FFFF);
+ acdb_data.preproc_lvnv.lvnv_ext_buf_start_msw =\
+ (((u32)acdb_data.fluence_extbuff\
+ & 0xFFFF0000) >> 16);
+ return 0;
+}
+
+s32 acdb_calibrate_audpreproc(void)
+{
+ s32 result = 0;
+ struct acdb_rmc_block *acdb_rmc = NULL;
+
+ result = acdb_fill_audpreproc_agc();
+ if (!IS_ERR_VALUE(result)) {
+ result = audpreproc_dsp_set_agc(acdb_data.preproc_agc, sizeof(
+ struct audpreproc_cmd_cfg_agc_params));
+ if (result) {
+ MM_ERR("ACDB=> Failed to send AGC data to preproc)\n");
+ result = -EINVAL;
+ goto done;
+ } else
+ MM_DBG("AUDPREC is calibrated with AGC parameters"
+ " for COPP ID %d and AUDREC session %d\n",
+ acdb_data.device_info->dev_id,
+ acdb_data.preproc_stream_id);
+ }
+ result = acdb_fill_audpreproc_iir();
+ if (!IS_ERR_VALUE(result)) {
+ result = audpreproc_dsp_set_iir(acdb_data.preproc_iir,
+ sizeof(struct\
+ audpreproc_cmd_cfg_iir_tuning_filter_params));
+ if (result) {
+ MM_ERR("ACDB=> Failed to send IIR data to preproc\n");
+ result = -EINVAL;
+ goto done;
+ } else
+ MM_DBG("audpreproc is calibrated with iir parameters"
+ " for COPP ID %d and AUREC session %d\n",
+ acdb_data.device_info->dev_id,
+ acdb_data.preproc_stream_id);
+ }
+ result = acdb_fill_audpreproc_cal_gain();
+ if (!(IS_ERR_VALUE(result))) {
+ result = audpreproc_dsp_set_gain_tx(acdb_data.calib_gain_tx,
+ sizeof(struct audpreproc_cmd_cfg_cal_gain));
+ if (result) {
+ MM_ERR("ACDB=> Failed to send calib_gain_tx"
+ " data to preproc\n");
+ result = -EINVAL;
+ goto done;
+ } else
+ MM_DBG("AUDPREPROC is calibrated"
+ " with calib_gain_tx\n");
+ }
+ acdb_rmc = get_rmc_blk();
+ if (acdb_rmc != NULL) {
+ result = afe_config_rmc_block(acdb_rmc);
+ if (result) {
+ MM_ERR("ACDB=> Failed to send rmc"
+ " data to afe\n");
+ result = -EINVAL;
+ goto done;
+ } else
+ MM_DBG("AFE is calibrated with rmc params\n");
+ } else
+ MM_DBG("RMC block was not found\n");
+ if (!acdb_data.fleuce_feature_status[acdb_data.preproc_stream_id]) {
+ result = acdb_fill_audpreproc_fluence();
+ if (!(IS_ERR_VALUE(result))) {
+ result = audpreproc_dsp_set_lvnv(
+ &acdb_data.preproc_lvnv,
+ sizeof(struct\
+ audpreproc_cmd_cfg_lvnv_param));
+ if (result) {
+ MM_ERR("ACDB=> Failed to send lvnv "
+ "data to preproc\n");
+ result = -EINVAL;
+ goto done;
+ } else
+ MM_DBG("AUDPREPROC is calibrated"
+ " with lvnv parameters\n");
+ } else
+ MM_ERR("fluence block is not found\n");
+ } else
+ MM_DBG("fluence block override\n");
+done:
+ return result;
+}
+
+static s32 acdb_send_calibration(void)
+{
+ s32 result = 0;
+
+ if ((acdb_data.device_info->dev_type & RX_DEVICE) == 1) {
+ result = acdb_calibrate_audpp();
+ if (result)
+ goto done;
+ } else if ((acdb_data.device_info->dev_type & TX_DEVICE) == 2) {
+ result = acdb_calibrate_audpreproc();
+ if (result)
+ goto done;
+ if (acdb_data.preproc_stream_id == 0)
+ acdb_data.audrec_applied |= AUDREC0_READY;
+ else if (acdb_data.preproc_stream_id == 1)
+ acdb_data.audrec_applied |= AUDREC1_READY;
+ else if (acdb_data.preproc_stream_id == 2)
+ acdb_data.audrec_applied |= AUDREC2_READY;
+ MM_DBG("acdb_data.audrec_applied = %x\n",
+ acdb_data.audrec_applied);
+ }
+done:
+ return result;
+}
+
+static u8 check_tx_acdb_values_cached(void)
+{
+ u8 stream_id = acdb_data.preproc_stream_id;
+
+ if ((acdb_data.device_info->dev_id ==
+ acdb_cache_tx[stream_id].device_info.dev_id) &&
+ (acdb_data.device_info->sample_rate ==
+ acdb_cache_tx[stream_id].device_info.sample_rate) &&
+ (acdb_data.device_info->acdb_id ==
+ acdb_cache_tx[stream_id].device_info.acdb_id) &&
+ (acdb_cache_tx[stream_id].node_status ==
+ ACDB_VALUES_FILLED))
+ return 0;
+ else
+ return 1;
+}
+
+static void handle_tx_device_ready_callback(void)
+{
+ u8 i = 0;
+ u8 ret = 0;
+ u8 acdb_value_apply = 0;
+ u8 result = 0;
+ u8 stream_id = acdb_data.preproc_stream_id;
+
+ if (acdb_data.multiple_sessions) {
+ for (i = 0; i < MAX_AUDREC_SESSIONS; i++) {
+ /*check is to exclude copying acdb values in the
+ current node pointed by acdb_data structure*/
+ if (acdb_cache_tx[i].phys_addr_acdb_values !=
+ acdb_data.phys_addr) {
+ ret = check_device_info_already_present(\
+ *acdb_data.device_info,
+ &acdb_cache_tx[i]);
+ if (ret) {
+ memcpy((char *)acdb_cache_tx[i].\
+ virt_addr_acdb_values,
+ (char *)acdb_data.virt_addr,
+ ACDB_BUF_SIZE);
+ acdb_cache_tx[i].node_status =
+ ACDB_VALUES_FILLED;
+ }
+ }
+ }
+ acdb_data.multiple_sessions = 0;
+ }
+ /*check wheather AUDREC enabled before device call backs*/
+ if ((acdb_data.acdb_state & AUDREC0_READY) &&
+ !(acdb_data.audrec_applied & AUDREC0_READY)) {
+ MM_DBG("AUDREC0 already enabled apply acdb values\n");
+ acdb_value_apply |= AUDREC0_READY;
+ } else if ((acdb_data.acdb_state & AUDREC1_READY) &&
+ !(acdb_data.audrec_applied & AUDREC1_READY)) {
+ MM_DBG("AUDREC1 already enabled apply acdb values\n");
+ acdb_value_apply |= AUDREC1_READY;
+ } else if ((acdb_data.acdb_state & AUDREC2_READY) &&
+ !(acdb_data.audrec_applied & AUDREC2_READY)) {
+ MM_DBG("AUDREC2 already enabled apply acdb values\n");
+ acdb_value_apply |= AUDREC2_READY;
+ }
+ if (acdb_value_apply) {
+ if (session_info[stream_id].sampling_freq)
+ acdb_data.device_info->sample_rate =
+ session_info[stream_id].sampling_freq;
+ result = check_tx_acdb_values_cached();
+ if (result) {
+ result = acdb_get_calibration();
+ if (result < 0) {
+ MM_ERR("Not able to get calibration"
+ " data continue\n");
+ return;
+ }
+ }
+ acdb_cache_tx[stream_id].node_status = ACDB_VALUES_FILLED;
+ acdb_send_calibration();
+ }
+}
+
+static struct acdb_cache_node *get_acdb_values_from_cache_tx(u32 stream_id)
+{
+ MM_DBG("searching node with stream_id %d\n", stream_id);
+ if ((acdb_cache_tx[stream_id].stream_id == stream_id) &&
+ (acdb_cache_tx[stream_id].node_status ==
+ ACDB_VALUES_NOT_FILLED)) {
+ return &acdb_cache_tx[stream_id];
+ }
+ MM_DBG("Error! in finding node\n");
+ return NULL;
+}
+
+static void update_acdb_data_struct(struct acdb_cache_node *cur_node)
+{
+ if (cur_node) {
+ acdb_data.device_info = &cur_node->device_info;
+ acdb_data.virt_addr = cur_node->virt_addr_acdb_values;
+ acdb_data.phys_addr = cur_node->phys_addr_acdb_values;
+ } else
+ MM_ERR("error in curent node\n");
+}
+
+static void send_acdb_values_for_active_devices(void)
+{
+ u32 i = 0;
+ for (i = 0; i < MAX_COPP_NODE_SUPPORTED; i++) {
+ if (acdb_cache_rx[i].node_status ==
+ ACDB_VALUES_FILLED) {
+ update_acdb_data_struct(&acdb_cache_rx[i]);
+ if (acdb_data.acdb_state & CAL_DATA_READY)
+ acdb_send_calibration();
+ }
+ }
+}
+
+static s32 initialize_rpc(void)
+{
+ s32 result = 0;
+
+ result = daldevice_attach(DALDEVICEID_ACDB, ACDB_PORT_NAME,
+ ACDB_CPU, &acdb_data.handle);
+
+ if (result) {
+ MM_ERR("ACDB=> Device Attach failed\n");
+ result = -ENODEV;
+ goto done;
+ }
+done:
+ return result;
+}
+
+static u32 allocate_memory_acdb_cache_tx(void)
+{
+ u32 result = 0;
+ u32 i = 0;
+ u32 err = 0;
+ /*initialize local cache */
+ for (i = 0; i < MAX_AUDREC_SESSIONS; i++) {
+ acdb_cache_tx[i].phys_addr_acdb_values =
+ pmem_kalloc(ACDB_BUF_SIZE,
+ (PMEM_MEMTYPE_EBI1
+ | PMEM_ALIGNMENT_4K));
+
+ if (IS_ERR((void *)acdb_cache_tx[i].phys_addr_acdb_values)) {
+ MM_ERR("ACDB=> Cannot allocate physical memory\n");
+ result = -ENOMEM;
+ goto error;
+ }
+ acdb_cache_tx[i].virt_addr_acdb_values =
+ ioremap(
+ acdb_cache_tx[i].phys_addr_acdb_values,
+ ACDB_BUF_SIZE);
+ if (acdb_cache_tx[i].virt_addr_acdb_values == NULL) {
+ MM_ERR("ACDB=> Could not map physical address\n");
+ result = -ENOMEM;
+ pmem_kfree(acdb_cache_tx[i].phys_addr_acdb_values);
+ goto error;
+ }
+ memset(acdb_cache_tx[i].virt_addr_acdb_values, 0,
+ ACDB_BUF_SIZE);
+ }
+ return result;
+error:
+ for (err = 0; err < i; err++) {
+ iounmap(acdb_cache_tx[i].virt_addr_acdb_values);
+ pmem_kfree(acdb_cache_tx[i].phys_addr_acdb_values);
+
+ }
+ return result;
+}
+
+static u32 allocate_memory_acdb_cache_rx(void)
+{
+ u32 result = 0;
+ u32 i = 0;
+ u32 err = 0;
+
+ /*initialize local cache */
+ for (i = 0; i < MAX_COPP_NODE_SUPPORTED; i++) {
+ acdb_cache_rx[i].phys_addr_acdb_values =
+ pmem_kalloc(ACDB_BUF_SIZE,
+ (PMEM_MEMTYPE_EBI1
+ | PMEM_ALIGNMENT_4K));
+
+ if (IS_ERR((void *)acdb_cache_rx[i].phys_addr_acdb_values)) {
+ MM_ERR("ACDB=> Can not allocate physical memory\n");
+ result = -ENOMEM;
+ goto error;
+ }
+ acdb_cache_rx[i].virt_addr_acdb_values =
+ ioremap(
+ acdb_cache_rx[i].phys_addr_acdb_values,
+ ACDB_BUF_SIZE);
+ if (acdb_cache_rx[i].virt_addr_acdb_values == NULL) {
+ MM_ERR("ACDB=> Could not map physical address\n");
+ result = -ENOMEM;
+ pmem_kfree(acdb_cache_rx[i].phys_addr_acdb_values);
+ goto error;
+ }
+ memset(acdb_cache_rx[i].virt_addr_acdb_values, 0,
+ ACDB_BUF_SIZE);
+ }
+ return result;
+error:
+ for (err = 0; err < i; err++) {
+ iounmap(acdb_cache_rx[i].virt_addr_acdb_values);
+ pmem_kfree(acdb_cache_rx[i].phys_addr_acdb_values);
+
+ }
+ return result;
+}
+
+static u32 allocate_memory_acdb_get_blk(void)
+{
+ u32 result = 0;
+ acdb_data.get_blk_paddr = pmem_kalloc(ACDB_BUF_SIZE,
+ (PMEM_MEMTYPE_EBI1
+ | PMEM_ALIGNMENT_4K));
+ if (IS_ERR((void *)acdb_data.get_blk_paddr)) {
+ MM_ERR("ACDB=> Cannot allocate physical memory\n");
+ result = -ENOMEM;
+ goto error;
+ }
+ acdb_data.get_blk_kvaddr = ioremap(acdb_data.get_blk_paddr,
+ ACDB_BUF_SIZE);
+ if (acdb_data.get_blk_kvaddr == NULL) {
+ MM_ERR("ACDB=> Could not map physical address\n");
+ result = -ENOMEM;
+ pmem_kfree(acdb_data.get_blk_paddr);
+ goto error;
+ }
+ memset(acdb_data.get_blk_kvaddr, 0, ACDB_BUF_SIZE);
+error:
+ return result;
+}
+
+static void free_memory_acdb_cache_rx(void)
+{
+ u32 i = 0;
+
+ for (i = 0; i < MAX_COPP_NODE_SUPPORTED; i++) {
+ iounmap(acdb_cache_rx[i].virt_addr_acdb_values);
+ pmem_kfree(acdb_cache_rx[i].phys_addr_acdb_values);
+ }
+}
+
+static void free_memory_acdb_cache_tx(void)
+{
+ u32 i = 0;
+
+ for (i = 0; i < MAX_AUDREC_SESSIONS; i++) {
+ iounmap(acdb_cache_tx[i].virt_addr_acdb_values);
+ pmem_kfree(acdb_cache_tx[i].phys_addr_acdb_values);
+ }
+}
+
+static void free_memory_acdb_get_blk(void)
+{
+ iounmap(acdb_data.get_blk_kvaddr);
+ pmem_kfree(acdb_data.get_blk_paddr);
+}
+
+static s32 initialize_memory(void)
+{
+ s32 result = 0;
+
+ result = allocate_memory_acdb_get_blk();
+ if (result < 0) {
+ MM_ERR("memory allocation for get blk failed\n");
+ goto done;
+ }
+
+ result = allocate_memory_acdb_cache_rx();
+ if (result < 0) {
+ MM_ERR("memory allocation for rx cache is failed\n");
+ free_memory_acdb_get_blk();
+ goto done;
+ }
+ result = allocate_memory_acdb_cache_tx();
+ if (result < 0) {
+ MM_ERR("memory allocation for tx cache is failed\n");
+ free_memory_acdb_get_blk();
+ free_memory_acdb_cache_rx();
+ goto done;
+ }
+ acdb_data.pp_iir = kmalloc(sizeof(*acdb_data.pp_iir),
+ GFP_KERNEL);
+ if (acdb_data.pp_iir == NULL) {
+ MM_ERR("ACDB=> Could not allocate postproc iir memory\n");
+ free_memory_acdb_get_blk();
+ free_memory_acdb_cache_rx();
+ free_memory_acdb_cache_tx();
+ result = -ENOMEM;
+ goto done;
+ }
+
+ acdb_data.pp_mbadrc = kmalloc(sizeof(*acdb_data.pp_mbadrc), GFP_KERNEL);
+ if (acdb_data.pp_mbadrc == NULL) {
+ MM_ERR("ACDB=> Could not allocate postproc mbadrc memory\n");
+ free_memory_acdb_get_blk();
+ free_memory_acdb_cache_rx();
+ free_memory_acdb_cache_tx();
+ kfree(acdb_data.pp_iir);
+ result = -ENOMEM;
+ goto done;
+ }
+ acdb_data.calib_gain_rx = kmalloc(sizeof(*acdb_data.calib_gain_rx),
+ GFP_KERNEL);
+ if (acdb_data.calib_gain_rx == NULL) {
+ MM_ERR("ACDB=> Could not allocate"
+ " postproc calib_gain_rx memory\n");
+ free_memory_acdb_get_blk();
+ free_memory_acdb_cache_rx();
+ free_memory_acdb_cache_tx();
+ kfree(acdb_data.pp_iir);
+ kfree(acdb_data.pp_mbadrc);
+ result = -ENOMEM;
+ goto done;
+ }
+
+ acdb_data.preproc_agc = kmalloc(sizeof(*acdb_data.preproc_agc),
+ GFP_KERNEL);
+ if (acdb_data.preproc_agc == NULL) {
+ MM_ERR("ACDB=> Could not allocate preproc agc memory\n");
+ free_memory_acdb_get_blk();
+ free_memory_acdb_cache_rx();
+ free_memory_acdb_cache_tx();
+ kfree(acdb_data.pp_iir);
+ kfree(acdb_data.pp_mbadrc);
+ kfree(acdb_data.calib_gain_rx);
+ result = -ENOMEM;
+ goto done;
+ }
+
+ acdb_data.preproc_iir = kmalloc(sizeof(*acdb_data.preproc_iir),
+ GFP_KERNEL);
+ if (acdb_data.preproc_iir == NULL) {
+ MM_ERR("ACDB=> Could not allocate preproc iir memory\n");
+ free_memory_acdb_get_blk();
+ free_memory_acdb_cache_rx();
+ free_memory_acdb_cache_tx();
+ kfree(acdb_data.pp_iir);
+ kfree(acdb_data.pp_mbadrc);
+ kfree(acdb_data.calib_gain_rx);
+ kfree(acdb_data.preproc_agc);
+ result = -ENOMEM;
+ goto done;
+ }
+ acdb_data.calib_gain_tx = kmalloc(sizeof(*acdb_data.calib_gain_tx),
+ GFP_KERNEL);
+ if (acdb_data.calib_gain_tx == NULL) {
+ MM_ERR("ACDB=> Could not allocate"
+ " preproc calib_gain_tx memory\n");
+ free_memory_acdb_get_blk();
+ free_memory_acdb_cache_rx();
+ free_memory_acdb_cache_tx();
+ kfree(acdb_data.pp_iir);
+ kfree(acdb_data.pp_mbadrc);
+ kfree(acdb_data.calib_gain_rx);
+ kfree(acdb_data.preproc_agc);
+ kfree(acdb_data.preproc_iir);
+ result = -ENOMEM;
+ goto done;
+ }
+ acdb_data.pbe_block = kmalloc(sizeof(*acdb_data.pbe_block),
+ GFP_KERNEL);
+ if (acdb_data.pbe_block == NULL) {
+ MM_ERR("ACDB=> Could not allocate pbe_block memory\n");
+ free_memory_acdb_get_blk();
+ free_memory_acdb_cache_rx();
+ free_memory_acdb_cache_tx();
+ kfree(acdb_data.pp_iir);
+ kfree(acdb_data.pp_mbadrc);
+ kfree(acdb_data.calib_gain_rx);
+ kfree(acdb_data.preproc_agc);
+ kfree(acdb_data.preproc_iir);
+ kfree(acdb_data.calib_gain_tx);
+ result = -ENOMEM;
+ goto done;
+ }
+ acdb_data.pbe_extbuff = (u16 *)(pmem_kalloc(PBE_BUF_SIZE,
+ (PMEM_MEMTYPE_EBI1 |
+ PMEM_ALIGNMENT_4K)));
+ if (IS_ERR((void *)acdb_data.pbe_extbuff)) {
+ MM_ERR("ACDB=> Cannot allocate physical memory\n");
+ free_memory_acdb_get_blk();
+ free_memory_acdb_cache_rx();
+ free_memory_acdb_cache_tx();
+ kfree(acdb_data.pp_iir);
+ kfree(acdb_data.pp_mbadrc);
+ kfree(acdb_data.calib_gain_rx);
+ kfree(acdb_data.preproc_agc);
+ kfree(acdb_data.preproc_iir);
+ kfree(acdb_data.calib_gain_tx);
+ kfree(acdb_data.pbe_block);
+ result = -ENOMEM;
+ goto done;
+ }
+ acdb_data.fluence_extbuff = pmem_kalloc(FLUENCE_BUF_SIZE,
+ (PMEM_MEMTYPE_EBI1 |
+ PMEM_ALIGNMENT_4K));
+ if (IS_ERR((void *)acdb_data.fluence_extbuff)) {
+ MM_ERR("ACDB=> cannot allocate physical memory for "
+ "fluence block\n");
+ free_memory_acdb_get_blk();
+ free_memory_acdb_cache_rx();
+ free_memory_acdb_cache_tx();
+ kfree(acdb_data.pp_iir);
+ kfree(acdb_data.pp_mbadrc);
+ kfree(acdb_data.calib_gain_rx);
+ kfree(acdb_data.preproc_agc);
+ kfree(acdb_data.preproc_iir);
+ kfree(acdb_data.calib_gain_tx);
+ kfree(acdb_data.pbe_block);
+ pmem_kfree((int32_t)acdb_data.pbe_extbuff);
+ result = -ENOMEM;
+ goto done;
+ }
+ acdb_data.fluence_extbuff_virt =
+ ioremap(
+ acdb_data.fluence_extbuff,
+ FLUENCE_BUF_SIZE);
+ if (acdb_data.fluence_extbuff_virt == NULL) {
+ MM_ERR("ACDB=> Could not map physical address\n");
+ free_memory_acdb_get_blk();
+ free_memory_acdb_cache_rx();
+ free_memory_acdb_cache_tx();
+ kfree(acdb_data.pp_iir);
+ kfree(acdb_data.pp_mbadrc);
+ kfree(acdb_data.calib_gain_rx);
+ kfree(acdb_data.preproc_agc);
+ kfree(acdb_data.preproc_iir);
+ kfree(acdb_data.calib_gain_tx);
+ kfree(acdb_data.pbe_block);
+ pmem_kfree((int32_t)acdb_data.pbe_extbuff);
+ pmem_kfree((int32_t)acdb_data.fluence_extbuff);
+ result = -ENOMEM;
+ goto done;
+ }
+done:
+ return result;
+}
+
+static u32 free_acdb_cache_node(union auddev_evt_data *evt)
+{
+ u32 session_id;
+ if ((evt->audcal_info.dev_type & TX_DEVICE) == 2) {
+ /*Second argument to find_first_bit should be maximum number
+ of bits interested
+ */
+ session_id = find_first_bit(
+ (unsigned long *)&(evt->audcal_info.sessions),
+ sizeof(evt->audcal_info.sessions) * 8);
+ MM_DBG("freeing node %d for tx device", session_id);
+ acdb_cache_tx[session_id].
+ node_status = ACDB_VALUES_NOT_FILLED;
+ } else {
+ if (--(acdb_cache_rx[evt->audcal_info.dev_id].stream_id) <= 0) {
+ MM_DBG("freeing rx cache node %d\n",
+ evt->audcal_info.dev_id);
+ acdb_cache_rx[evt->audcal_info.dev_id].
+ node_status = ACDB_VALUES_NOT_FILLED;
+ acdb_cache_rx[evt->audcal_info.dev_id].stream_id = 0;
+ }
+ }
+ return 0;
+}
+
+static u8 check_device_change(struct auddev_evt_audcal_info audcal_info)
+{
+ if (!acdb_data.device_info) {
+ MM_ERR("not pointing to previous valid device detail\n");
+ return 1; /*device info will not be pointing to*/
+ /* valid device when acdb driver comes up*/
+ }
+ if ((audcal_info.dev_id == acdb_data.device_info->dev_id) &&
+ (audcal_info.sample_rate ==
+ acdb_data.device_info->sample_rate) &&
+ (audcal_info.acdb_id == acdb_data.device_info->acdb_id)) {
+ return 0;
+ }
+ return 1;
+}
+
+static void device_cb(u32 evt_id, union auddev_evt_data *evt, void *private)
+{
+ struct auddev_evt_audcal_info audcal_info;
+ struct acdb_cache_node *acdb_cache_free_node = NULL;
+ u32 stream_id = 0;
+ u8 ret = 0;
+ u8 count = 0;
+ u8 i = 0;
+ u8 device_change = 0;
+
+ if (!((evt_id == AUDDEV_EVT_DEV_RDY) ||
+ (evt_id == AUDDEV_EVT_DEV_RLS))) {
+ goto done;
+ }
+ /*if session value is zero it indicates that device call back is for
+ voice call we will drop the request as acdb values for voice call is
+ not applied from acdb driver*/
+ if (!evt->audcal_info.sessions) {
+ MM_DBG("no active sessions and call back is for"
+ " voice call\n");
+ goto done;
+ }
+ if (evt_id == AUDDEV_EVT_DEV_RLS) {
+ MM_DBG("got release command for dev %d\n",
+ evt->audcal_info.dev_id);
+ acdb_data.acdb_state &= ~CAL_DATA_READY;
+ free_acdb_cache_node(evt);
+ /*reset the applied flag for the session routed to the device*/
+ acdb_data.audrec_applied &= ~(evt->audcal_info.sessions
+ << AUDREC_OFFSET);
+ goto done;
+ }
+ if (((evt->audcal_info.dev_type & RX_DEVICE) == 1) &&
+ (evt->audcal_info.acdb_id == PSEUDO_ACDB_ID)) {
+ MM_INFO("device cb is for rx device with pseudo acdb id\n");
+ goto done;
+ }
+ audcal_info = evt->audcal_info;
+ MM_DBG("dev_id = %d\n", audcal_info.dev_id);
+ MM_DBG("sample_rate = %d\n", audcal_info.sample_rate);
+ MM_DBG("acdb_id = %d\n", audcal_info.acdb_id);
+ MM_DBG("sessions = %d\n", audcal_info.sessions);
+ MM_DBG("acdb_state = %x\n", acdb_data.acdb_state);
+ mutex_lock(&acdb_data.acdb_mutex);
+ device_change = check_device_change(audcal_info);
+ if (!device_change) {
+ if ((audcal_info.dev_type & TX_DEVICE) == 2) {
+ if (!(acdb_data.acdb_state & AUDREC0_READY))
+ acdb_data.audrec_applied &= ~AUDREC0_READY;
+ if (!(acdb_data.acdb_state & AUDREC1_READY))
+ acdb_data.audrec_applied &= ~AUDREC1_READY;
+ if (!(acdb_data.acdb_state & AUDREC2_READY))
+ acdb_data.audrec_applied &= ~AUDREC2_READY;
+ acdb_data.acdb_state &= ~CAL_DATA_READY;
+ goto update_cache;
+ }
+ } else
+ /* state is updated to querry the modem for values */
+ acdb_data.acdb_state &= ~CAL_DATA_READY;
+
+update_cache:
+ if ((audcal_info.dev_type & TX_DEVICE) == 2) {
+ /*loop is to take care of use case:- multiple Audrec
+ sessions are routed before enabling the device in this use
+ case we will get the sessions value as bits set for all the
+ sessions routed before device enable, so we should take care
+ of copying device info to all the sessions*/
+ for (i = 0; i < MAX_AUDREC_SESSIONS; i++) {
+ stream_id = ((audcal_info.sessions >> i) & 0x01);
+ if (stream_id) {
+ acdb_cache_free_node = &acdb_cache_tx[i];
+ ret = check_device_info_already_present(
+ audcal_info,
+ acdb_cache_free_node);
+ acdb_cache_free_node->stream_id = i;
+ acdb_data.cur_tx_session = i;
+ count++;
+ }
+ }
+ if (count > 1)
+ acdb_data.multiple_sessions = 1;
+ } else {
+ acdb_cache_free_node = &acdb_cache_rx[audcal_info.dev_id];
+ ret = check_device_info_already_present(audcal_info,
+ acdb_cache_free_node);
+ if (ret == 1) {
+ MM_DBG("got device ready call back for another "
+ "audplay task sessions on same COPP\n");
+ /*stream_id is used to keep track of number of active*/
+ /*sessions active on this device*/
+ acdb_cache_free_node->stream_id++;
+ mutex_unlock(&acdb_data.acdb_mutex);
+ goto done;
+ }
+ acdb_cache_free_node->stream_id++;
+ }
+ update_acdb_data_struct(acdb_cache_free_node);
+ acdb_data.device_cb_compl = 1;
+ mutex_unlock(&acdb_data.acdb_mutex);
+ wake_up(&acdb_data.wait);
+done:
+ return;
+}
+
+
+static s32 register_device_cb(void)
+{
+ s32 result = 0;
+
+ result = auddev_register_evt_listner((AUDDEV_EVT_DEV_RDY
+ | AUDDEV_EVT_DEV_RLS),
+ AUDDEV_CLNT_AUDIOCAL, 0, device_cb, (void *)&acdb_data);
+
+ if (result) {
+ MM_ERR("ACDB=> Could not register device callback\n");
+ result = -ENODEV;
+ goto done;
+ }
+done:
+ return result;
+}
+
+static void audpp_cb(void *private, u32 id, u16 *msg)
+{
+ MM_DBG("\n");
+ if (id != AUDPP_MSG_CFG_MSG)
+ goto done;
+
+ if (msg[0] == AUDPP_MSG_ENA_DIS) {
+ acdb_data.acdb_state &= ~AUDPP_READY;
+ MM_DBG("AUDPP_MSG_ENA_DIS\n");
+ goto done;
+ }
+
+ acdb_data.acdb_state |= AUDPP_READY;
+ acdb_data.audpp_cb_compl = 1;
+ wake_up(&acdb_data.wait);
+done:
+ return;
+}
+
+static s8 handle_audpreproc_cb(void)
+{
+ struct acdb_cache_node *acdb_cached_values;
+ s8 result = 0;
+ u8 stream_id = acdb_data.preproc_stream_id;
+ acdb_data.preproc_cb_compl = 0;
+ acdb_cached_values = get_acdb_values_from_cache_tx(stream_id);
+ if (acdb_cached_values == NULL) {
+ MM_DBG("ERROR: to get chached acdb values\n");
+ return -EPERM;
+ }
+ update_acdb_data_struct(acdb_cached_values);
+ if (acdb_data.device_info->dev_id == PSEUDO_ACDB_ID) {
+ MM_INFO("audpreproc is routed to pseudo device\n");
+ return result;
+ }
+ if (session_info[stream_id].sampling_freq)
+ acdb_data.device_info->sample_rate =
+ session_info[stream_id].sampling_freq;
+ if (!(acdb_data.acdb_state & CAL_DATA_READY)) {
+ result = check_tx_acdb_values_cached();
+ if (result) {
+ result = acdb_get_calibration();
+ if (result < 0) {
+ MM_ERR("failed to get calibration data\n");
+ return result;
+ }
+ }
+ acdb_cached_values->node_status = ACDB_VALUES_FILLED;
+ }
+ return result;
+}
+
+void fluence_feature_update(int enable, int stream_id)
+{
+ MM_INFO("Fluence feature over ride with = %d\n", enable);
+ acdb_data.fleuce_feature_status[stream_id] = enable;
+}
+EXPORT_SYMBOL(fluence_feature_update);
+
+static void audpreproc_cb(void *private, u32 id, void *msg)
+{
+ struct audpreproc_cmd_enc_cfg_done_msg *tmp;
+ u8 result = 0;
+ int stream_id = 0;
+ if (id != AUDPREPROC_CMD_ENC_CFG_DONE_MSG)
+ goto done;
+
+ tmp = (struct audpreproc_cmd_enc_cfg_done_msg *)msg;
+ acdb_data.preproc_stream_id = tmp->stream_id;
+ stream_id = acdb_data.preproc_stream_id;
+ get_audrec_session_info(stream_id, &session_info[stream_id]);
+ MM_DBG("rec_enc_type = %x\n", tmp->rec_enc_type);
+ if ((tmp->rec_enc_type & 0x8000) ==
+ AUD_PREPROC_CONFIG_DISABLED) {
+ if (acdb_data.preproc_stream_id == 0) {
+ acdb_data.acdb_state &= ~AUDREC0_READY;
+ acdb_data.audrec_applied &= ~AUDREC0_READY;
+ } else if (acdb_data.preproc_stream_id == 1) {
+ acdb_data.acdb_state &= ~AUDREC1_READY;
+ acdb_data.audrec_applied &= ~AUDREC1_READY;
+ } else if (acdb_data.preproc_stream_id == 2) {
+ acdb_data.acdb_state &= ~AUDREC2_READY;
+ acdb_data.audrec_applied &= ~AUDREC2_READY;
+ }
+ acdb_data.fleuce_feature_status[stream_id] = 0;
+ acdb_cache_tx[tmp->stream_id].node_status =\
+ ACDB_VALUES_NOT_FILLED;
+ acdb_data.acdb_state &= ~CAL_DATA_READY;
+ goto done;
+ }
+ /*Following check is added to make sure that device info
+ is updated. audpre proc layer enabled without device
+ callback at this scenario we should not access
+ device information
+ */
+ if (acdb_data.device_info &&
+ session_info[stream_id].sampling_freq) {
+ acdb_data.device_info->sample_rate =
+ session_info[stream_id].sampling_freq;
+ result = check_tx_acdb_values_cached();
+ if (!result) {
+ MM_INFO("acdb values for the stream is" \
+ " querried from modem");
+ acdb_data.acdb_state |= CAL_DATA_READY;
+ } else {
+ acdb_data.acdb_state &= ~CAL_DATA_READY;
+ }
+ }
+ if (acdb_data.preproc_stream_id == 0)
+ acdb_data.acdb_state |= AUDREC0_READY;
+ else if (acdb_data.preproc_stream_id == 1)
+ acdb_data.acdb_state |= AUDREC1_READY;
+ else if (acdb_data.preproc_stream_id == 2)
+ acdb_data.acdb_state |= AUDREC2_READY;
+ acdb_data.preproc_cb_compl = 1;
+ MM_DBG("acdb_data.acdb_state = %x\n", acdb_data.acdb_state);
+ wake_up(&acdb_data.wait);
+done:
+ return;
+}
+
+static s32 register_audpp_cb(void)
+{
+ s32 result = 0;
+
+ acdb_data.audpp_cb.fn = audpp_cb;
+ acdb_data.audpp_cb.private = NULL;
+ result = audpp_register_event_callback(&acdb_data.audpp_cb);
+ if (result) {
+ MM_ERR("ACDB=> Could not register audpp callback\n");
+ result = -ENODEV;
+ goto done;
+ }
+done:
+ return result;
+}
+
+static s32 register_audpreproc_cb(void)
+{
+ s32 result = 0;
+
+ acdb_data.audpreproc_cb.fn = audpreproc_cb;
+ acdb_data.audpreproc_cb.private = NULL;
+ result = audpreproc_register_event_callback(&acdb_data.audpreproc_cb);
+ if (result) {
+ MM_ERR("ACDB=> Could not register audpreproc callback\n");
+ result = -ENODEV;
+ goto done;
+ }
+
+done:
+ return result;
+}
+
+static s32 acdb_initialize_data(void)
+{
+ s32 result = 0;
+
+ mutex_init(&acdb_data.acdb_mutex);
+
+ result = initialize_rpc();
+ if (result)
+ goto err;
+
+ result = initialize_memory();
+ if (result)
+ goto err1;
+
+ result = register_device_cb();
+ if (result)
+ goto err2;
+
+ result = register_audpp_cb();
+ if (result)
+ goto err3;
+
+ result = register_audpreproc_cb();
+ if (result)
+ goto err4;
+
+ return result;
+
+err4:
+ result = audpreproc_unregister_event_callback(&acdb_data.audpreproc_cb);
+ if (result)
+ MM_ERR("ACDB=> Could not unregister audpreproc callback\n");
+err3:
+ result = audpp_unregister_event_callback(&acdb_data.audpp_cb);
+ if (result)
+ MM_ERR("ACDB=> Could not unregister audpp callback\n");
+err2:
+ result = auddev_unregister_evt_listner(AUDDEV_CLNT_AUDIOCAL, 0);
+ if (result)
+ MM_ERR("ACDB=> Could not unregister device callback\n");
+err1:
+ daldevice_detach(acdb_data.handle);
+ acdb_data.handle = NULL;
+err:
+ return result;
+}
+
+static s32 initialize_modem_acdb(void)
+{
+ struct acdb_cmd_init_adie acdb_cmd;
+ u8 codec_type = -1;
+ s32 result = 0;
+ u8 iterations = 0;
+
+ codec_type = adie_get_detected_codec_type();
+ if (codec_type == MARIMBA_ID)
+ acdb_cmd.adie_type = ACDB_CURRENT_ADIE_MODE_MARIMBA;
+ else if (codec_type == TIMPANI_ID)
+ acdb_cmd.adie_type = ACDB_CURRENT_ADIE_MODE_TIMPANI;
+ else
+ acdb_cmd.adie_type = ACDB_CURRENT_ADIE_MODE_UNKNOWN;
+ acdb_cmd.command_id = ACDB_CMD_INITIALIZE_FOR_ADIE;
+ do {
+ /*Initialize ACDB software on modem based on codec type*/
+ result = dalrpc_fcn_8(ACDB_DalACDB_ioctl, acdb_data.handle,
+ (const void *)&acdb_cmd, sizeof(acdb_cmd),
+ &acdb_data.acdb_result,
+ sizeof(acdb_data.acdb_result));
+ if (result < 0) {
+ MM_ERR("ACDB=> RPC failure result = %d\n", result);
+ goto error;
+ }
+ /*following check is introduced to handle boot up race
+ condition between AUDCAL SW peers running on apps
+ and modem (ACDB_RES_BADSTATE indicates modem AUDCAL SW is
+ not in initialized sate) we need to retry to get ACDB
+ initialized*/
+ if (acdb_data.acdb_result.result == ACDB_RES_BADSTATE) {
+ msleep(500);
+ iterations++;
+ } else if (acdb_data.acdb_result.result == ACDB_RES_SUCCESS) {
+ MM_DBG("Modem ACDB SW initialized ((iterations = %d)\n",
+ iterations);
+ return result;
+ } else {
+ MM_ERR("ACDB=> Modem ACDB SW failed to initialize"
+ " reuslt = %d, (iterations = %d)\n",
+ acdb_data.acdb_result.result,
+ iterations);
+ goto error;
+ }
+ } while (iterations < MAX_RETRY);
+ MM_ERR("ACDB=> AUDCAL SW on modem is not in intiailized state (%d)\n",
+ acdb_data.acdb_result.result);
+error:
+ result = -EINVAL;
+ return result;
+}
+
+static s32 acdb_calibrate_device(void *data)
+{
+ s32 result = 0;
+
+ /* initialize driver */
+ result = acdb_initialize_data();
+ if (result)
+ goto done;
+
+ result = initialize_modem_acdb();
+ if (result < 0)
+ MM_ERR("failed to initialize modem ACDB\n");
+
+ while (!kthread_should_stop()) {
+ MM_DBG("Waiting for call back events\n");
+ wait_event_interruptible(acdb_data.wait,
+ (acdb_data.device_cb_compl
+ | acdb_data.audpp_cb_compl
+ | acdb_data.preproc_cb_compl));
+ mutex_lock(&acdb_data.acdb_mutex);
+ if (acdb_data.device_cb_compl) {
+ acdb_data.device_cb_compl = 0;
+ if (!(acdb_data.acdb_state & CAL_DATA_READY)) {
+ if ((acdb_data.device_info->dev_type
+ & RX_DEVICE) == 1) {
+ /*we need to get calibration values
+ only for RX device as resampler
+ moved to start of the pre - proc chain
+ tx calibration value will be based on
+ sampling frequency what audrec is
+ configured, calibration values for tx
+ device are fetch in audpreproc
+ callback*/
+ result = acdb_get_calibration();
+ if (result < 0) {
+ mutex_unlock(
+ &acdb_data.acdb_mutex);
+ MM_ERR("Not able to get "
+ "calibration "
+ "data continue\n");
+ continue;
+ }
+ }
+ }
+ MM_DBG("acdb state = %d\n",
+ acdb_data.acdb_state);
+ if ((acdb_data.device_info->dev_type & TX_DEVICE) == 2)
+ handle_tx_device_ready_callback();
+ else {
+ acdb_cache_rx[acdb_data.device_info->dev_id]\
+ .node_status =
+ ACDB_VALUES_FILLED;
+ if (acdb_data.acdb_state &
+ AUDPP_READY) {
+ MM_DBG("AUDPP already enabled "
+ "apply acdb values\n");
+ goto apply;
+ }
+ }
+ }
+
+ if (!(acdb_data.audpp_cb_compl ||
+ acdb_data.preproc_cb_compl)) {
+ MM_DBG("need to wait for either AUDPP / AUDPREPROC "
+ "Event\n");
+ mutex_unlock(&acdb_data.acdb_mutex);
+ continue;
+ } else {
+ MM_DBG("got audpp / preproc call back\n");
+ if (acdb_data.audpp_cb_compl) {
+ send_acdb_values_for_active_devices();
+ acdb_data.audpp_cb_compl = 0;
+ mutex_unlock(&acdb_data.acdb_mutex);
+ continue;
+ } else {
+ result = handle_audpreproc_cb();
+ if (result < 0) {
+ mutex_unlock(&acdb_data.acdb_mutex);
+ continue;
+ }
+ }
+ }
+apply:
+ if (acdb_data.acdb_state & CAL_DATA_READY)
+ result = acdb_send_calibration();
+
+ mutex_unlock(&acdb_data.acdb_mutex);
+ }
+done:
+ return 0;
+}
+
+static int __init acdb_init(void)
+{
+
+ s32 result = 0;
+
+ memset(&acdb_data, 0, sizeof(acdb_data));
+ spin_lock_init(&acdb_data.dsp_lock);
+ acdb_data.cb_thread_task = kthread_run(acdb_calibrate_device,
+ NULL, "acdb_cb_thread");
+
+ if (IS_ERR(acdb_data.cb_thread_task)) {
+ MM_ERR("ACDB=> Could not register cb thread\n");
+ result = -ENODEV;
+ goto err;
+ }
+#ifdef CONFIG_DEBUG_FS
+ /*This is RTC specific INIT used only with debugfs*/
+ if (!rtc_acdb_init())
+ MM_ERR("RTC ACDB=>INIT Failure\n");
+
+#endif
+ init_waitqueue_head(&acdb_data.wait);
+
+ return misc_register(&acdb_misc);
+err:
+ return result;
+}
+
+static void __exit acdb_exit(void)
+{
+ s32 result = 0;
+ u32 i = 0;
+
+ result = auddev_unregister_evt_listner(AUDDEV_CLNT_AUDIOCAL, 0);
+ if (result)
+ MM_ERR("ACDB=> Could not unregister device callback\n");
+
+ result = audpp_unregister_event_callback(&acdb_data.audpp_cb);
+ if (result)
+ MM_ERR("ACDB=> Could not unregister audpp callback\n");
+
+ result = audpreproc_unregister_event_callback(&acdb_data.\
+ audpreproc_cb);
+ if (result)
+ MM_ERR("ACDB=> Could not unregister audpreproc callback\n");
+
+ result = kthread_stop(acdb_data.cb_thread_task);
+ if (result)
+ MM_ERR("ACDB=> Could not stop kthread\n");
+
+ free_memory_acdb_get_blk();
+
+ for (i = 0; i < MAX_COPP_NODE_SUPPORTED; i++) {
+ if (i < MAX_AUDREC_SESSIONS) {
+ iounmap(acdb_cache_tx[i].virt_addr_acdb_values);
+ pmem_kfree(acdb_cache_tx[i].phys_addr_acdb_values);
+ }
+ iounmap(acdb_cache_rx[i].virt_addr_acdb_values);
+ pmem_kfree(acdb_cache_rx[i].phys_addr_acdb_values);
+ }
+ kfree(acdb_data.device_info);
+ kfree(acdb_data.pp_iir);
+ kfree(acdb_data.pp_mbadrc);
+ kfree(acdb_data.preproc_agc);
+ kfree(acdb_data.preproc_iir);
+ pmem_kfree((int32_t)acdb_data.pbe_extbuff);
+ iounmap(acdb_data.fluence_extbuff_virt);
+ pmem_kfree((int32_t)acdb_data.fluence_extbuff);
+ mutex_destroy(&acdb_data.acdb_mutex);
+ memset(&acdb_data, 0, sizeof(acdb_data));
+ #ifdef CONFIG_DEBUG_FS
+ rtc_acdb_deinit();
+ #endif
+}
+
+late_initcall(acdb_init);
+module_exit(acdb_exit);
+
+MODULE_DESCRIPTION("MSM 7x30 Audio ACDB driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_adpcm.c b/arch/arm/mach-msm/qdsp5v2/audio_adpcm.c
new file mode 100644
index 0000000..8d767c9
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_adpcm.c
@@ -0,0 +1,1741 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Based on the mp3 native driver in arch/arm/mach-msm/qdsp5v2/audio_mp3.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ *
+ * All source code in this file is licensed under the following license except
+ * where indicated.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/earlysuspend.h>
+#include <linux/android_pmem.h>
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+#include <linux/slab.h>
+#include <linux/msm_audio.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/qdsp5audplaycmdi.h>
+#include <mach/qdsp5v2/qdsp5audplaymsg.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/qdsp5v2/audpp.h>
+#include <mach/debug_mm.h>
+
+/* Size must be power of 2 */
+#define BUFSZ_MAX 32768 /* Includes meta in size */
+#define BUFSZ_MIN 4096 /* Includes meta in size */
+#define DMASZ_MAX (BUFSZ_MAX * 2)
+#define DMASZ_MIN (BUFSZ_MIN * 2)
+
+#define AUDPLAY_INVALID_READ_PTR_OFFSET 0xFFFF
+#define AUDDEC_DEC_ADPCM 1
+
+#define PCM_BUFSZ_MIN 8216 /* Hold one stereo ADPCM frame and meta out*/
+#define PCM_BUF_MAX_COUNT 5 /* DSP only accepts 5 buffers at most
+ but support 2 buffers currently */
+#define ROUTING_MODE_FTRT 1
+#define ROUTING_MODE_RT 2
+/* Decoder status received from AUDPPTASK */
+#define AUDPP_DEC_STATUS_SLEEP 0
+#define AUDPP_DEC_STATUS_INIT 1
+#define AUDPP_DEC_STATUS_CFG 2
+#define AUDPP_DEC_STATUS_PLAY 3
+
+#define AUDADPCM_METAFIELD_MASK 0xFFFF0000
+#define AUDADPCM_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */
+#define AUDADPCM_EOS_FLG_MASK 0x01
+#define AUDADPCM_EOS_NONE 0x0 /* No EOS detected */
+#define AUDADPCM_EOS_SET 0x1 /* EOS set in meta field */
+
+#define AUDADPCM_EVENT_NUM 10 /* Default no. of pre-allocated event packets */
+
+struct buffer {
+ void *data;
+ unsigned size;
+ unsigned used; /* Input usage actual DSP produced PCM size */
+ unsigned addr;
+ unsigned short mfield_sz; /*only useful for data has meta field */
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+struct audadpcm_suspend_ctl {
+ struct early_suspend node;
+ struct audio *audio;
+};
+#endif
+
+struct audadpcm_event{
+ struct list_head list;
+ int event_type;
+ union msm_audio_event_payload payload;
+};
+
+struct audio {
+ struct buffer out[2];
+
+ spinlock_t dsp_lock;
+
+ uint8_t out_head;
+ uint8_t out_tail;
+ uint8_t out_needed; /* number of buffers the dsp is waiting for */
+ unsigned out_dma_sz;
+
+ atomic_t out_bytes;
+
+ struct mutex lock;
+ struct mutex write_lock;
+ wait_queue_head_t write_wait;
+
+ /* Host PCM section */
+ struct buffer in[PCM_BUF_MAX_COUNT];
+ struct mutex read_lock;
+ wait_queue_head_t read_wait; /* Wait queue for read */
+ char *read_data; /* pointer to reader buffer */
+ int32_t read_phys; /* physical address of reader buffer */
+ uint8_t read_next; /* index to input buffers to be read next */
+ uint8_t fill_next; /* index to buffer that DSP should be filling */
+ uint8_t pcm_buf_count; /* number of pcm buffer allocated */
+ /* ---- End of Host PCM section */
+
+ struct msm_adsp_module *audplay;
+
+ /* configuration to use on next enable */
+ uint32_t out_sample_rate;
+ uint32_t out_channel_mode;
+ uint32_t out_block_size;
+
+ /* data allocated for various buffers */
+ char *data;
+ int32_t phys; /* physical address of write buffer */
+
+ int mfield; /* meta field embedded in data */
+ int rflush; /* Read flush */
+ int wflush; /* Write flush */
+ int opened;
+ int enabled;
+ int running;
+ int stopped; /* set when stopped, cleared on flush */
+ int pcm_feedback;
+ int buf_refresh;
+ int teos; /* valid only if tunnel mode & no data left for decoder */
+ enum msm_aud_decoder_state dec_state; /* Represents decoder state */
+ int reserved; /* A byte is being reserved */
+ char rsv_byte; /* Handle odd length user data */
+
+ const char *module_name;
+ unsigned queue_id;
+ uint16_t dec_id;
+ uint32_t read_ptr_offset;
+ int16_t source;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct audadpcm_suspend_ctl suspend_ctl;
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dentry;
+#endif
+
+ wait_queue_head_t wait;
+ struct list_head free_event_queue;
+ struct list_head event_queue;
+ wait_queue_head_t event_wait;
+ spinlock_t event_queue_lock;
+ struct mutex get_event_lock;
+ int event_abort;
+ /* AV sync Info */
+ int avsync_flag; /* Flag to indicate feedback from DSP */
+ wait_queue_head_t avsync_wait;/* Wait queue for AV Sync Message */
+ /* flags, 48 bits sample/bytes counter per channel */
+ uint16_t avsync[AUDPP_AVSYNC_CH_COUNT * AUDPP_AVSYNC_NUM_WORDS + 1];
+ uint32_t device_events;
+
+ int eq_enable;
+ int eq_needs_commit;
+ struct audpp_cmd_cfg_object_params_eqalizer eq;
+ struct audpp_cmd_cfg_object_params_volume vol_pan;
+};
+
+static int auddec_dsp_config(struct audio *audio, int enable);
+static void audpp_cmd_cfg_adec_params(struct audio *audio);
+static void audpp_cmd_cfg_routing_mode(struct audio *audio);
+static void audplay_send_data(struct audio *audio, unsigned needed);
+static void audplay_config_hostpcm(struct audio *audio);
+static void audplay_buffer_refresh(struct audio *audio);
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg);
+static void audadpcm_post_event(struct audio *audio, int type,
+ union msm_audio_event_payload payload);
+
+/* must be called with audio->lock held */
+static int audio_enable(struct audio *audio)
+{
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ if (audio->enabled)
+ return 0;
+
+ audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+ audio->out_tail = 0;
+ audio->out_needed = 0;
+
+ if (msm_adsp_enable(audio->audplay)) {
+ MM_ERR("msm_adsp_enable(audplay) failed\n");
+ return -ENODEV;
+ }
+
+ if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) {
+ MM_ERR("audpp_enable() failed\n");
+ msm_adsp_disable(audio->audplay);
+ return -ENODEV;
+ }
+
+ audio->enabled = 1;
+ return 0;
+}
+
+static void adpcm_listner(u32 evt_id, union auddev_evt_data *evt_payload,
+ void *private_data)
+{
+ struct audio *audio = (struct audio *) private_data;
+ switch (evt_id) {
+ case AUDDEV_EVT_DEV_RDY:
+ MM_DBG(":AUDDEV_EVT_DEV_RDY\n");
+ audio->source |= (0x1 << evt_payload->routing_id);
+ if (audio->running == 1 && audio->enabled == 1)
+ audpp_route_stream(audio->dec_id, audio->source);
+ break;
+ case AUDDEV_EVT_DEV_RLS:
+ MM_DBG(":AUDDEV_EVT_DEV_RLS\n");
+ if (audio->dec_state == MSM_AUD_DECODER_STATE_SUCCESS &&
+ audio->enabled == 1)
+ audpp_route_stream(audio->dec_id,
+ msm_snddev_route_dec(audio->dec_id));
+ break;
+ case AUDDEV_EVT_STREAM_VOL_CHG:
+ audio->vol_pan.volume = evt_payload->session_vol;
+ MM_DBG(":AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n",
+ audio->vol_pan.volume);
+ if (audio->running)
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ break;
+ default:
+ MM_ERR(":ERROR:wrong event\n");
+ break;
+ }
+}
+/* must be called with audio->lock held */
+static int audio_disable(struct audio *audio)
+{
+ int rc = 0;
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ if (audio->enabled) {
+ audio->enabled = 0;
+ audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+ auddec_dsp_config(audio, 0);
+ rc = wait_event_interruptible_timeout(audio->wait,
+ audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+ msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE)
+ rc = -EFAULT;
+ else
+ rc = 0;
+ wake_up(&audio->write_wait);
+ wake_up(&audio->read_wait);
+ msm_adsp_disable(audio->audplay);
+ audpp_disable(audio->dec_id, audio);
+ audio->out_needed = 0;
+ }
+ return rc;
+}
+
+/* ------------------- dsp --------------------- */
+static void audio_update_pcm_buf_entry(struct audio *audio,
+ uint32_t *payload)
+{
+ uint8_t index;
+ unsigned long flags;
+
+ if (audio->rflush)
+ return;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ for (index = 0; index < payload[1]; index++) {
+ if (audio->in[audio->fill_next].addr ==
+ payload[2 + index * 2]) {
+ MM_DBG("audio_update_pcm_buf_entry: \
+ in[%d] ready\n", audio->fill_next);
+ audio->in[audio->fill_next].used =
+ payload[3 + index * 2];
+ if ((++audio->fill_next) == audio->pcm_buf_count)
+ audio->fill_next = 0;
+ } else {
+ MM_ERR("audio_update_pcm_buf_entry: \
+ expected=%x ret=%x\n",
+ audio->in[audio->fill_next].addr,
+ payload[1 + index * 2]);
+ break;
+ }
+ }
+ if (audio->in[audio->fill_next].used == 0) {
+ audplay_buffer_refresh(audio);
+ } else {
+ MM_DBG("read cannot keep up\n");
+ audio->buf_refresh = 1;
+ }
+ wake_up(&audio->read_wait);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+static void audplay_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent) (void *ptr, size_t len))
+{
+ struct audio *audio = data;
+ uint32_t msg[28];
+
+ getevent(msg, sizeof(msg));
+
+ MM_DBG("msg_id=%x\n", id);
+
+ switch (id) {
+ case AUDPLAY_MSG_DEC_NEEDS_DATA:
+ audplay_send_data(audio, 1);
+ break;
+
+ case AUDPLAY_MSG_BUFFER_UPDATE:
+ audio_update_pcm_buf_entry(audio, msg);
+ break;
+
+ case ADSP_MESSAGE_ID:
+ MM_DBG("Received ADSP event: module enable(audplaytask)\n");
+ break;
+
+ default:
+ MM_ERR("unexpected message from decoder \n");
+ break;
+ }
+}
+
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg)
+{
+ struct audio *audio = private;
+
+ switch (id) {
+ case AUDPP_MSG_STATUS_MSG:{
+ unsigned status = msg[1];
+
+ switch (status) {
+ case AUDPP_DEC_STATUS_SLEEP: {
+ uint16_t reason = msg[2];
+ MM_DBG("decoder status:sleep reason = \
+ 0x%04x\n", reason);
+ if ((reason == AUDPP_MSG_REASON_MEM)
+ || (reason ==
+ AUDPP_MSG_REASON_NODECODER)) {
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_FAILURE;
+ wake_up(&audio->wait);
+ } else if (reason == AUDPP_MSG_REASON_NONE) {
+ /* decoder is in disable state */
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_CLOSE;
+ wake_up(&audio->wait);
+ }
+ break;
+ }
+ case AUDPP_DEC_STATUS_INIT:
+ MM_DBG("decoder status: init\n");
+ if (audio->pcm_feedback)
+ audpp_cmd_cfg_routing_mode(audio);
+ else
+ audpp_cmd_cfg_adec_params(audio);
+ break;
+
+ case AUDPP_DEC_STATUS_CFG:
+ MM_DBG("decoder status: cfg\n");
+ break;
+ case AUDPP_DEC_STATUS_PLAY:
+ MM_DBG("decoder status: play \n");
+ /* send mixer command */
+ audpp_route_stream(audio->dec_id,
+ audio->source);
+ if (audio->pcm_feedback) {
+ audplay_config_hostpcm(audio);
+ audplay_buffer_refresh(audio);
+ }
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_SUCCESS;
+ wake_up(&audio->wait);
+ break;
+ default:
+ MM_ERR("unknown decoder status\n");
+ }
+ break;
+ }
+ case AUDPP_MSG_CFG_MSG:
+ if (msg[0] == AUDPP_MSG_ENA_ENA) {
+ MM_DBG("CFG_MSG ENABLE\n");
+ auddec_dsp_config(audio, 1);
+ audio->out_needed = 0;
+ audio->running = 1;
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ audpp_dsp_set_eq(audio->dec_id, audio->eq_enable,
+ &audio->eq, POPP);
+ } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+ MM_DBG("CFG_MSG DISABLE\n");
+ audio->running = 0;
+ } else {
+ MM_DBG("CFG_MSG %d?\n", msg[0]);
+ }
+ break;
+ case AUDPP_MSG_ROUTING_ACK:
+ MM_DBG("ROUTING_ACK mode=%d\n", msg[1]);
+ audpp_cmd_cfg_adec_params(audio);
+ break;
+
+ case AUDPP_MSG_FLUSH_ACK:
+ MM_DBG("FLUSH_ACK\n");
+ audio->wflush = 0;
+ audio->rflush = 0;
+ wake_up(&audio->write_wait);
+ if (audio->pcm_feedback)
+ audplay_buffer_refresh(audio);
+ break;
+
+ case AUDPP_MSG_PCMDMAMISSED:
+ MM_DBG("PCMDMAMISSED\n");
+ audio->teos = 1;
+ wake_up(&audio->write_wait);
+ break;
+
+ case AUDPP_MSG_AVSYNC_MSG:
+ MM_DBG("AUDPP_MSG_AVSYNC_MSG\n");
+ memcpy(&audio->avsync[0], msg, sizeof(audio->avsync));
+ audio->avsync_flag = 1;
+ wake_up(&audio->avsync_wait);
+ break;
+
+ default:
+ MM_ERR("UNKNOWN (%d)\n", id);
+ }
+
+}
+
+static struct msm_adsp_ops audplay_adsp_ops_adpcm = {
+ .event = audplay_dsp_event,
+};
+
+#define audplay_send_queue0(audio, cmd, len) \
+ msm_adsp_write(audio->audplay, audio->queue_id, \
+ cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+ struct audpp_cmd_cfg_dec_type cfg_dec_cmd;
+
+ memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd));
+
+ cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+ if (enable)
+ cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_ADPCM;
+ else
+ cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_DIS_DEC_V;
+ cfg_dec_cmd.dm_mode = 0x0;
+ cfg_dec_cmd.stream_id = audio->dec_id;
+ return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd));
+}
+
+static void audpp_cmd_cfg_adec_params(struct audio *audio)
+{
+ struct audpp_cmd_cfg_adec_params_adpcm cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+ cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_ADPCM_LEN;
+ cmd.common.dec_id = audio->dec_id;
+ cmd.common.input_sampling_frequency = audio->out_sample_rate;
+
+ cmd.stereo_cfg = audio->out_channel_mode;
+ cmd.block_size = audio->out_block_size;
+
+ audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_routing_mode(struct audio *audio)
+{
+ struct audpp_cmd_routing_mode cmd;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_ROUTING_MODE;
+ cmd.object_number = audio->dec_id;
+ if (audio->pcm_feedback)
+ cmd.routing_mode = ROUTING_MODE_FTRT;
+ else
+ cmd.routing_mode = ROUTING_MODE_RT;
+
+ audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static void audplay_buffer_refresh(struct audio *audio)
+{
+ struct audplay_cmd_buffer_refresh refresh_cmd;
+
+ refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH;
+ refresh_cmd.num_buffers = 1;
+ refresh_cmd.buf0_address = audio->in[audio->fill_next].addr;
+ refresh_cmd.buf0_length = audio->in[audio->fill_next].size;
+ refresh_cmd.buf_read_count = 0;
+
+ MM_DBG("buf0_addr=%x buf0_len=%d\n",
+ refresh_cmd.buf0_address,
+ refresh_cmd.buf0_length);
+
+ (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd));
+}
+
+static void audplay_config_hostpcm(struct audio *audio)
+{
+ struct audplay_cmd_hpcm_buf_cfg cfg_cmd;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG;
+ cfg_cmd.max_buffers = audio->pcm_buf_count;
+ cfg_cmd.byte_swap = 0;
+ cfg_cmd.hostpcm_config = (0x8000) | (0x4000);
+ cfg_cmd.feedback_frequency = 1;
+ cfg_cmd.partition_number = 0;
+
+ (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd));
+}
+
+
+static int audplay_dsp_send_data_avail(struct audio *audio,
+ unsigned idx, unsigned len)
+{
+ struct audplay_cmd_bitstream_data_avail_nt2 cmd;
+
+ cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2;
+ if (audio->mfield)
+ cmd.decoder_id = AUDADPCM_METAFIELD_MASK |
+ (audio->out[idx].mfield_sz >> 1);
+ else
+ cmd.decoder_id = audio->dec_id;
+ cmd.buf_ptr = audio->out[idx].addr;
+ cmd.buf_size = len/2;
+ cmd.partition_number = 0;
+ return audplay_send_queue0(audio, &cmd, sizeof(cmd));
+}
+
+static void audplay_send_data(struct audio *audio, unsigned needed)
+{
+ struct buffer *frame;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (!audio->running)
+ goto done;
+
+ if (audio->wflush) {
+ audio->out_needed = 1;
+ goto done;
+ }
+
+ if (needed && !audio->wflush) {
+ /* We were called from the callback because the DSP
+ * requested more data. Note that the DSP does want
+ * more data, and if a buffer was in-flight, mark it
+ * as available (since the DSP must now be done with
+ * it).
+ */
+ audio->out_needed = 1;
+ frame = audio->out + audio->out_tail;
+ if (frame->used == 0xffffffff) {
+ MM_DBG("frame %d free\n", audio->out_tail);
+ frame->used = 0;
+ audio->out_tail ^= 1;
+ wake_up(&audio->write_wait);
+ }
+ }
+
+ if (audio->out_needed) {
+ /* If the DSP currently wants data and we have a
+ * buffer available, we will send it and reset
+ * the needed flag. We'll mark the buffer as in-flight
+ * so that it won't be recycled until the next buffer
+ * is requested
+ */
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ frame = audio->out + audio->out_tail;
+ if (frame->used) {
+ BUG_ON(frame->used == 0xffffffff);
+ MM_DBG("frame %d busy\n", audio->out_tail);
+ audplay_dsp_send_data_avail(audio, audio->out_tail,
+ frame->used);
+ frame->used = 0xffffffff;
+ audio->out_needed = 0;
+ }
+ }
+done:
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+/* ------------------- device --------------------- */
+
+static void audio_flush(struct audio *audio)
+{
+ audio->out[0].used = 0;
+ audio->out[1].used = 0;
+ audio->out_head = 0;
+ audio->out_tail = 0;
+ audio->reserved = 0;
+ atomic_set(&audio->out_bytes, 0);
+}
+
+static void audio_flush_pcm_buf(struct audio *audio)
+{
+ uint8_t index;
+
+ for (index = 0; index < PCM_BUF_MAX_COUNT; index++)
+ audio->in[index].used = 0;
+ audio->buf_refresh = 0;
+ audio->read_next = 0;
+ audio->fill_next = 0;
+}
+
+static void audio_ioport_reset(struct audio *audio)
+{
+ /* Make sure read/write thread are free from
+ * sleep and knowing that system is not able
+ * to process io request at the moment
+ */
+ wake_up(&audio->write_wait);
+ mutex_lock(&audio->write_lock);
+ audio_flush(audio);
+ mutex_unlock(&audio->write_lock);
+ wake_up(&audio->read_wait);
+ mutex_lock(&audio->read_lock);
+ audio_flush_pcm_buf(audio);
+ mutex_unlock(&audio->read_lock);
+ audio->avsync_flag = 1;
+ wake_up(&audio->avsync_wait);
+}
+
+static int audadpcm_events_pending(struct audio *audio)
+{
+ unsigned long flags;
+ int empty;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ empty = !list_empty(&audio->event_queue);
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+ return empty || audio->event_abort;
+}
+
+static void audadpcm_reset_event_queue(struct audio *audio)
+{
+ unsigned long flags;
+ struct audadpcm_event *drv_evt;
+ struct list_head *ptr, *next;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ list_for_each_safe(ptr, next, &audio->event_queue) {
+ drv_evt = list_first_entry(&audio->event_queue,
+ struct audadpcm_event, list);
+ list_del(&drv_evt->list);
+ kfree(drv_evt);
+ }
+ list_for_each_safe(ptr, next, &audio->free_event_queue) {
+ drv_evt = list_first_entry(&audio->free_event_queue,
+ struct audadpcm_event, list);
+ list_del(&drv_evt->list);
+ kfree(drv_evt);
+ }
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+ return;
+}
+
+static long audadpcm_process_event_req(struct audio *audio, void __user *arg)
+{
+ long rc;
+ struct msm_audio_event usr_evt;
+ struct audadpcm_event *drv_evt = NULL;
+ int timeout;
+ unsigned long flags;
+
+ if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event)))
+ return -EFAULT;
+
+ timeout = (int) usr_evt.timeout_ms;
+
+ if (timeout > 0) {
+ rc = wait_event_interruptible_timeout(
+ audio->event_wait, audadpcm_events_pending(audio),
+ msecs_to_jiffies(timeout));
+ if (rc == 0)
+ return -ETIMEDOUT;
+ } else {
+ rc = wait_event_interruptible(
+ audio->event_wait, audadpcm_events_pending(audio));
+ }
+
+ if (rc < 0)
+ return rc;
+
+ if (audio->event_abort) {
+ audio->event_abort = 0;
+ return -ENODEV;
+ }
+
+ rc = 0;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ if (!list_empty(&audio->event_queue)) {
+ drv_evt = list_first_entry(&audio->event_queue,
+ struct audadpcm_event, list);
+ list_del(&drv_evt->list);
+ }
+
+ if (drv_evt) {
+ usr_evt.event_type = drv_evt->event_type;
+ usr_evt.event_payload = drv_evt->payload;
+ list_add_tail(&drv_evt->list, &audio->free_event_queue);
+ } else
+ rc = -1;
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+ if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt)))
+ rc = -EFAULT;
+
+ return rc;
+}
+
+static int audio_enable_eq(struct audio *audio, int enable)
+{
+ if (audio->eq_enable == enable && !audio->eq_needs_commit)
+ return 0;
+
+ audio->eq_enable = enable;
+
+ if (audio->running) {
+ audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq, POPP);
+ audio->eq_needs_commit = 0;
+ }
+ return 0;
+}
+
+static int audio_get_avsync_data(struct audio *audio,
+ struct msm_audio_stats *stats)
+{
+ int rc = -EINVAL;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) {
+ /* av_sync sample count */
+ stats->sample_count = (audio->avsync[2] << 16) |
+ (audio->avsync[3]);
+
+ /* av_sync byte_count */
+ stats->byte_count = (audio->avsync[5] << 16) |
+ (audio->avsync[6]);
+
+ audio->avsync_flag = 0;
+ rc = 0;
+ }
+ local_irq_restore(flags);
+ return rc;
+
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct audio *audio = file->private_data;
+ int rc = -EINVAL;
+ unsigned long flags = 0;
+ uint16_t enable_mask;
+ int enable;
+ int prev_state;
+
+ MM_DBG("cmd = %d\n", cmd);
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+
+ audio->avsync_flag = 0;
+ memset(&stats, 0, sizeof(stats));
+ if (audpp_query_avsync(audio->dec_id) < 0)
+ return rc;
+
+ rc = wait_event_interruptible_timeout(audio->avsync_wait,
+ (audio->avsync_flag == 1),
+ msecs_to_jiffies(AUDPP_AVSYNC_EVENT_TIMEOUT));
+
+ if (rc < 0)
+ return rc;
+ else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) {
+ if (audio_get_avsync_data(audio, &stats) < 0)
+ return rc;
+
+ if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ } else
+ return -EAGAIN;
+ }
+
+ switch (cmd) {
+ case AUDIO_ENABLE_AUDPP:
+ if (copy_from_user(&enable_mask, (void *) arg,
+ sizeof(enable_mask))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ enable = (enable_mask & EQ_ENABLE) ? 1 : 0;
+ audio_enable_eq(audio, enable);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ rc = 0;
+ break;
+ case AUDIO_SET_VOLUME:
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->vol_pan.volume = arg;
+ if (audio->running)
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ rc = 0;
+ break;
+
+ case AUDIO_SET_PAN:
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->vol_pan.pan = arg;
+ if (audio->running)
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ rc = 0;
+ break;
+
+ case AUDIO_SET_EQ:
+ prev_state = audio->eq_enable;
+ audio->eq_enable = 0;
+ if (copy_from_user(&audio->eq.num_bands, (void *) arg,
+ sizeof(audio->eq) -
+ (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) {
+ rc = -EFAULT;
+ break;
+ }
+ audio->eq_enable = prev_state;
+ audio->eq_needs_commit = 1;
+ rc = 0;
+ break;
+ }
+
+ if (-EINVAL != rc)
+ return rc;
+
+ if (cmd == AUDIO_GET_EVENT) {
+ MM_DBG("AUDIO_GET_EVENT\n");
+ if (mutex_trylock(&audio->get_event_lock)) {
+ rc = audadpcm_process_event_req(audio,
+ (void __user *) arg);
+ mutex_unlock(&audio->get_event_lock);
+ } else
+ rc = -EBUSY;
+ return rc;
+ }
+
+ if (cmd == AUDIO_ABORT_GET_EVENT) {
+ audio->event_abort = 1;
+ wake_up(&audio->event_wait);
+ return 0;
+ }
+
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START:
+ MM_DBG("AUDIO_START\n");
+ rc = audio_enable(audio);
+ if (!rc) {
+ rc = wait_event_interruptible_timeout(audio->wait,
+ audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+ msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+ MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc);
+
+ if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS)
+ rc = -ENODEV;
+ else
+ rc = 0;
+ }
+ break;
+ case AUDIO_STOP:
+ MM_DBG("AUDIO_STOP\n");
+ rc = audio_disable(audio);
+ audio->stopped = 1;
+ audio_ioport_reset(audio);
+ audio->stopped = 0;
+ break;
+ case AUDIO_FLUSH:
+ MM_DBG("AUDIO_FLUSH\n");
+ audio->rflush = 1;
+ audio->wflush = 1;
+ audio_ioport_reset(audio);
+ if (audio->running) {
+ audpp_flush(audio->dec_id);
+ rc = wait_event_interruptible(audio->write_wait,
+ !audio->wflush);
+ if (rc < 0) {
+ MM_ERR("AUDIO_FLUSH interrupted\n");
+ rc = -EINTR;
+ }
+ } else {
+ audio->rflush = 0;
+ audio->wflush = 0;
+ }
+ break;
+ case AUDIO_SET_CONFIG: {
+ struct msm_audio_config config;
+ if (copy_from_user(&config, (void *) arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (config.channel_count == 1) {
+ config.channel_count = AUDPP_CMD_PCM_INTF_MONO_V;
+ } else if (config.channel_count == 2) {
+ config.channel_count = AUDPP_CMD_PCM_INTF_STEREO_V;
+ } else {
+ rc = -EINVAL;
+ break;
+ }
+ audio->mfield = config.meta_field;
+ audio->out_sample_rate = config.sample_rate;
+ audio->out_channel_mode = config.channel_count;
+ audio->out_block_size = config.bits;
+ rc = 0;
+ break;
+ }
+ case AUDIO_GET_CONFIG: {
+ struct msm_audio_config config;
+ config.buffer_size = (audio->out_dma_sz >> 1);
+ config.buffer_count = 2;
+ config.sample_rate = audio->out_sample_rate;
+ if (audio->out_channel_mode == AUDPP_CMD_PCM_INTF_MONO_V)
+ config.channel_count = 1;
+ else
+ config.channel_count = 2;
+ config.meta_field = 0;
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+ config.unused[2] = 0;
+ if (copy_to_user((void *) arg, &config, sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+
+ break;
+ }
+ case AUDIO_GET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+ config.pcm_feedback = audio->pcm_feedback;
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+ config.buffer_size = PCM_BUFSZ_MIN;
+ if (copy_to_user((void *)arg, &config,
+ sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ case AUDIO_SET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+ if (copy_from_user
+ (&config, (void *)arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (config.pcm_feedback != audio->pcm_feedback) {
+ MM_ERR("Not sufficient permission to"
+ "change the playback mode\n");
+ rc = -EACCES;
+ break;
+ }
+ if ((config.buffer_count > PCM_BUF_MAX_COUNT) ||
+ (config.buffer_count == 1))
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+
+ if (config.buffer_size < PCM_BUFSZ_MIN)
+ config.buffer_size = PCM_BUFSZ_MIN;
+
+ /* Check if pcm feedback is required */
+ if ((config.pcm_feedback) && (!audio->read_data)) {
+ MM_DBG("allocate PCM buffer %d\n",
+ config.buffer_count *
+ config.buffer_size);
+ audio->read_phys = pmem_kalloc(
+ config.buffer_size *
+ config.buffer_count,
+ PMEM_MEMTYPE_EBI1|
+ PMEM_ALIGNMENT_4K);
+ if (IS_ERR((void *)audio->read_phys)) {
+ rc = -ENOMEM;
+ break;
+ }
+ audio->read_data = ioremap(audio->read_phys,
+ config.buffer_size *
+ config.buffer_count);
+ if (!audio->read_data) {
+ MM_ERR("read buf alloc fail\n");
+ rc = -ENOMEM;
+ pmem_kfree(audio->read_phys);
+ } else {
+ uint8_t index;
+ uint32_t offset = 0;
+ audio->buf_refresh = 0;
+ audio->pcm_buf_count =
+ config.buffer_count;
+ audio->read_next = 0;
+ audio->fill_next = 0;
+
+ for (index = 0;
+ index < config.buffer_count;
+ index++) {
+ audio->in[index].data =
+ audio->read_data + offset;
+ audio->in[index].addr =
+ audio->read_phys + offset;
+ audio->in[index].size =
+ config.buffer_size;
+ audio->in[index].used = 0;
+ offset += config.buffer_size;
+ }
+ MM_DBG("read buf: phy addr \
+ 0x%08x kernel addr 0x%08x\n",
+ audio->read_phys,
+ (int)audio->read_data);
+ rc = 0;
+ }
+ } else {
+ rc = 0;
+ }
+ break;
+ }
+ case AUDIO_PAUSE:
+ MM_DBG("AUDIO_PAUSE %ld\n", arg);
+ rc = audpp_pause(audio->dec_id, (int) arg);
+ break;
+ case AUDIO_GET_SESSION_ID:
+ if (copy_to_user((void *) arg, &audio->dec_id,
+ sizeof(unsigned short)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+/* Only useful in tunnel-mode */
+static int audio_fsync(struct file *file, int datasync)
+{
+ struct audio *audio = file->private_data;
+ struct buffer *frame;
+ int rc = 0;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+
+ if (!audio->running || audio->pcm_feedback) {
+ rc = -EINVAL;
+ goto done_nolock;
+ }
+
+ mutex_lock(&audio->write_lock);
+
+ rc = wait_event_interruptible(audio->write_wait,
+ (!audio->out[0].used &&
+ !audio->out[1].used &&
+ audio->out_needed) || audio->wflush);
+
+ if (rc < 0)
+ goto done;
+ else if (audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+
+ if (audio->reserved) {
+ MM_DBG("send reserved byte\n");
+ frame = audio->out + audio->out_tail;
+ ((char *) frame->data)[0] = audio->rsv_byte;
+ ((char *) frame->data)[1] = 0;
+ frame->used = 2;
+ audplay_send_data(audio, 0);
+
+ rc = wait_event_interruptible(audio->write_wait,
+ (!audio->out[0].used &&
+ !audio->out[1].used &&
+ audio->out_needed) || audio->wflush);
+
+ if (rc < 0)
+ goto done;
+ else if (audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+ }
+
+ /* pcm dmamiss message is sent continously
+ * when decoder is starved so no race
+ * condition concern
+ */
+ audio->teos = 0;
+
+ rc = wait_event_interruptible(audio->write_wait,
+ audio->teos || audio->wflush);
+
+ if (audio->wflush)
+ rc = -EBUSY;
+
+done:
+ mutex_unlock(&audio->write_lock);
+done_nolock:
+ return rc;
+}
+
+static ssize_t audio_read(struct file *file, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ int rc = 0;
+
+ if (!audio->pcm_feedback)
+ return 0; /* PCM feedback is not enabled. Nothing to read */
+
+ mutex_lock(&audio->read_lock);
+ MM_DBG("%d \n", count);
+ while (count > 0) {
+ rc = wait_event_interruptible(audio->read_wait,
+ (audio->in[audio->read_next].used > 0) ||
+ (audio->stopped) || (audio->rflush));
+
+ if (rc < 0)
+ break;
+
+ if (audio->stopped || audio->rflush) {
+ rc = -EBUSY;
+ break;
+ }
+
+ if (count < audio->in[audio->read_next].used) {
+ /* Read must happen in frame boundary. Since driver
+ does not know frame size, read count must be greater
+ or equal to size of PCM samples */
+ MM_DBG("audio_read: no partial frame done reading\n");
+ break;
+ } else {
+ MM_DBG("audio_read: read from in[%d]\n",
+ audio->read_next);
+ if (copy_to_user
+ (buf, audio->in[audio->read_next].data,
+ audio->in[audio->read_next].used)) {
+ MM_ERR("invalid addr %x \n", (unsigned int)buf);
+ rc = -EFAULT;
+ break;
+ }
+ count -= audio->in[audio->read_next].used;
+ buf += audio->in[audio->read_next].used;
+ audio->in[audio->read_next].used = 0;
+ if ((++audio->read_next) == audio->pcm_buf_count)
+ audio->read_next = 0;
+ break; /* Force to exit while loop
+ * to prevent output thread
+ * sleep too long if data is
+ * not ready at this moment.
+ */
+ }
+ }
+
+ /* don't feed output buffer to HW decoder during flushing
+ * buffer refresh command will be sent once flush completes
+ * send buf refresh command here can confuse HW decoder
+ */
+ if (audio->buf_refresh && !audio->rflush) {
+ audio->buf_refresh = 0;
+ MM_DBG("kick start pcm feedback again\n");
+ audplay_buffer_refresh(audio);
+ }
+
+ mutex_unlock(&audio->read_lock);
+
+ if (buf > start)
+ rc = buf - start;
+
+ MM_DBG("read %d bytes\n", rc);
+ return rc;
+}
+
+static int audadpcm_process_eos(struct audio *audio,
+ const char __user *buf_start, unsigned short mfield_size)
+{
+ int rc = 0;
+ struct buffer *frame;
+ char *buf_ptr;
+
+ if (audio->reserved) {
+ MM_DBG("flush reserve byte\n");
+ frame = audio->out + audio->out_head;
+ buf_ptr = frame->data;
+ rc = wait_event_interruptible(audio->write_wait,
+ (frame->used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+ if (rc < 0)
+ goto done;
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+
+ buf_ptr[0] = audio->rsv_byte;
+ buf_ptr[1] = 0;
+ audio->out_head ^= 1;
+ frame->mfield_sz = 0;
+ frame->used = 2;
+ audio->reserved = 0;
+ audplay_send_data(audio, 0);
+ }
+
+ frame = audio->out + audio->out_head;
+
+ rc = wait_event_interruptible(audio->write_wait,
+ (audio->out_needed &&
+ audio->out[0].used == 0 &&
+ audio->out[1].used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+
+ if (rc < 0)
+ goto done;
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+
+ if (copy_from_user(frame->data, buf_start, mfield_size)) {
+ rc = -EFAULT;
+ goto done;
+ }
+
+ frame->mfield_sz = mfield_size;
+ audio->out_head ^= 1;
+ frame->used = mfield_size;
+ audplay_send_data(audio, 0);
+done:
+ return rc;
+}
+
+static ssize_t audio_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ struct buffer *frame;
+ size_t xfer;
+ char *cpy_ptr;
+ int rc = 0, eos_condition = AUDADPCM_EOS_NONE;
+ unsigned dsize;
+ unsigned short mfield_size = 0;
+
+ MM_DBG("cnt=%d\n", count);
+
+ mutex_lock(&audio->write_lock);
+ while (count > 0) {
+ frame = audio->out + audio->out_head;
+ cpy_ptr = frame->data;
+ dsize = 0;
+ rc = wait_event_interruptible(audio->write_wait,
+ (frame->used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+ if (rc < 0)
+ break;
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ break;
+ }
+ if (audio->mfield) {
+ if (buf == start) {
+ /* Processing beginning of user buffer */
+ if (__get_user(mfield_size,
+ (unsigned short __user *) buf)) {
+ rc = -EFAULT;
+ break;
+ } else if (mfield_size > count) {
+ rc = -EINVAL;
+ break;
+ }
+ MM_DBG("audio_write: mf offset_val %x\n",
+ mfield_size);
+ if (copy_from_user(cpy_ptr, buf, mfield_size)) {
+ rc = -EFAULT;
+ break;
+ }
+ /* Check if EOS flag is set and buffer has
+ * contains just meta field
+ */
+ if (cpy_ptr[AUDADPCM_EOS_FLG_OFFSET] &
+ AUDADPCM_EOS_FLG_MASK) {
+ MM_DBG("audio_write: EOS SET\n");
+ eos_condition = AUDADPCM_EOS_SET;
+ if (mfield_size == count) {
+ buf += mfield_size;
+ break;
+ } else
+ cpy_ptr[AUDADPCM_EOS_FLG_OFFSET]
+ &= ~AUDADPCM_EOS_FLG_MASK;
+ }
+ cpy_ptr += mfield_size;
+ count -= mfield_size;
+ dsize += mfield_size;
+ buf += mfield_size;
+ } else {
+ mfield_size = 0;
+ MM_DBG("audio_write: continuous buffer\n");
+ }
+ frame->mfield_sz = mfield_size;
+ }
+
+ if (audio->reserved) {
+ MM_DBG("append reserved byte %x\n", audio->rsv_byte);
+ *cpy_ptr = audio->rsv_byte;
+ xfer = (count > ((frame->size - mfield_size) - 1)) ?
+ (frame->size - mfield_size) - 1 : count;
+ cpy_ptr++;
+ dsize += 1;
+ audio->reserved = 0;
+ } else
+ xfer = (count > (frame->size - mfield_size)) ?
+ (frame->size - mfield_size) : count;
+
+ if (copy_from_user(cpy_ptr, buf, xfer)) {
+ rc = -EFAULT;
+ break;
+ }
+
+ dsize += xfer;
+ if (dsize & 1) {
+ audio->rsv_byte = ((char *) frame->data)[dsize - 1];
+ MM_DBG("odd length buf reserve last byte %x\n",
+ audio->rsv_byte);
+ audio->reserved = 1;
+ dsize--;
+ }
+ count -= xfer;
+ buf += xfer;
+
+ if (dsize > 0) {
+ audio->out_head ^= 1;
+ frame->used = dsize;
+ audplay_send_data(audio, 0);
+ }
+ }
+ if (eos_condition == AUDADPCM_EOS_SET)
+ rc = audadpcm_process_eos(audio, start, mfield_size);
+ mutex_unlock(&audio->write_lock);
+ if (!rc) {
+ if (buf > start)
+ return buf - start;
+ }
+ return rc;
+}
+
+static int audio_release(struct inode *inode, struct file *file)
+{
+ struct audio *audio = file->private_data;
+
+ MM_INFO("audio instance 0x%08x freeing\n", (int)audio);
+
+ mutex_lock(&audio->lock);
+ auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id);
+ audio_disable(audio);
+ audio_flush(audio);
+ audio_flush_pcm_buf(audio);
+ msm_adsp_put(audio->audplay);
+ audpp_adec_free(audio->dec_id);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&audio->suspend_ctl.node);
+#endif
+ audio->event_abort = 1;
+ wake_up(&audio->event_wait);
+ audadpcm_reset_event_queue(audio);
+ iounmap(audio->data);
+ pmem_kfree(audio->phys);
+ if (audio->read_data) {
+ iounmap(audio->read_data);
+ pmem_kfree(audio->read_phys);
+ }
+ mutex_unlock(&audio->lock);
+#ifdef CONFIG_DEBUG_FS
+ if (audio->dentry)
+ debugfs_remove(audio->dentry);
+#endif
+ kfree(audio);
+ return 0;
+}
+
+static void audadpcm_post_event(struct audio *audio, int type,
+ union msm_audio_event_payload payload)
+{
+ struct audadpcm_event *e_node = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+
+ if (!list_empty(&audio->free_event_queue)) {
+ e_node = list_first_entry(&audio->free_event_queue,
+ struct audadpcm_event, list);
+ list_del(&e_node->list);
+ } else {
+ e_node = kmalloc(sizeof(struct audadpcm_event), GFP_ATOMIC);
+ if (!e_node) {
+ MM_ERR("No mem to post event %d\n", type);
+ return;
+ }
+ }
+
+ e_node->event_type = type;
+ e_node->payload = payload;
+
+ list_add_tail(&e_node->list, &audio->event_queue);
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+ wake_up(&audio->event_wait);
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void audadpcm_suspend(struct early_suspend *h)
+{
+ struct audadpcm_suspend_ctl *ctl =
+ container_of(h, struct audadpcm_suspend_ctl, node);
+ union msm_audio_event_payload payload;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ audadpcm_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload);
+}
+
+static void audadpcm_resume(struct early_suspend *h)
+{
+ struct audadpcm_suspend_ctl *ctl =
+ container_of(h, struct audadpcm_suspend_ctl, node);
+ union msm_audio_event_payload payload;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ audadpcm_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload);
+}
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t audadpcm_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t audadpcm_debug_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ const int debug_bufmax = 4096;
+ static char buffer[4096];
+ int n = 0, i;
+ struct audio *audio = file->private_data;
+
+ mutex_lock(&audio->lock);
+ n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "enabled %d\n", audio->enabled);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "stopped %d\n", audio->stopped);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "pcm_feedback %d\n", audio->pcm_feedback);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_buf_sz %d\n", audio->out[0].size);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "pcm_buf_count %d \n", audio->pcm_buf_count);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "pcm_buf_sz %d \n", audio->in[0].size);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "volume %x \n", audio->vol_pan.volume);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "sample rate %d \n", audio->out_sample_rate);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "channel mode %d \n", audio->out_channel_mode);
+ mutex_unlock(&audio->lock);
+ /* Following variables are only useful for debugging when
+ * when playback halts unexpectedly. Thus, no mutual exclusion
+ * enforced
+ */
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "wflush %d\n", audio->wflush);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "rflush %d\n", audio->rflush);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "running %d \n", audio->running);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "dec state %d \n", audio->dec_state);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_needed %d \n", audio->out_needed);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_head %d \n", audio->out_head);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_tail %d \n", audio->out_tail);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out[0].used %d \n", audio->out[0].used);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out[1].used %d \n", audio->out[1].used);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "buffer_refresh %d \n", audio->buf_refresh);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "read_next %d \n", audio->read_next);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "fill_next %d \n", audio->fill_next);
+ for (i = 0; i < audio->pcm_buf_count; i++)
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "in[%d].size %d \n", i, audio->in[i].used);
+ buffer[n] = 0;
+ return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static const struct file_operations audadpcm_debug_fops = {
+ .read = audadpcm_debug_read,
+ .open = audadpcm_debug_open,
+};
+#endif
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct audio *audio = NULL;
+ int rc, dec_attrb, decid, i;
+ unsigned pmem_sz = DMASZ_MAX;
+ struct audadpcm_event *e_node = NULL;
+#ifdef CONFIG_DEBUG_FS
+ /* 4 bytes represents decoder number, 1 byte for terminate string */
+ char name[sizeof "msm_adpcm_" + 5];
+#endif
+
+ /* Allocate Mem for audio instance */
+ audio = kzalloc(sizeof(struct audio), GFP_KERNEL);
+ if (!audio) {
+ MM_ERR("no memory to allocate audio instance \n");
+ rc = -ENOMEM;
+ goto done;
+ }
+ MM_INFO("audio instance 0x%08x created\n", (int)audio);
+
+ /* Allocate the decoder */
+ dec_attrb = AUDDEC_DEC_ADPCM;
+ if ((file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ dec_attrb |= MSM_AUD_MODE_NONTUNNEL;
+ audio->pcm_feedback = NON_TUNNEL_MODE_PLAYBACK;
+ } else if ((file->f_mode & FMODE_WRITE) &&
+ !(file->f_mode & FMODE_READ)) {
+ dec_attrb |= MSM_AUD_MODE_TUNNEL;
+ audio->pcm_feedback = TUNNEL_MODE_PLAYBACK;
+ } else {
+ kfree(audio);
+ rc = -EACCES;
+ goto done;
+ }
+
+ decid = audpp_adec_alloc(dec_attrb, &audio->module_name,
+ &audio->queue_id);
+
+ if (decid < 0) {
+ MM_ERR("No free decoder available, freeing instance 0x%08x\n",
+ (int)audio);
+ rc = -ENODEV;
+ kfree(audio);
+ goto done;
+ }
+ audio->dec_id = decid & MSM_AUD_DECODER_MASK;
+
+ while (pmem_sz >= DMASZ_MIN) {
+ MM_DBG("pmemsz = %d\n", pmem_sz);
+ audio->phys = pmem_kalloc(pmem_sz, PMEM_MEMTYPE_EBI1|
+ PMEM_ALIGNMENT_4K);
+ if (!IS_ERR((void *)audio->phys)) {
+ audio->data = ioremap(audio->phys, pmem_sz);
+ if (!audio->data) {
+ MM_ERR("could not allocate write buffers, \
+ freeing instance 0x%08x\n",
+ (int)audio);
+ rc = -ENOMEM;
+ pmem_kfree(audio->phys);
+ audpp_adec_free(audio->dec_id);
+ kfree(audio);
+ goto done;
+ }
+ MM_DBG("write buf: phy addr 0x%08x kernel addr \
+ 0x%08x\n", audio->phys, (int)audio->data);
+ break;
+ } else if (pmem_sz == DMASZ_MIN) {
+ MM_ERR("could not allocate write buffers, freeing \
+ instance 0x%08x\n", (int)audio);
+ rc = -ENOMEM;
+ audpp_adec_free(audio->dec_id);
+ kfree(audio);
+ goto done;
+ } else
+ pmem_sz >>= 1;
+ }
+ audio->out_dma_sz = pmem_sz;
+
+ rc = msm_adsp_get(audio->module_name, &audio->audplay,
+ &audplay_adsp_ops_adpcm, audio);
+ if (rc) {
+ MM_ERR("failed to get %s module, freeing instance 0x%08x\n",
+ audio->module_name, (int)audio);
+ goto err;
+ }
+
+ mutex_init(&audio->lock);
+ mutex_init(&audio->write_lock);
+ mutex_init(&audio->read_lock);
+ mutex_init(&audio->get_event_lock);
+ spin_lock_init(&audio->dsp_lock);
+ init_waitqueue_head(&audio->write_wait);
+ init_waitqueue_head(&audio->read_wait);
+ INIT_LIST_HEAD(&audio->free_event_queue);
+ INIT_LIST_HEAD(&audio->event_queue);
+ init_waitqueue_head(&audio->wait);
+ init_waitqueue_head(&audio->event_wait);
+ spin_lock_init(&audio->event_queue_lock);
+ init_waitqueue_head(&audio->avsync_wait);
+
+ audio->out[0].data = audio->data + 0;
+ audio->out[0].addr = audio->phys + 0;
+ audio->out[0].size = audio->out_dma_sz >> 1;
+
+ audio->out[1].data = audio->data + audio->out[0].size;
+ audio->out[1].addr = audio->phys + audio->out[0].size;
+ audio->out[1].size = audio->out[0].size;
+
+ audio->out_sample_rate = 44100;
+ audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V;
+
+ audio->vol_pan.volume = 0x2000;
+
+ audio_flush(audio);
+
+ file->private_data = audio;
+ audio->opened = 1;
+
+ audio->device_events = AUDDEV_EVT_DEV_RDY
+ |AUDDEV_EVT_DEV_RLS|
+ AUDDEV_EVT_STREAM_VOL_CHG;
+
+ rc = auddev_register_evt_listner(audio->device_events,
+ AUDDEV_CLNT_DEC,
+ audio->dec_id,
+ adpcm_listner,
+ (void *)audio);
+ if (rc) {
+ MM_ERR("%s: failed to register listner\n", __func__);
+ goto event_err;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ snprintf(name, sizeof name, "msm_adpcm_%04x", audio->dec_id);
+ audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *) audio,
+ &audadpcm_debug_fops);
+
+ if (IS_ERR(audio->dentry))
+ MM_DBG("debugfs_create_file failed\n");
+#endif
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+ audio->suspend_ctl.node.resume = audadpcm_resume;
+ audio->suspend_ctl.node.suspend = audadpcm_suspend;
+ audio->suspend_ctl.audio = audio;
+ register_early_suspend(&audio->suspend_ctl.node);
+#endif
+ for (i = 0; i < AUDADPCM_EVENT_NUM; i++) {
+ e_node = kmalloc(sizeof(struct audadpcm_event), GFP_KERNEL);
+ if (e_node)
+ list_add_tail(&e_node->list, &audio->free_event_queue);
+ else {
+ MM_ERR("event pkt alloc failed\n");
+ break;
+ }
+ }
+done:
+ return rc;
+event_err:
+ msm_adsp_put(audio->audplay);
+err:
+ iounmap(audio->data);
+ pmem_kfree(audio->phys);
+ audpp_adec_free(audio->dec_id);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_adpcm_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_release,
+ .read = audio_read,
+ .write = audio_write,
+ .unlocked_ioctl = audio_ioctl,
+ .fsync = audio_fsync,
+};
+
+struct miscdevice audio_adpcm_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_adpcm",
+ .fops = &audio_adpcm_fops,
+};
+
+static int __init audio_init(void)
+{
+ return misc_register(&audio_adpcm_misc);
+}
+
+device_initcall(audio_init);
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_amrnb.c b/arch/arm/mach-msm/qdsp5v2/audio_amrnb.c
new file mode 100644
index 0000000..53a8850
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_amrnb.c
@@ -0,0 +1,1633 @@
+/*
+ * amrnb audio decoder device
+ *
+ * Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
+ *
+ * Based on the mp3 native driver in arch/arm/mach-msm/qdsp5/audio_mp3.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ *
+ * All source code in this file is licensed under the following license except
+ * where indicated.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/earlysuspend.h>
+#include <linux/android_pmem.h>
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+#include <linux/msm_audio.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <linux/slab.h>
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/qdsp5audplaycmdi.h>
+#include <mach/qdsp5v2/qdsp5audplaymsg.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/qdsp5v2/audpp.h>
+#include <mach/debug_mm.h>
+
+#define BUFSZ 1024 /* Hold minimum 700ms voice data and 14 bytes of meta in*/
+#define DMASZ (BUFSZ * 2)
+
+#define AUDPLAY_INVALID_READ_PTR_OFFSET 0xFFFF
+#define AUDDEC_DEC_AMRNB 10
+
+#define PCM_BUFSZ_MIN 1624 /* 100ms worth of data and 24 bytes of meta out*/
+#define AMRNB_DECODED_FRSZ 320 /* AMR-NB 20ms 8KHz mono PCM size */
+#define PCM_BUF_MAX_COUNT 5 /* DSP only accepts 5 buffers at most
+ but support 2 buffers currently */
+#define ROUTING_MODE_FTRT 1
+#define ROUTING_MODE_RT 2
+/* Decoder status received from AUDPPTASK */
+#define AUDPP_DEC_STATUS_SLEEP 0
+#define AUDPP_DEC_STATUS_INIT 1
+#define AUDPP_DEC_STATUS_CFG 2
+#define AUDPP_DEC_STATUS_PLAY 3
+
+#define AUDAMRNB_METAFIELD_MASK 0xFFFF0000
+#define AUDAMRNB_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */
+#define AUDAMRNB_EOS_FLG_MASK 0x01
+#define AUDAMRNB_EOS_NONE 0x0 /* No EOS detected */
+#define AUDAMRNB_EOS_SET 0x1 /* EOS set in meta field */
+
+#define AUDAMRNB_EVENT_NUM 10 /* Default number of pre-allocated event pkts */
+
+struct buffer {
+ void *data;
+ unsigned size;
+ unsigned used; /* Input usage actual DSP produced PCM size */
+ unsigned addr;
+ unsigned short mfield_sz; /*only useful for data has meta field */
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+struct audamrnb_suspend_ctl {
+ struct early_suspend node;
+ struct audio *audio;
+};
+#endif
+
+struct audamrnb_event{
+ struct list_head list;
+ int event_type;
+ union msm_audio_event_payload payload;
+};
+
+struct audio {
+ struct buffer out[2];
+
+ spinlock_t dsp_lock;
+
+ uint8_t out_head;
+ uint8_t out_tail;
+ uint8_t out_needed; /* number of buffers the dsp is waiting for */
+
+ atomic_t out_bytes;
+
+ struct mutex lock;
+ struct mutex write_lock;
+ wait_queue_head_t write_wait;
+
+ /* Host PCM section */
+ struct buffer in[PCM_BUF_MAX_COUNT];
+ struct mutex read_lock;
+ wait_queue_head_t read_wait; /* Wait queue for read */
+ char *read_data; /* pointer to reader buffer */
+ int32_t read_phys; /* physical address of reader buffer */
+ uint8_t read_next; /* index to input buffers to be read next */
+ uint8_t fill_next; /* index to buffer that DSP should be filling */
+ uint8_t pcm_buf_count; /* number of pcm buffer allocated */
+ /* ---- End of Host PCM section */
+
+ struct msm_adsp_module *audplay;
+
+ /* data allocated for various buffers */
+ char *data;
+ int32_t phys; /* physical address of write buffer */
+
+ int mfield; /* meta field embedded in data */
+ int rflush; /* Read flush */
+ int wflush; /* Write flush */
+ uint8_t opened:1;
+ uint8_t enabled:1;
+ uint8_t running:1;
+ uint8_t stopped:1; /* set when stopped, cleared on flush */
+ uint8_t pcm_feedback:1;
+ uint8_t buf_refresh:1;
+ int teos; /* valid only if tunnel mode & no data left for decoder */
+ enum msm_aud_decoder_state dec_state; /* Represents decoder state */
+
+ const char *module_name;
+ unsigned queue_id;
+ uint16_t dec_id;
+ uint32_t read_ptr_offset;
+ int16_t source;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct audamrnb_suspend_ctl suspend_ctl;
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dentry;
+#endif
+
+ wait_queue_head_t wait;
+ struct list_head free_event_queue;
+ struct list_head event_queue;
+ wait_queue_head_t event_wait;
+ spinlock_t event_queue_lock;
+ struct mutex get_event_lock;
+ int event_abort;
+ /* AV sync Info */
+ int avsync_flag; /* Flag to indicate feedback from DSP */
+ wait_queue_head_t avsync_wait;/* Wait queue for AV Sync Message */
+ /* flags, 48 bits sample/bytes counter per channel */
+ uint16_t avsync[AUDPP_AVSYNC_CH_COUNT * AUDPP_AVSYNC_NUM_WORDS + 1];
+
+ uint32_t device_events;
+
+ int eq_enable;
+ int eq_needs_commit;
+ struct audpp_cmd_cfg_object_params_eqalizer eq;
+ struct audpp_cmd_cfg_object_params_volume vol_pan;
+};
+
+struct audpp_cmd_cfg_adec_params_amrnb {
+ struct audpp_cmd_cfg_adec_params_common common;
+ unsigned short stereo_cfg;
+} __attribute__((packed)) ;
+
+static int auddec_dsp_config(struct audio *audio, int enable);
+static void audpp_cmd_cfg_adec_params(struct audio *audio);
+static void audpp_cmd_cfg_routing_mode(struct audio *audio);
+static void audamrnb_send_data(struct audio *audio, unsigned needed);
+static void audamrnb_config_hostpcm(struct audio *audio);
+static void audamrnb_buffer_refresh(struct audio *audio);
+static void audamrnb_dsp_event(void *private, unsigned id, uint16_t *msg);
+static void audamrnb_post_event(struct audio *audio, int type,
+ union msm_audio_event_payload payload);
+
+/* must be called with audio->lock held */
+static int audamrnb_enable(struct audio *audio)
+{
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ if (audio->enabled)
+ return 0;
+
+ audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+ audio->out_tail = 0;
+ audio->out_needed = 0;
+
+ if (msm_adsp_enable(audio->audplay)) {
+ MM_ERR("msm_adsp_enable(audplay) failed\n");
+ return -ENODEV;
+ }
+
+ if (audpp_enable(audio->dec_id, audamrnb_dsp_event, audio)) {
+ MM_ERR("audpp_enable() failed\n");
+ msm_adsp_disable(audio->audplay);
+ return -ENODEV;
+ }
+ audio->enabled = 1;
+ return 0;
+}
+
+static void amrnb_listner(u32 evt_id, union auddev_evt_data *evt_payload,
+ void *private_data)
+{
+ struct audio *audio = (struct audio *) private_data;
+ switch (evt_id) {
+ case AUDDEV_EVT_DEV_RDY:
+ MM_DBG(":AUDDEV_EVT_DEV_RDY\n");
+ audio->source |= (0x1 << evt_payload->routing_id);
+ if (audio->running == 1 && audio->enabled == 1)
+ audpp_route_stream(audio->dec_id, audio->source);
+ break;
+ case AUDDEV_EVT_DEV_RLS:
+ MM_DBG(":AUDDEV_EVT_DEV_RLS\n");
+ audio->source &= ~(0x1 << evt_payload->routing_id);
+ if (audio->running == 1 && audio->enabled == 1)
+ audpp_route_stream(audio->dec_id, audio->source);
+ break;
+ case AUDDEV_EVT_STREAM_VOL_CHG:
+ audio->vol_pan.volume = evt_payload->session_vol;
+ MM_DBG(":AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n",
+ audio->vol_pan.volume);
+ if (audio->running)
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ break;
+ default:
+ MM_ERR(":ERROR:wrong event\n");
+ break;
+ }
+}
+/* must be called with audio->lock held */
+static int audamrnb_disable(struct audio *audio)
+{
+ int rc = 0;
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ if (audio->enabled) {
+ audio->enabled = 0;
+ audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+ auddec_dsp_config(audio, 0);
+ rc = wait_event_interruptible_timeout(audio->wait,
+ audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+ msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE)
+ rc = -EFAULT;
+ else
+ rc = 0;
+ wake_up(&audio->write_wait);
+ wake_up(&audio->read_wait);
+ msm_adsp_disable(audio->audplay);
+ audpp_disable(audio->dec_id, audio);
+ audio->out_needed = 0;
+ }
+ return rc;
+}
+
+/* ------------------- dsp --------------------- */
+static void audamrnb_update_pcm_buf_entry(struct audio *audio,
+ uint32_t *payload)
+{
+ uint8_t index;
+ unsigned long flags;
+
+ if (audio->rflush)
+ return;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ for (index = 0; index < payload[1]; index++) {
+ if (audio->in[audio->fill_next].addr ==
+ payload[2 + index * 2]) {
+ MM_DBG("in[%d] ready\n", audio->fill_next);
+ audio->in[audio->fill_next].used =
+ payload[3 + index * 2];
+ if ((++audio->fill_next) == audio->pcm_buf_count)
+ audio->fill_next = 0;
+
+ } else {
+ MM_ERR("expected=%x ret=%x\n",
+ audio->in[audio->fill_next].addr,
+ payload[1 + index * 2]);
+ break;
+ }
+ }
+ if (audio->in[audio->fill_next].used == 0) {
+ audamrnb_buffer_refresh(audio);
+ } else {
+ MM_DBG("read cannot keep up\n");
+ audio->buf_refresh = 1;
+ }
+ wake_up(&audio->read_wait);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+static void audplay_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent) (void *ptr, size_t len))
+{
+ struct audio *audio = data;
+ uint32_t msg[28];
+ getevent(msg, sizeof(msg));
+
+ MM_DBG("msg_id=%x\n", id);
+
+ switch (id) {
+ case AUDPLAY_MSG_DEC_NEEDS_DATA:
+ audamrnb_send_data(audio, 1);
+ break;
+
+ case AUDPLAY_MSG_BUFFER_UPDATE:
+ audamrnb_update_pcm_buf_entry(audio, msg);
+ break;
+
+ case ADSP_MESSAGE_ID:
+ MM_DBG("Received ADSP event: module enable(audplaytask)\n");
+ break;
+
+ default:
+ MM_ERR("unexpected message from decoder\n");
+ }
+}
+
+static void audamrnb_dsp_event(void *private, unsigned id, uint16_t *msg)
+{
+ struct audio *audio = private;
+
+ switch (id) {
+ case AUDPP_MSG_STATUS_MSG:{
+ unsigned status = msg[1];
+
+ switch (status) {
+ case AUDPP_DEC_STATUS_SLEEP: {
+ uint16_t reason = msg[2];
+ MM_DBG("decoder status:sleep reason = \
+ 0x%04x\n", reason);
+ if ((reason == AUDPP_MSG_REASON_MEM)
+ || (reason ==
+ AUDPP_MSG_REASON_NODECODER)) {
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_FAILURE;
+ wake_up(&audio->wait);
+ } else if (reason == AUDPP_MSG_REASON_NONE) {
+ /* decoder is in disable state */
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_CLOSE;
+ wake_up(&audio->wait);
+ }
+ break;
+ }
+ case AUDPP_DEC_STATUS_INIT:
+ MM_DBG("decoder status: init \n");
+ if (audio->pcm_feedback)
+ audpp_cmd_cfg_routing_mode(audio);
+ else
+ audpp_cmd_cfg_adec_params(audio);
+ break;
+
+ case AUDPP_DEC_STATUS_CFG:
+ MM_DBG("decoder status: cfg \n");
+ break;
+ case AUDPP_DEC_STATUS_PLAY:
+ MM_DBG("decoder status: play \n");
+ /* send mixer command */
+ audpp_route_stream(audio->dec_id,
+ audio->source);
+ if (audio->pcm_feedback) {
+ audamrnb_config_hostpcm(audio);
+ audamrnb_buffer_refresh(audio);
+ }
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_SUCCESS;
+ wake_up(&audio->wait);
+ break;
+ default:
+ MM_ERR("unknown decoder status \n");
+ break;
+ }
+ break;
+ }
+ case AUDPP_MSG_CFG_MSG:
+ if (msg[0] == AUDPP_MSG_ENA_ENA) {
+ MM_DBG("CFG_MSG ENABLE\n");
+ auddec_dsp_config(audio, 1);
+ audio->out_needed = 0;
+ audio->running = 1;
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ audpp_dsp_set_eq(audio->dec_id, audio->eq_enable,
+ &audio->eq, POPP);
+ } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+ MM_DBG("CFG_MSG DISABLE\n");
+ audio->running = 0;
+ } else {
+ MM_DBG("CFG_MSG %d?\n", msg[0]);
+ }
+ break;
+ case AUDPP_MSG_ROUTING_ACK:
+ MM_DBG("ROUTING_ACK mode=%d\n", msg[1]);
+ audpp_cmd_cfg_adec_params(audio);
+ break;
+ case AUDPP_MSG_FLUSH_ACK:
+ MM_DBG("FLUSH_ACK\n");
+ audio->wflush = 0;
+ audio->rflush = 0;
+ wake_up(&audio->write_wait);
+ if (audio->pcm_feedback)
+ audamrnb_buffer_refresh(audio);
+ break;
+ case AUDPP_MSG_PCMDMAMISSED:
+ MM_DBG("PCMDMAMISSED\n");
+ audio->teos = 1;
+ wake_up(&audio->write_wait);
+ break;
+
+ case AUDPP_MSG_AVSYNC_MSG:
+ MM_DBG("AUDPP_MSG_AVSYNC_MSG\n");
+ memcpy(&audio->avsync[0], msg, sizeof(audio->avsync));
+ audio->avsync_flag = 1;
+ wake_up(&audio->avsync_wait);
+ break;
+
+ default:
+ MM_ERR("UNKNOWN (%d)\n", id);
+ }
+
+}
+
+struct msm_adsp_ops audplay_adsp_ops_amrnb = {
+ .event = audplay_dsp_event,
+};
+
+#define audplay_send_queue0(audio, cmd, len) \
+ msm_adsp_write(audio->audplay, audio->queue_id, \
+ cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+ struct audpp_cmd_cfg_dec_type cfg_dec_cmd;
+
+ memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd));
+
+ cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+ if (enable)
+ cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_AMRNB;
+ else
+ cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_DIS_DEC_V;
+ cfg_dec_cmd.dm_mode = 0x0;
+ cfg_dec_cmd.stream_id = audio->dec_id;
+
+ return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd));
+}
+
+static void audpp_cmd_cfg_adec_params(struct audio *audio)
+{
+ struct audpp_cmd_cfg_adec_params_amrnb cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+ cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_V13K_LEN;
+ cmd.common.dec_id = audio->dec_id;
+ cmd.common.input_sampling_frequency = 8000;
+ cmd.stereo_cfg = AUDPP_CMD_PCM_INTF_MONO_V;
+
+ audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_routing_mode(struct audio *audio)
+{
+ struct audpp_cmd_routing_mode cmd;
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_ROUTING_MODE;
+ cmd.object_number = audio->dec_id;
+ if (audio->pcm_feedback)
+ cmd.routing_mode = ROUTING_MODE_FTRT;
+ else
+ cmd.routing_mode = ROUTING_MODE_RT;
+
+ audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static int audplay_dsp_send_data_avail(struct audio *audio,
+ unsigned idx, unsigned len)
+{
+ struct audplay_cmd_bitstream_data_avail_nt2 cmd;
+
+ cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2;
+ if (audio->mfield)
+ cmd.decoder_id = AUDAMRNB_METAFIELD_MASK |
+ (audio->out[idx].mfield_sz >> 1);
+ else
+ cmd.decoder_id = audio->dec_id;
+ cmd.buf_ptr = audio->out[idx].addr;
+ cmd.buf_size = len / 2;
+ cmd.partition_number = 0;
+ return audplay_send_queue0(audio, &cmd, sizeof(cmd));
+}
+
+static void audamrnb_buffer_refresh(struct audio *audio)
+{
+ struct audplay_cmd_buffer_refresh refresh_cmd;
+
+ refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH;
+ refresh_cmd.num_buffers = 1;
+ refresh_cmd.buf0_address = audio->in[audio->fill_next].addr;
+ refresh_cmd.buf0_length = audio->in[audio->fill_next].size -
+ (audio->in[audio->fill_next].size % AMRNB_DECODED_FRSZ) +
+ (audio->mfield ? 24 : 0);
+ refresh_cmd.buf_read_count = 0;
+ MM_DBG("buf0_addr=%x buf0_len=%d\n", refresh_cmd.buf0_address,
+ refresh_cmd.buf0_length);
+ (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd));
+}
+
+static void audamrnb_config_hostpcm(struct audio *audio)
+{
+ struct audplay_cmd_hpcm_buf_cfg cfg_cmd;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG;
+ cfg_cmd.max_buffers = audio->pcm_buf_count;
+ cfg_cmd.byte_swap = 0;
+ cfg_cmd.hostpcm_config = (0x8000) | (0x4000);
+ cfg_cmd.feedback_frequency = 1;
+ cfg_cmd.partition_number = 0;
+ (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd));
+
+}
+
+static void audamrnb_send_data(struct audio *audio, unsigned needed)
+{
+ struct buffer *frame;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (!audio->running)
+ goto done;
+
+ if (needed && !audio->wflush) {
+ /* We were called from the callback because the DSP
+ * requested more data. Note that the DSP does want
+ * more data, and if a buffer was in-flight, mark it
+ * as available (since the DSP must now be done with
+ * it).
+ */
+ audio->out_needed = 1;
+ frame = audio->out + audio->out_tail;
+ if (frame->used == 0xffffffff) {
+ frame->used = 0;
+ audio->out_tail ^= 1;
+ wake_up(&audio->write_wait);
+ }
+ }
+
+ if (audio->out_needed) {
+ /* If the DSP currently wants data and we have a
+ * buffer available, we will send it and reset
+ * the needed flag. We'll mark the buffer as in-flight
+ * so that it won't be recycled until the next buffer
+ * is requested
+ */
+
+ frame = audio->out + audio->out_tail;
+ if (frame->used) {
+ BUG_ON(frame->used == 0xffffffff);
+ MM_DBG("frame %d busy\n", audio->out_tail);
+ audplay_dsp_send_data_avail(audio, audio->out_tail,
+ frame->used);
+ frame->used = 0xffffffff;
+ audio->out_needed = 0;
+ }
+ }
+ done:
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+/* ------------------- device --------------------- */
+
+static void audamrnb_flush(struct audio *audio)
+{
+ audio->out[0].used = 0;
+ audio->out[1].used = 0;
+ audio->out_head = 0;
+ audio->out_tail = 0;
+ audio->out_needed = 0;
+ atomic_set(&audio->out_bytes, 0);
+}
+
+static void audamrnb_flush_pcm_buf(struct audio *audio)
+{
+ uint8_t index;
+
+ for (index = 0; index < PCM_BUF_MAX_COUNT; index++)
+ audio->in[index].used = 0;
+
+ audio->buf_refresh = 0;
+ audio->read_next = 0;
+ audio->fill_next = 0;
+}
+
+static void audamrnb_ioport_reset(struct audio *audio)
+{
+ /* Make sure read/write thread are free from
+ * sleep and knowing that system is not able
+ * to process io request at the moment
+ */
+ wake_up(&audio->write_wait);
+ mutex_lock(&audio->write_lock);
+ audamrnb_flush(audio);
+ mutex_unlock(&audio->write_lock);
+ wake_up(&audio->read_wait);
+ mutex_lock(&audio->read_lock);
+ audamrnb_flush_pcm_buf(audio);
+ mutex_unlock(&audio->read_lock);
+ audio->avsync_flag = 1;
+ wake_up(&audio->avsync_wait);
+}
+
+static int audamrnb_events_pending(struct audio *audio)
+{
+ unsigned long flags;
+ int empty;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ empty = !list_empty(&audio->event_queue);
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+ return empty || audio->event_abort;
+}
+
+static void audamrnb_reset_event_queue(struct audio *audio)
+{
+ unsigned long flags;
+ struct audamrnb_event *drv_evt;
+ struct list_head *ptr, *next;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ list_for_each_safe(ptr, next, &audio->event_queue) {
+ drv_evt = list_first_entry(&audio->event_queue,
+ struct audamrnb_event, list);
+ list_del(&drv_evt->list);
+ kfree(drv_evt);
+ }
+ list_for_each_safe(ptr, next, &audio->free_event_queue) {
+ drv_evt = list_first_entry(&audio->free_event_queue,
+ struct audamrnb_event, list);
+ list_del(&drv_evt->list);
+ kfree(drv_evt);
+ }
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+ return;
+}
+
+static long audamrnb_process_event_req(struct audio *audio, void __user *arg)
+{
+ long rc;
+ struct msm_audio_event usr_evt;
+ struct audamrnb_event *drv_evt = NULL;
+ int timeout;
+ unsigned long flags;
+
+ if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event)))
+ return -EFAULT;
+
+ timeout = (int) usr_evt.timeout_ms;
+
+ if (timeout > 0) {
+ rc = wait_event_interruptible_timeout(
+ audio->event_wait, audamrnb_events_pending(audio),
+ msecs_to_jiffies(timeout));
+ if (rc == 0)
+ return -ETIMEDOUT;
+ } else {
+ rc = wait_event_interruptible(
+ audio->event_wait, audamrnb_events_pending(audio));
+ }
+
+ if (rc < 0)
+ return rc;
+
+ if (audio->event_abort) {
+ audio->event_abort = 0;
+ return -ENODEV;
+ }
+
+ rc = 0;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ if (!list_empty(&audio->event_queue)) {
+ drv_evt = list_first_entry(&audio->event_queue,
+ struct audamrnb_event, list);
+ list_del(&drv_evt->list);
+ }
+
+ if (drv_evt) {
+ usr_evt.event_type = drv_evt->event_type;
+ usr_evt.event_payload = drv_evt->payload;
+ list_add_tail(&drv_evt->list, &audio->free_event_queue);
+ } else
+ rc = -1;
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+ if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt)))
+ rc = -EFAULT;
+
+ return rc;
+}
+
+static int audio_enable_eq(struct audio *audio, int enable)
+{
+ if (audio->eq_enable == enable && !audio->eq_needs_commit)
+ return 0;
+
+ audio->eq_enable = enable;
+
+ if (audio->running) {
+ audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq, POPP);
+ audio->eq_needs_commit = 0;
+ }
+ return 0;
+}
+
+static int audio_get_avsync_data(struct audio *audio,
+ struct msm_audio_stats *stats)
+{
+ int rc = -EINVAL;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) {
+ /* av_sync sample count */
+ stats->sample_count = (audio->avsync[2] << 16) |
+ (audio->avsync[3]);
+
+ /* av_sync byte_count */
+ stats->byte_count = (audio->avsync[5] << 16) |
+ (audio->avsync[6]);
+
+ audio->avsync_flag = 0;
+ rc = 0;
+ }
+ local_irq_restore(flags);
+ return rc;
+
+}
+
+static long audamrnb_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct audio *audio = file->private_data;
+ int rc = -EINVAL;
+ unsigned long flags = 0;
+ uint16_t enable_mask;
+ int enable;
+ int prev_state;
+
+ MM_DBG("cmd = %d\n", cmd);
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+
+ audio->avsync_flag = 0;
+ memset(&stats, 0, sizeof(stats));
+ if (audpp_query_avsync(audio->dec_id) < 0)
+ return rc;
+
+ rc = wait_event_interruptible_timeout(audio->avsync_wait,
+ (audio->avsync_flag == 1),
+ msecs_to_jiffies(AUDPP_AVSYNC_EVENT_TIMEOUT));
+
+ if (rc < 0)
+ return rc;
+ else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) {
+ if (audio_get_avsync_data(audio, &stats) < 0)
+ return rc;
+
+ if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ } else
+ return -EAGAIN;
+ }
+
+ switch (cmd) {
+ case AUDIO_ENABLE_AUDPP:
+ if (copy_from_user(&enable_mask, (void *) arg,
+ sizeof(enable_mask))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ enable = (enable_mask & EQ_ENABLE) ? 1 : 0;
+ audio_enable_eq(audio, enable);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ rc = 0;
+ break;
+ case AUDIO_SET_VOLUME:
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->vol_pan.volume = arg;
+ if (audio->running)
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ rc = 0;
+ break;
+
+ case AUDIO_SET_PAN:
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->vol_pan.pan = arg;
+ if (audio->running)
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ rc = 0;
+ break;
+
+ case AUDIO_SET_EQ:
+ prev_state = audio->eq_enable;
+ audio->eq_enable = 0;
+ if (copy_from_user(&audio->eq.num_bands, (void *) arg,
+ sizeof(audio->eq) -
+ (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) {
+ rc = -EFAULT;
+ break;
+ }
+ audio->eq_enable = prev_state;
+ audio->eq_needs_commit = 1;
+ rc = 0;
+ break;
+ }
+
+ if (-EINVAL != rc)
+ return rc;
+
+ if (cmd == AUDIO_GET_EVENT) {
+ MM_DBG("AUDIO_GET_EVENT\n");
+ if (mutex_trylock(&audio->get_event_lock)) {
+ rc = audamrnb_process_event_req(audio,
+ (void __user *) arg);
+ mutex_unlock(&audio->get_event_lock);
+ } else
+ rc = -EBUSY;
+ return rc;
+ }
+
+ if (cmd == AUDIO_ABORT_GET_EVENT) {
+ audio->event_abort = 1;
+ wake_up(&audio->event_wait);
+ return 0;
+ }
+
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START:
+ MM_DBG("AUDIO_START\n");
+ rc = audamrnb_enable(audio);
+ if (!rc) {
+ rc = wait_event_interruptible_timeout(audio->wait,
+ audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+ msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+ MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc);
+
+ if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS)
+ rc = -ENODEV;
+ else
+ rc = 0;
+ }
+ break;
+ case AUDIO_STOP:
+ MM_DBG("AUDIO_STOP\n");
+ rc = audamrnb_disable(audio);
+ audio->stopped = 1;
+ audamrnb_ioport_reset(audio);
+ audio->stopped = 0;
+ break;
+ case AUDIO_FLUSH:
+ MM_DBG("AUDIO_FLUSH\n");
+ audio->rflush = 1;
+ audio->wflush = 1;
+ audamrnb_ioport_reset(audio);
+ if (audio->running) {
+ audpp_flush(audio->dec_id);
+ rc = wait_event_interruptible(audio->write_wait,
+ !audio->wflush);
+ if (rc < 0) {
+ MM_ERR("AUDIO_FLUSH interrupted\n");
+ rc = -EINTR;
+ }
+ } else {
+ audio->rflush = 0;
+ audio->wflush = 0;
+ }
+ break;
+ case AUDIO_SET_CONFIG:{
+ struct msm_audio_config config;
+ if (copy_from_user
+ (&config, (void *)arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ audio->mfield = config.meta_field;
+ rc = 0;
+ break;
+ }
+ case AUDIO_GET_CONFIG:{
+ struct msm_audio_config config;
+ config.buffer_size = BUFSZ;
+ config.buffer_count = 2;
+ config.sample_rate = 8000;
+ config.channel_count = 1;
+ config.meta_field = 0;
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+ config.unused[2] = 0;
+ if (copy_to_user((void *)arg, &config,
+ sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+
+ break;
+ }
+ case AUDIO_GET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+ config.pcm_feedback = audio->pcm_feedback;
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+ config.buffer_size = PCM_BUFSZ_MIN;
+ if (copy_to_user((void *)arg, &config,
+ sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ case AUDIO_SET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+ if (copy_from_user
+ (&config, (void *)arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (config.pcm_feedback != audio->pcm_feedback) {
+ MM_ERR("Not sufficient permission to"
+ "change the playback mode\n");
+ rc = -EACCES;
+ break;
+ }
+ if ((config.buffer_count > PCM_BUF_MAX_COUNT) ||
+ (config.buffer_count == 1))
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+
+ if (config.buffer_size < PCM_BUFSZ_MIN)
+ config.buffer_size = PCM_BUFSZ_MIN;
+
+ /* Check if pcm feedback is required */
+ if ((config.pcm_feedback) && (!audio->read_data)) {
+ MM_DBG("allocate PCM buf %d\n",
+ config.buffer_count *
+ config.buffer_size);
+ audio->read_phys = pmem_kalloc(
+ config.buffer_size *
+ config.buffer_count,
+ PMEM_MEMTYPE_EBI1|
+ PMEM_ALIGNMENT_4K);
+ if (IS_ERR((void *)audio->read_phys)) {
+ rc = -ENOMEM;
+ break;
+ }
+ audio->read_data = ioremap(audio->read_phys,
+ config.buffer_size *
+ config.buffer_count);
+ if (!audio->read_data) {
+ MM_ERR("no mem for read buf\n");
+ rc = -ENOMEM;
+ pmem_kfree(audio->read_phys);
+ } else {
+ uint8_t index;
+ uint32_t offset = 0;
+ audio->buf_refresh = 0;
+ audio->pcm_buf_count =
+ config.buffer_count;
+ audio->read_next = 0;
+ audio->fill_next = 0;
+
+ for (index = 0;
+ index < config.buffer_count; index++) {
+ audio->in[index].data =
+ audio->read_data + offset;
+ audio->in[index].addr =
+ audio->read_phys + offset;
+ audio->in[index].size =
+ config.buffer_size;
+ audio->in[index].used = 0;
+ offset += config.buffer_size;
+ }
+ MM_DBG("read buf: phy addr 0x%08x kernel \
+ addr 0x%08x\n", audio->read_phys,
+ (int)audio->read_data);
+ rc = 0;
+ }
+ } else {
+ rc = 0;
+ }
+ break;
+ }
+ case AUDIO_GET_SESSION_ID:
+ if (copy_to_user((void *) arg, &audio->dec_id,
+ sizeof(unsigned short)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+/* Only useful in tunnel-mode */
+static int audamrnb_fsync(struct file *file, int datasync)
+{
+ struct audio *audio = file->private_data;
+ int rc = 0;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+
+ if (!audio->running || audio->pcm_feedback) {
+ rc = -EINVAL;
+ goto done_nolock;
+ }
+
+ mutex_lock(&audio->write_lock);
+
+ rc = wait_event_interruptible(audio->write_wait,
+ (!audio->out[0].used &&
+ !audio->out[1].used &&
+ audio->out_needed) || audio->wflush);
+
+ if (rc < 0)
+ goto done;
+ else if (audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+
+ /* pcm dmamiss message is sent continously
+ * when decoder is starved so no race
+ * condition concern
+ */
+ audio->teos = 0;
+
+ rc = wait_event_interruptible(audio->write_wait,
+ audio->teos || audio->wflush);
+
+ if (audio->wflush)
+ rc = -EBUSY;
+
+done:
+ mutex_unlock(&audio->write_lock);
+done_nolock:
+ return rc;
+}
+
+static ssize_t audamrnb_read(struct file *file, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ int rc = 0;
+
+ if (!audio->pcm_feedback)
+ return 0; /* PCM feedback is not enabled. Nothing to read */
+
+ mutex_lock(&audio->read_lock);
+ MM_DBG("%d \n", count);
+ while (count > 0) {
+ rc = wait_event_interruptible(audio->read_wait,
+ (audio->in[audio->read_next].used > 0) ||
+ (audio->stopped) || (audio->rflush));
+
+ if (rc < 0)
+ break;
+
+ if (audio->stopped || audio->rflush) {
+ rc = -EBUSY;
+ break;
+ }
+
+ if (count < audio->in[audio->read_next].used) {
+ /* Read must happen in frame boundary. Since driver does
+ * not know frame size, read count must be greater or
+ * equal to size of PCM samples
+ */
+ MM_DBG("read stop - partial frame\n");
+ break;
+ } else {
+ MM_DBG("read from in[%d]\n", audio->read_next);
+
+ if (copy_to_user
+ (buf, audio->in[audio->read_next].data,
+ audio->in[audio->read_next].used)) {
+ MM_ERR("invalid addr %x \n", (unsigned int)buf);
+ rc = -EFAULT;
+ break;
+ }
+ count -= audio->in[audio->read_next].used;
+ buf += audio->in[audio->read_next].used;
+ audio->in[audio->read_next].used = 0;
+ if ((++audio->read_next) == audio->pcm_buf_count)
+ audio->read_next = 0;
+ break;
+ }
+ }
+
+ /* don't feed output buffer to HW decoder during flushing
+ * buffer refresh command will be sent once flush completes
+ * send buf refresh command here can confuse HW decoder
+ */
+ if (audio->buf_refresh && !audio->rflush) {
+ audio->buf_refresh = 0;
+ MM_DBG("kick start pcm feedback again\n");
+ audamrnb_buffer_refresh(audio);
+ }
+
+ mutex_unlock(&audio->read_lock);
+
+ if (buf > start)
+ rc = buf - start;
+
+ MM_DBG("read %d bytes\n", rc);
+ return rc;
+}
+
+static int audamrnb_process_eos(struct audio *audio,
+ const char __user *buf_start, unsigned short mfield_size)
+{
+ int rc = 0;
+ struct buffer *frame;
+
+ frame = audio->out + audio->out_head;
+
+ rc = wait_event_interruptible(audio->write_wait,
+ (audio->out_needed &&
+ audio->out[0].used == 0 &&
+ audio->out[1].used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+
+ if (rc < 0)
+ goto done;
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+
+ if (copy_from_user(frame->data, buf_start, mfield_size)) {
+ rc = -EFAULT;
+ goto done;
+ }
+
+ frame->mfield_sz = mfield_size;
+ audio->out_head ^= 1;
+ frame->used = mfield_size;
+ audamrnb_send_data(audio, 0);
+
+done:
+ return rc;
+}
+
+static ssize_t audamrnb_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ struct buffer *frame;
+ size_t xfer;
+ char *cpy_ptr;
+ int rc = 0, eos_condition = AUDAMRNB_EOS_NONE;
+ unsigned short mfield_size = 0;
+
+ MM_DBG("cnt=%d\n", count);
+
+ if (count & 1)
+ return -EINVAL;
+
+ mutex_lock(&audio->write_lock);
+ while (count > 0) {
+ frame = audio->out + audio->out_head;
+ cpy_ptr = frame->data;
+ rc = wait_event_interruptible(audio->write_wait,
+ (frame->used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+
+ MM_DBG("buffer available\n");
+ if (rc < 0)
+ break;
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ break;
+ }
+
+ if (audio->mfield) {
+ if (buf == start) {
+ /* Processing beginning of user buffer */
+ if (__get_user(mfield_size,
+ (unsigned short __user *) buf)) {
+ rc = -EFAULT;
+ break;
+ } else if (mfield_size > count) {
+ rc = -EINVAL;
+ break;
+ }
+ MM_DBG("mf offset_val %x\n", mfield_size);
+ if (copy_from_user(cpy_ptr, buf, mfield_size)) {
+ rc = -EFAULT;
+ break;
+ }
+ /* Check if EOS flag is set and buffer
+ * contains just meta field
+ */
+ if (cpy_ptr[AUDAMRNB_EOS_FLG_OFFSET] &
+ AUDAMRNB_EOS_FLG_MASK) {
+ MM_DBG("eos set\n");
+ eos_condition = AUDAMRNB_EOS_SET;
+ if (mfield_size == count) {
+ buf += mfield_size;
+ break;
+ } else
+ cpy_ptr[AUDAMRNB_EOS_FLG_OFFSET] &=
+ ~AUDAMRNB_EOS_FLG_MASK;
+ }
+ cpy_ptr += mfield_size;
+ count -= mfield_size;
+ buf += mfield_size;
+ } else {
+ mfield_size = 0;
+ MM_DBG("continuous buffer\n");
+ }
+ frame->mfield_sz = mfield_size;
+ }
+
+ xfer = (count > (frame->size - mfield_size)) ?
+ (frame->size - mfield_size) : count;
+ if (copy_from_user(cpy_ptr, buf, xfer)) {
+ rc = -EFAULT;
+ break;
+ }
+
+ frame->used = (xfer + mfield_size);
+ audio->out_head ^= 1;
+ count -= xfer;
+ buf += xfer;
+
+ audamrnb_send_data(audio, 0);
+
+ }
+ if (eos_condition == AUDAMRNB_EOS_SET)
+ rc = audamrnb_process_eos(audio, start, mfield_size);
+ mutex_unlock(&audio->write_lock);
+ if (!rc) {
+ if (buf > start)
+ return buf - start;
+ }
+ return rc;
+}
+
+static int audamrnb_release(struct inode *inode, struct file *file)
+{
+ struct audio *audio = file->private_data;
+
+ MM_INFO("audio instance 0x%08x freeing\n", (int)audio);
+
+ mutex_lock(&audio->lock);
+ auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id);
+ audamrnb_disable(audio);
+ audamrnb_flush(audio);
+ audamrnb_flush_pcm_buf(audio);
+ msm_adsp_put(audio->audplay);
+ audpp_adec_free(audio->dec_id);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&audio->suspend_ctl.node);
+#endif
+ audio->event_abort = 1;
+ wake_up(&audio->event_wait);
+ audamrnb_reset_event_queue(audio);
+ iounmap(audio->data);
+ pmem_kfree(audio->phys);
+ if (audio->read_data) {
+ iounmap(audio->read_data);
+ pmem_kfree(audio->read_phys);
+ }
+ mutex_unlock(&audio->lock);
+#ifdef CONFIG_DEBUG_FS
+ if (audio->dentry)
+ debugfs_remove(audio->dentry);
+#endif
+ kfree(audio);
+ return 0;
+}
+
+static void audamrnb_post_event(struct audio *audio, int type,
+ union msm_audio_event_payload payload)
+{
+ struct audamrnb_event *e_node = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+
+ if (!list_empty(&audio->free_event_queue)) {
+ e_node = list_first_entry(&audio->free_event_queue,
+ struct audamrnb_event, list);
+ list_del(&e_node->list);
+ } else {
+ e_node = kmalloc(sizeof(struct audamrnb_event), GFP_ATOMIC);
+ if (!e_node) {
+ MM_ERR("No mem to post event %d\n", type);
+ return;
+ }
+ }
+
+ e_node->event_type = type;
+ e_node->payload = payload;
+
+ list_add_tail(&e_node->list, &audio->event_queue);
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+ wake_up(&audio->event_wait);
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void audamrnb_suspend(struct early_suspend *h)
+{
+ struct audamrnb_suspend_ctl *ctl =
+ container_of(h, struct audamrnb_suspend_ctl, node);
+ union msm_audio_event_payload payload;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ audamrnb_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload);
+}
+
+static void audamrnb_resume(struct early_suspend *h)
+{
+ struct audamrnb_suspend_ctl *ctl =
+ container_of(h, struct audamrnb_suspend_ctl, node);
+ union msm_audio_event_payload payload;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ audamrnb_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload);
+}
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t audamrnb_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t audamrnb_debug_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ const int debug_bufmax = 1024;
+ static char buffer[1024];
+ int n = 0, i;
+ struct audio *audio = file->private_data;
+
+ mutex_lock(&audio->lock);
+ n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "enabled %d\n", audio->enabled);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "stopped %d\n", audio->stopped);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "pcm_feedback %d\n", audio->pcm_feedback);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_buf_sz %d\n", audio->out[0].size);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "pcm_buf_count %d \n", audio->pcm_buf_count);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "pcm_buf_sz %d \n", audio->in[0].size);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "volume %x \n", audio->vol_pan.volume);
+ mutex_unlock(&audio->lock);
+ /* Following variables are only useful for debugging when
+ * when playback halts unexpectedly. Thus, no mutual exclusion
+ * enforced
+ */
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "wflush %d\n", audio->wflush);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "rflush %d\n", audio->rflush);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "running %d \n", audio->running);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "dec state %d \n", audio->dec_state);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_needed %d \n", audio->out_needed);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_head %d \n", audio->out_head);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_tail %d \n", audio->out_tail);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out[0].used %d \n", audio->out[0].used);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out[1].used %d \n", audio->out[1].used);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "buffer_refresh %d \n", audio->buf_refresh);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "read_next %d \n", audio->read_next);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "fill_next %d \n", audio->fill_next);
+ for (i = 0; i < audio->pcm_buf_count; i++)
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "in[%d].used %d \n", i, audio->in[i].used);
+ buffer[n] = 0;
+ return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static const struct file_operations audamrnb_debug_fops = {
+ .read = audamrnb_debug_read,
+ .open = audamrnb_debug_open,
+};
+#endif
+
+static int audamrnb_open(struct inode *inode, struct file *file)
+{
+ struct audio *audio = NULL;
+ int rc, dec_attrb, decid, i;
+ struct audamrnb_event *e_node = NULL;
+#ifdef CONFIG_DEBUG_FS
+ /* 4 bytes represents decoder number, 1 byte for terminate string */
+ char name[sizeof "msm_amrnb_" + 5];
+#endif
+
+ /* Allocate Mem for audio instance */
+ audio = kzalloc(sizeof(struct audio), GFP_KERNEL);
+ if (!audio) {
+ MM_ERR("no memory to allocate audio instance \n");
+ rc = -ENOMEM;
+ goto done;
+ }
+ MM_INFO("audio instance 0x%08x created\n", (int)audio);
+
+ /* Allocate the decoder */
+ dec_attrb = AUDDEC_DEC_AMRNB;
+ if ((file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ dec_attrb |= MSM_AUD_MODE_NONTUNNEL;
+ audio->pcm_feedback = NON_TUNNEL_MODE_PLAYBACK;
+ } else if ((file->f_mode & FMODE_WRITE) &&
+ !(file->f_mode & FMODE_READ)) {
+ dec_attrb |= MSM_AUD_MODE_TUNNEL;
+ audio->pcm_feedback = TUNNEL_MODE_PLAYBACK;
+ } else {
+ kfree(audio);
+ rc = -EACCES;
+ goto done;
+ }
+
+ decid = audpp_adec_alloc(dec_attrb, &audio->module_name,
+ &audio->queue_id);
+
+ if (decid < 0) {
+ MM_ERR("No free decoder available, freeing instance 0x%08x\n",
+ (int)audio);
+ rc = -ENODEV;
+ kfree(audio);
+ goto done;
+ }
+
+ audio->dec_id = decid & MSM_AUD_DECODER_MASK;
+
+ audio->phys = pmem_kalloc(DMASZ, PMEM_MEMTYPE_EBI1|PMEM_ALIGNMENT_4K);
+ if (IS_ERR((void *)audio->phys)) {
+ MM_ERR("could not allocate write buffers, freeing instance \
+ 0x%08x\n", (int)audio);
+ rc = -ENOMEM;
+ audpp_adec_free(audio->dec_id);
+ kfree(audio);
+ goto done;
+ } else {
+ audio->data = ioremap(audio->phys, DMASZ);
+ if (!audio->data) {
+ MM_ERR("could not allocate write buffers, freeing \
+ instance 0x%08x\n", (int)audio);
+ rc = -ENOMEM;
+ pmem_kfree(audio->phys);
+ audpp_adec_free(audio->dec_id);
+ kfree(audio);
+ goto done;
+ }
+ MM_DBG("write buf: phy addr 0x%08x kernel addr \
+ 0x%08x\n", audio->phys, (int)audio->data);
+ }
+
+ rc = msm_adsp_get(audio->module_name, &audio->audplay,
+ &audplay_adsp_ops_amrnb, audio);
+ if (rc) {
+ MM_ERR("failed to get %s module freeing instance 0x%08x\n",
+ audio->module_name, (int)audio);
+ goto err;
+ }
+
+ mutex_init(&audio->lock);
+ mutex_init(&audio->write_lock);
+ mutex_init(&audio->read_lock);
+ mutex_init(&audio->get_event_lock);
+ spin_lock_init(&audio->dsp_lock);
+ spin_lock_init(&audio->event_queue_lock);
+ INIT_LIST_HEAD(&audio->free_event_queue);
+ INIT_LIST_HEAD(&audio->event_queue);
+ init_waitqueue_head(&audio->write_wait);
+ init_waitqueue_head(&audio->read_wait);
+ init_waitqueue_head(&audio->wait);
+ init_waitqueue_head(&audio->event_wait);
+ init_waitqueue_head(&audio->avsync_wait);
+
+ audio->out[0].data = audio->data + 0;
+ audio->out[0].addr = audio->phys + 0;
+ audio->out[0].size = BUFSZ;
+
+ audio->out[1].data = audio->data + BUFSZ;
+ audio->out[1].addr = audio->phys + BUFSZ;
+ audio->out[1].size = BUFSZ;
+
+ audio->vol_pan.volume = 0x2000;
+
+ audamrnb_flush(audio);
+
+ file->private_data = audio;
+ audio->opened = 1;
+
+ audio->device_events = AUDDEV_EVT_DEV_RDY
+ |AUDDEV_EVT_DEV_RLS|
+ AUDDEV_EVT_STREAM_VOL_CHG;
+
+ rc = auddev_register_evt_listner(audio->device_events,
+ AUDDEV_CLNT_DEC,
+ audio->dec_id,
+ amrnb_listner,
+ (void *)audio);
+ if (rc) {
+ MM_ERR("%s: failed to register listner\n", __func__);
+ goto event_err;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ snprintf(name, sizeof name, "msm_amrnb_%04x", audio->dec_id);
+ audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *) audio, &audamrnb_debug_fops);
+
+ if (IS_ERR(audio->dentry))
+ MM_DBG("debugfs_create_file failed\n");
+#endif
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+ audio->suspend_ctl.node.resume = audamrnb_resume;
+ audio->suspend_ctl.node.suspend = audamrnb_suspend;
+ audio->suspend_ctl.audio = audio;
+ register_early_suspend(&audio->suspend_ctl.node);
+#endif
+ for (i = 0; i < AUDAMRNB_EVENT_NUM; i++) {
+ e_node = kmalloc(sizeof(struct audamrnb_event), GFP_KERNEL);
+ if (e_node)
+ list_add_tail(&e_node->list, &audio->free_event_queue);
+ else {
+ MM_ERR("event pkt alloc failed\n");
+ break;
+ }
+ }
+done:
+ return rc;
+event_err:
+ msm_adsp_put(audio->audplay);
+err:
+ iounmap(audio->data);
+ pmem_kfree(audio->phys);
+ audpp_adec_free(audio->dec_id);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_amrnb_fops = {
+ .owner = THIS_MODULE,
+ .open = audamrnb_open,
+ .release = audamrnb_release,
+ .read = audamrnb_read,
+ .write = audamrnb_write,
+ .unlocked_ioctl = audamrnb_ioctl,
+ .fsync = audamrnb_fsync,
+};
+
+struct miscdevice audio_amrnb_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_amrnb",
+ .fops = &audio_amrnb_fops,
+};
+
+static int __init audamrnb_init(void)
+{
+ return misc_register(&audio_amrnb_misc);
+}
+
+static void __exit audamrnb_exit(void)
+{
+ misc_deregister(&audio_amrnb_misc);
+}
+
+module_init(audamrnb_init);
+module_exit(audamrnb_exit);
+
+MODULE_DESCRIPTION("MSM AMR-NB driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_amrnb_in.c b/arch/arm/mach-msm/qdsp5v2/audio_amrnb_in.c
new file mode 100644
index 0000000..a685af5
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_amrnb_in.c
@@ -0,0 +1,887 @@
+/*
+ * amrnb audio input device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/msm_audio_amrnb.h>
+#include <linux/android_pmem.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+
+#include <mach/msm_adsp.h>
+#include <mach/qdsp5v2/qdsp5audreccmdi.h>
+#include <mach/qdsp5v2/qdsp5audrecmsg.h>
+#include <mach/qdsp5v2/audpreproc.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/debug_mm.h>
+
+/* FRAME_NUM must be a power of two */
+#define FRAME_NUM (8)
+#define FRAME_SIZE (22 * 2) /* 36 bytes data */
+#define DMASZ (FRAME_SIZE * FRAME_NUM)
+
+struct buffer {
+ void *data;
+ uint32_t size;
+ uint32_t read;
+ uint32_t addr;
+};
+
+struct audio_in {
+ struct buffer in[FRAME_NUM];
+
+ spinlock_t dsp_lock;
+
+ atomic_t in_bytes;
+ atomic_t in_samples;
+
+ struct mutex lock;
+ struct mutex read_lock;
+ wait_queue_head_t wait;
+ wait_queue_head_t wait_enable;
+
+ struct msm_adsp_module *audrec;
+ struct audrec_session_info session_info; /*audrec session info*/
+
+ /* configuration to use on next enable */
+ uint32_t buffer_size; /* Frame size (36 bytes) */
+ uint32_t enc_type;
+
+ int dtx_mode;
+ uint32_t frame_format;
+ uint32_t used_mode;
+ uint32_t rec_mode;
+
+ uint32_t dsp_cnt;
+ uint32_t in_head; /* next buffer dsp will write */
+ uint32_t in_tail; /* next buffer read() will read */
+ uint32_t in_count; /* number of buffers available to read() */
+ uint32_t mode;
+
+ const char *module_name;
+ unsigned queue_ids;
+ uint16_t enc_id;
+
+ uint16_t source; /* Encoding source bit mask */
+ uint32_t device_events;
+ uint32_t in_call;
+ uint32_t dev_cnt;
+ int voice_state;
+ spinlock_t dev_lock;
+
+ /* data allocated for various buffers */
+ char *data;
+ dma_addr_t phys;
+
+ int opened;
+ int enabled;
+ int running;
+ int stopped; /* set when stopped, cleared on flush */
+};
+
+struct audio_frame {
+ uint16_t frame_count_lsw;
+ uint16_t frame_count_msw;
+ uint16_t frame_length;
+ uint16_t erased_pcm;
+ unsigned char raw_bitstream[]; /* samples */
+} __attribute__((packed));
+
+/* Audrec Queue command sent macro's */
+#define audrec_send_bitstreamqueue(audio, cmd, len) \
+ msm_adsp_write(audio->audrec, ((audio->queue_ids & 0xFFFF0000) >> 16),\
+ cmd, len)
+
+#define audrec_send_audrecqueue(audio, cmd, len) \
+ msm_adsp_write(audio->audrec, (audio->queue_ids & 0x0000FFFF),\
+ cmd, len)
+
+struct audio_in the_audio_amrnb_in;
+
+/* DSP command send functions */
+static int audamrnb_in_enc_config(struct audio_in *audio, int enable);
+static int audamrnb_in_param_config(struct audio_in *audio);
+static int audamrnb_in_mem_config(struct audio_in *audio);
+static int audamrnb_in_record_config(struct audio_in *audio, int enable);
+static int audamrnb_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt);
+
+static void audamrnb_in_get_dsp_frames(struct audio_in *audio);
+
+static void audamrnb_in_flush(struct audio_in *audio);
+
+static void amrnb_in_listener(u32 evt_id, union auddev_evt_data *evt_payload,
+ void *private_data)
+{
+ struct audio_in *audio = (struct audio_in *) private_data;
+ unsigned long flags;
+
+ MM_DBG("evt_id = 0x%8x\n", evt_id);
+ switch (evt_id) {
+ case AUDDEV_EVT_DEV_RDY: {
+ MM_DBG("AUDDEV_EVT_DEV_RDY\n");
+ spin_lock_irqsave(&audio->dev_lock, flags);
+ audio->dev_cnt++;
+ if (!audio->in_call)
+ audio->source |= (0x1 << evt_payload->routing_id);
+ spin_unlock_irqrestore(&audio->dev_lock, flags);
+
+ if ((audio->running == 1) && (audio->enabled == 1))
+ audamrnb_in_record_config(audio, 1);
+
+ break;
+ }
+ case AUDDEV_EVT_DEV_RLS: {
+ MM_DBG("AUDDEV_EVT_DEV_RLS\n");
+ spin_lock_irqsave(&audio->dev_lock, flags);
+ audio->dev_cnt--;
+ if (!audio->in_call)
+ audio->source &= ~(0x1 << evt_payload->routing_id);
+ spin_unlock_irqrestore(&audio->dev_lock, flags);
+
+ if ((!audio->running) || (!audio->enabled))
+ break;
+
+ /* Turn of as per source */
+ if (audio->source)
+ audamrnb_in_record_config(audio, 1);
+ else
+ /* Turn off all */
+ audamrnb_in_record_config(audio, 0);
+
+ break;
+ }
+ case AUDDEV_EVT_VOICE_STATE_CHG: {
+ MM_DBG("AUDDEV_EVT_VOICE_STATE_CHG, state = %d\n",
+ evt_payload->voice_state);
+ audio->voice_state = evt_payload->voice_state;
+ if (audio->in_call && audio->running) {
+ if (audio->voice_state == VOICE_STATE_INCALL)
+ audamrnb_in_record_config(audio, 1);
+ else if (audio->voice_state == VOICE_STATE_OFFCALL) {
+ audamrnb_in_record_config(audio, 0);
+ wake_up(&audio->wait);
+ }
+ }
+
+ break;
+ }
+ default:
+ MM_ERR("wrong event %d\n", evt_id);
+ break;
+ }
+}
+
+/* ------------------- dsp preproc event handler--------------------- */
+static void audpreproc_dsp_event(void *data, unsigned id, void *msg)
+{
+ struct audio_in *audio = data;
+
+ switch (id) {
+ case AUDPREPROC_ERROR_MSG: {
+ struct audpreproc_err_msg *err_msg = msg;
+
+ MM_ERR("ERROR_MSG: stream id %d err idx %d\n",
+ err_msg->stream_id, err_msg->aud_preproc_err_idx);
+ /* Error case */
+ wake_up(&audio->wait_enable);
+ break;
+ }
+ case AUDPREPROC_CMD_CFG_DONE_MSG: {
+ MM_DBG("CMD_CFG_DONE_MSG \n");
+ break;
+ }
+ case AUDPREPROC_CMD_ENC_CFG_DONE_MSG: {
+ struct audpreproc_cmd_enc_cfg_done_msg *enc_cfg_msg = msg;
+
+ MM_DBG("CMD_ENC_CFG_DONE_MSG: stream id %d enc type \
+ 0x%8x\n", enc_cfg_msg->stream_id,
+ enc_cfg_msg->rec_enc_type);
+ /* Encoder enable success */
+ if (enc_cfg_msg->rec_enc_type & ENCODE_ENABLE)
+ audamrnb_in_param_config(audio);
+ else { /* Encoder disable success */
+ audio->running = 0;
+ audamrnb_in_record_config(audio, 0);
+ }
+ break;
+ }
+ case AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG: {
+ MM_DBG("CMD_ENC_PARAM_CFG_DONE_MSG \n");
+ audamrnb_in_mem_config(audio);
+ break;
+ }
+ case AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG: {
+ MM_DBG("AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG \n");
+ wake_up(&audio->wait_enable);
+ break;
+ }
+ default:
+ MM_ERR("Unknown Event id %d\n", id);
+ }
+}
+
+/* ------------------- dsp audrec event handler--------------------- */
+static void audrec_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent)(void *ptr, size_t len))
+{
+ struct audio_in *audio = data;
+
+ switch (id) {
+ case AUDREC_CMD_MEM_CFG_DONE_MSG: {
+ MM_DBG("CMD_MEM_CFG_DONE MSG DONE\n");
+ audio->running = 1;
+ if ((!audio->in_call && (audio->dev_cnt > 0)) ||
+ (audio->in_call &&
+ (audio->voice_state == VOICE_STATE_INCALL)))
+ audamrnb_in_record_config(audio, 1);
+ break;
+ }
+ case AUDREC_FATAL_ERR_MSG: {
+ struct audrec_fatal_err_msg fatal_err_msg;
+
+ getevent(&fatal_err_msg, AUDREC_FATAL_ERR_MSG_LEN);
+ MM_ERR("FATAL_ERR_MSG: err id %d\n",
+ fatal_err_msg.audrec_err_id);
+ /* Error stop the encoder */
+ audio->stopped = 1;
+ wake_up(&audio->wait);
+ break;
+ }
+ case AUDREC_UP_PACKET_READY_MSG: {
+ struct audrec_up_pkt_ready_msg pkt_ready_msg;
+
+ getevent(&pkt_ready_msg, AUDREC_UP_PACKET_READY_MSG_LEN);
+ MM_DBG("UP_PACKET_READY_MSG: write cnt lsw %d \
+ write cnt msw %d read cnt lsw %d read cnt msw %d \n",\
+ pkt_ready_msg.audrec_packet_write_cnt_lsw, \
+ pkt_ready_msg.audrec_packet_write_cnt_msw, \
+ pkt_ready_msg.audrec_up_prev_read_cnt_lsw, \
+ pkt_ready_msg.audrec_up_prev_read_cnt_msw);
+
+ audamrnb_in_get_dsp_frames(audio);
+ break;
+ }
+ case ADSP_MESSAGE_ID: {
+ MM_DBG("Received ADSP event:module audrectask\n");
+ break;
+ }
+ default:
+ MM_ERR("Unknown Event id %d\n", id);
+ }
+}
+
+static void audamrnb_in_get_dsp_frames(struct audio_in *audio)
+{
+ struct audio_frame *frame;
+ uint32_t index;
+ unsigned long flags;
+
+ index = audio->in_head;
+
+ frame = (void *) (((char *)audio->in[index].data) - \
+ sizeof(*frame));
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->in[index].size = frame->frame_length;
+
+ /* statistics of read */
+ atomic_add(audio->in[index].size, &audio->in_bytes);
+ atomic_add(1, &audio->in_samples);
+
+ audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1);
+
+ /* If overflow, move the tail index foward. */
+ if (audio->in_head == audio->in_tail)
+ audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1);
+ else
+ audio->in_count++;
+
+ audamrnb_dsp_read_buffer(audio, audio->dsp_cnt++);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+
+ wake_up(&audio->wait);
+}
+struct msm_adsp_ops audrec_amrnb_adsp_ops = {
+ .event = audrec_dsp_event,
+};
+
+static int audamrnb_in_enc_config(struct audio_in *audio, int enable)
+{
+ struct audpreproc_audrec_cmd_enc_cfg cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ENC_CFG_2;
+ cmd.stream_id = audio->enc_id;
+
+ if (enable)
+ cmd.audrec_enc_type = audio->enc_type | ENCODE_ENABLE;
+ else
+ cmd.audrec_enc_type &= ~(ENCODE_ENABLE);
+
+ return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+static int audamrnb_in_param_config(struct audio_in *audio)
+{
+ struct audpreproc_audrec_cmd_parm_cfg_amrnb cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.cmd_id = AUDPREPROC_AUDREC_CMD_PARAM_CFG;
+ cmd.common.stream_id = audio->enc_id;
+
+ cmd.dtx_mode = audio->dtx_mode;
+ cmd.test_mode = -1; /* Default set to -1 */
+ cmd.used_mode = audio->used_mode;
+
+ return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+/* To Do: msm_snddev_route_enc(audio->enc_id); */
+static int audamrnb_in_record_config(struct audio_in *audio, int enable)
+{
+ struct audpreproc_afe_cmd_audio_record_cfg cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG;
+ cmd.stream_id = audio->enc_id;
+ if (enable)
+ cmd.destination_activity = AUDIO_RECORDING_TURN_ON;
+ else
+ cmd.destination_activity = AUDIO_RECORDING_TURN_OFF;
+
+ cmd.source_mix_mask = audio->source;
+ if (audio->enc_id == 2) {
+ if ((cmd.source_mix_mask &
+ INTERNAL_CODEC_TX_SOURCE_MIX_MASK) ||
+ (cmd.source_mix_mask & AUX_CODEC_TX_SOURCE_MIX_MASK) ||
+ (cmd.source_mix_mask & VOICE_UL_SOURCE_MIX_MASK) ||
+ (cmd.source_mix_mask & VOICE_DL_SOURCE_MIX_MASK)) {
+ cmd.pipe_id = SOURCE_PIPE_1;
+ }
+ if (cmd.source_mix_mask &
+ AUDPP_A2DP_PIPE_SOURCE_MIX_MASK)
+ cmd.pipe_id |= SOURCE_PIPE_0;
+ }
+
+ return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+static int audamrnb_in_mem_config(struct audio_in *audio)
+{
+ struct audrec_cmd_arecmem_cfg cmd;
+ uint16_t *data = (void *) audio->data;
+ int n;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDREC_CMD_MEM_CFG_CMD;
+ cmd.audrec_up_pkt_intm_count = 1;
+ cmd.audrec_ext_pkt_start_addr_msw = audio->phys >> 16;
+ cmd.audrec_ext_pkt_start_addr_lsw = audio->phys;
+ cmd.audrec_ext_pkt_buf_number = FRAME_NUM;
+
+ /* prepare buffer pointers:
+ * 36 bytes amrnb packet + 4 halfword header
+ */
+ for (n = 0; n < FRAME_NUM; n++) {
+ audio->in[n].data = data + 4;
+ data += (FRAME_SIZE/2); /* word increment */
+ MM_DBG("0x%8x\n", (int)(audio->in[n].data - 8));
+ }
+
+ return audrec_send_audrecqueue(audio, &cmd, sizeof(cmd));
+}
+
+static int audamrnb_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt)
+{
+ struct up_audrec_packet_ext_ptr cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = UP_AUDREC_PACKET_EXT_PTR;
+ cmd.audrec_up_curr_read_count_msw = read_cnt >> 16;
+ cmd.audrec_up_curr_read_count_lsw = read_cnt;
+
+ return audrec_send_bitstreamqueue(audio, &cmd, sizeof(cmd));
+}
+
+/* must be called with audio->lock held */
+static int audamrnb_in_enable(struct audio_in *audio)
+{
+ if (audio->enabled)
+ return 0;
+
+ if (audpreproc_enable(audio->enc_id, &audpreproc_dsp_event, audio)) {
+ MM_ERR("msm_adsp_enable(audpreproc) failed\n");
+ return -ENODEV;
+ }
+
+ if (msm_adsp_enable(audio->audrec)) {
+ MM_ERR("msm_adsp_enable(audrec) failed\n");
+ audpreproc_disable(audio->enc_id, audio);
+ return -ENODEV;
+ }
+ audio->enabled = 1;
+ audamrnb_in_enc_config(audio, 1);
+
+ return 0;
+}
+
+/* must be called with audio->lock held */
+static int audamrnb_in_disable(struct audio_in *audio)
+{
+ if (audio->enabled) {
+ audio->enabled = 0;
+ audamrnb_in_enc_config(audio, 0);
+ wake_up(&audio->wait);
+ wait_event_interruptible_timeout(audio->wait_enable,
+ audio->running == 0, 1*HZ);
+ msm_adsp_disable(audio->audrec);
+ audpreproc_disable(audio->enc_id, audio);
+ }
+ return 0;
+}
+
+static void audamrnb_in_flush(struct audio_in *audio)
+{
+ int i;
+
+ audio->dsp_cnt = 0;
+ audio->in_head = 0;
+ audio->in_tail = 0;
+ audio->in_count = 0;
+ for (i = 0; i < FRAME_NUM; i++) {
+ audio->in[i].size = 0;
+ audio->in[i].read = 0;
+ }
+ MM_DBG("in_bytes %d\n", atomic_read(&audio->in_bytes));
+ MM_DBG("in_samples %d\n", atomic_read(&audio->in_samples));
+ atomic_set(&audio->in_bytes, 0);
+ atomic_set(&audio->in_samples, 0);
+}
+
+/* ------------------- device --------------------- */
+static long audamrnb_in_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct audio_in *audio = file->private_data;
+ int rc = 0;
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ stats.byte_count = atomic_read(&audio->in_bytes);
+ stats.sample_count = atomic_read(&audio->in_samples);
+ if (copy_to_user((void *) arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return rc;
+ }
+
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START: {
+ uint32_t freq;
+ freq = 48000;
+ MM_DBG("AUDIO_START\n");
+ if (audio->in_call && (audio->voice_state !=
+ VOICE_STATE_INCALL)) {
+ rc = -EPERM;
+ break;
+ }
+ rc = msm_snddev_request_freq(&freq, audio->enc_id,
+ SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+ MM_DBG("sample rate configured %d\n", freq);
+ if (rc < 0) {
+ MM_DBG(" Sample rate can not be set, return code %d\n",
+ rc);
+ msm_snddev_withdraw_freq(audio->enc_id,
+ SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+ MM_DBG("msm_snddev_withdraw_freq\n");
+ break;
+ }
+ /*update aurec session info in audpreproc layer*/
+ audio->session_info.session_id = audio->enc_id;
+ /*amrnb works only on 8KHz*/
+ audio->session_info.sampling_freq = 8000;
+ audpreproc_update_audrec_info(&audio->session_info);
+ rc = audamrnb_in_enable(audio);
+ if (!rc) {
+ rc =
+ wait_event_interruptible_timeout(audio->wait_enable,
+ audio->running != 0, 1*HZ);
+ MM_DBG("state %d rc = %d\n", audio->running, rc);
+
+ if (audio->running == 0)
+ rc = -ENODEV;
+ else
+ rc = 0;
+ }
+ audio->stopped = 0;
+ break;
+ }
+ case AUDIO_STOP: {
+ /*reset the sampling frequency information at audpreproc layer*/
+ audio->session_info.sampling_freq = 0;
+ audpreproc_update_audrec_info(&audio->session_info);
+ rc = audamrnb_in_disable(audio);
+ rc = msm_snddev_withdraw_freq(audio->enc_id,
+ SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+ MM_DBG("msm_snddev_withdraw_freq\n");
+ audio->stopped = 1;
+ break;
+ }
+ case AUDIO_FLUSH: {
+ if (audio->stopped) {
+ /* Make sure we're stopped and we wake any threads
+ * that might be blocked holding the read_lock.
+ * While audio->stopped read threads will always
+ * exit immediately.
+ */
+ wake_up(&audio->wait);
+ mutex_lock(&audio->read_lock);
+ audamrnb_in_flush(audio);
+ mutex_unlock(&audio->read_lock);
+ }
+ break;
+ }
+ case AUDIO_SET_STREAM_CONFIG: {
+ struct msm_audio_stream_config cfg;
+ if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) {
+ rc = -EFAULT;
+ break;
+ }
+ /* Allow only single frame */
+ if (cfg.buffer_size != (FRAME_SIZE - 8))
+ rc = -EINVAL;
+ else
+ audio->buffer_size = cfg.buffer_size;
+ break;
+ }
+ case AUDIO_GET_STREAM_CONFIG: {
+ struct msm_audio_stream_config cfg;
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.buffer_size = audio->buffer_size;
+ cfg.buffer_count = FRAME_NUM;
+ if (copy_to_user((void *) arg, &cfg, sizeof(cfg)))
+ rc = -EFAULT;
+ break;
+ }
+ case AUDIO_GET_AMRNB_ENC_CONFIG_V2: {
+ struct msm_audio_amrnb_enc_config_v2 cfg;
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.dtx_enable = ((audio->dtx_mode == -1) ? 1 : 0);
+ cfg.band_mode = audio->used_mode;
+ cfg.frame_format = audio->frame_format;
+ if (copy_to_user((void *) arg, &cfg, sizeof(cfg)))
+ rc = -EFAULT;
+ break;
+ }
+ case AUDIO_SET_AMRNB_ENC_CONFIG_V2: {
+ struct msm_audio_amrnb_enc_config_v2 cfg;
+ if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) {
+ rc = -EFAULT;
+ break;
+ }
+ /* DSP does not support any other than default format */
+ if (audio->frame_format != cfg.frame_format) {
+ rc = -EINVAL;
+ break;
+ }
+ if (cfg.dtx_enable == 0)
+ audio->dtx_mode = 0;
+ else if (cfg.dtx_enable == 1)
+ audio->dtx_mode = -1;
+ else {
+ rc = -EINVAL;
+ break;
+ }
+ audio->used_mode = cfg.band_mode;
+ break;
+ }
+ case AUDIO_SET_INCALL: {
+ struct msm_voicerec_mode cfg;
+ unsigned long flags;
+ if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (cfg.rec_mode != VOC_REC_BOTH &&
+ cfg.rec_mode != VOC_REC_UPLINK &&
+ cfg.rec_mode != VOC_REC_DOWNLINK) {
+ MM_ERR("invalid rec_mode\n");
+ rc = -EINVAL;
+ break;
+ } else {
+ spin_lock_irqsave(&audio->dev_lock, flags);
+ if (cfg.rec_mode == VOC_REC_UPLINK)
+ audio->source = VOICE_UL_SOURCE_MIX_MASK;
+ else if (cfg.rec_mode == VOC_REC_DOWNLINK)
+ audio->source = VOICE_DL_SOURCE_MIX_MASK;
+ else
+ audio->source = VOICE_DL_SOURCE_MIX_MASK |
+ VOICE_UL_SOURCE_MIX_MASK ;
+ audio->in_call = 1;
+ spin_unlock_irqrestore(&audio->dev_lock, flags);
+ }
+ break;
+ }
+ case AUDIO_GET_SESSION_ID: {
+ if (copy_to_user((void *) arg, &audio->enc_id,
+ sizeof(unsigned short))) {
+ rc = -EFAULT;
+ }
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static ssize_t audamrnb_in_read(struct file *file,
+ char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio_in *audio = file->private_data;
+ unsigned long flags;
+ const char __user *start = buf;
+ void *data;
+ uint32_t index;
+ uint32_t size;
+ int rc = 0;
+
+ mutex_lock(&audio->read_lock);
+ while (count > 0) {
+ rc = wait_event_interruptible(
+ audio->wait, (audio->in_count > 0) || audio->stopped
+ || (audio->in_call && audio->running &&
+ (audio->voice_state == VOICE_STATE_OFFCALL)));
+ if (rc < 0)
+ break;
+
+ if (!audio->in_count) {
+ if (audio->stopped) {
+ rc = 0;/* End of File */
+ break;
+ } else if (audio->in_call && audio->running &&
+ (audio->voice_state == VOICE_STATE_OFFCALL)) {
+ MM_DBG("Not Permitted Voice Terminated\n");
+ rc = -EPERM; /* Voice Call stopped */
+ break;
+ }
+ }
+
+ index = audio->in_tail;
+ data = (uint8_t *) audio->in[index].data;
+ size = audio->in[index].size;
+ if (count >= size) {
+ if (copy_to_user(buf, data, size)) {
+ rc = -EFAULT;
+ break;
+ }
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (index != audio->in_tail) {
+ /* overrun -- data is
+ * invalid and we need to retry */
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ continue;
+ }
+ audio->in[index].size = 0;
+ audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1);
+ audio->in_count--;
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ count -= size;
+ buf += size;
+ } else {
+ MM_ERR("short read\n");
+ break;
+ }
+ }
+ mutex_unlock(&audio->read_lock);
+
+ if (buf > start)
+ return buf - start;
+
+ return rc;
+}
+
+static ssize_t audamrnb_in_write(struct file *file,
+ const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ return -EINVAL;
+}
+
+static int audamrnb_in_release(struct inode *inode, struct file *file)
+{
+ struct audio_in *audio = file->private_data;
+
+ MM_DBG("\n");
+ mutex_lock(&audio->lock);
+ audio->in_call = 0;
+ /* with draw frequency for session
+ incase not stopped the driver */
+ msm_snddev_withdraw_freq(audio->enc_id, SNDDEV_CAP_TX,
+ AUDDEV_CLNT_ENC);
+ auddev_unregister_evt_listner(AUDDEV_CLNT_ENC, audio->enc_id);
+ /*reset the sampling frequency information at audpreproc layer*/
+ audio->session_info.sampling_freq = 0;
+ audpreproc_update_audrec_info(&audio->session_info);
+ audamrnb_in_disable(audio);
+ audamrnb_in_flush(audio);
+ msm_adsp_put(audio->audrec);
+ audpreproc_aenc_free(audio->enc_id);
+ audio->audrec = NULL;
+ audio->opened = 0;
+ if (audio->data) {
+ iounmap(audio->data);
+ pmem_kfree(audio->phys);
+ audio->data = NULL;
+ }
+ mutex_unlock(&audio->lock);
+ return 0;
+}
+
+static int audamrnb_in_open(struct inode *inode, struct file *file)
+{
+ struct audio_in *audio = &the_audio_amrnb_in;
+ int rc;
+ int encid;
+
+ mutex_lock(&audio->lock);
+ if (audio->opened) {
+ rc = -EBUSY;
+ goto done;
+ }
+ audio->phys = pmem_kalloc(DMASZ, PMEM_MEMTYPE_EBI1|
+ PMEM_ALIGNMENT_4K);
+ if (!IS_ERR((void *)audio->phys)) {
+ audio->data = ioremap(audio->phys, DMASZ);
+ if (!audio->data) {
+ MM_ERR("could not allocate DMA buffers\n");
+ rc = -ENOMEM;
+ pmem_kfree(audio->phys);
+ goto done;
+ }
+ } else {
+ MM_ERR("could not allocate DMA buffers\n");
+ rc = -ENOMEM;
+ goto done;
+ }
+ MM_DBG("Memory addr = 0x%8x phy addr = 0x%8x\n",\
+ (int) audio->data, (int) audio->phys);
+ if ((file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ rc = -EACCES;
+ MM_ERR("Non tunnel encoding is not supported\n");
+ goto done;
+ } else if (!(file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ audio->mode = MSM_AUD_ENC_MODE_TUNNEL;
+ MM_DBG("Opened for tunnel mode encoding\n");
+ } else {
+ rc = -EACCES;
+ goto done;
+ }
+
+
+ /* Settings will be re-config at AUDIO_SET_CONFIG,
+ * but at least we need to have initial config
+ */
+ audio->buffer_size = (FRAME_SIZE - 8);
+ audio->enc_type = ENC_TYPE_AMRNB | audio->mode;
+ audio->dtx_mode = -1;
+ audio->frame_format = 0;
+ audio->used_mode = 7; /* Bit Rate 12.2 kbps MR122 */
+
+ encid = audpreproc_aenc_alloc(audio->enc_type, &audio->module_name,
+ &audio->queue_ids);
+ if (encid < 0) {
+ MM_ERR("No free encoder available\n");
+ rc = -ENODEV;
+ goto done;
+ }
+ audio->enc_id = encid;
+
+ rc = msm_adsp_get(audio->module_name, &audio->audrec,
+ &audrec_amrnb_adsp_ops, audio);
+
+ if (rc) {
+ audpreproc_aenc_free(audio->enc_id);
+ goto done;
+ }
+
+ audio->stopped = 0;
+ audio->source = 0;
+
+ audamrnb_in_flush(audio);
+
+ audio->device_events = AUDDEV_EVT_DEV_RDY | AUDDEV_EVT_DEV_RLS |
+ AUDDEV_EVT_VOICE_STATE_CHG;
+
+ audio->voice_state = msm_get_voice_state();
+ rc = auddev_register_evt_listner(audio->device_events,
+ AUDDEV_CLNT_ENC, audio->enc_id,
+ amrnb_in_listener, (void *) audio);
+ if (rc) {
+ MM_ERR("failed to register device event listener\n");
+ goto evt_error;
+ }
+ file->private_data = audio;
+ audio->opened = 1;
+done:
+ mutex_unlock(&audio->lock);
+ return rc;
+evt_error:
+ msm_adsp_put(audio->audrec);
+ audpreproc_aenc_free(audio->enc_id);
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static const struct file_operations audio_in_fops = {
+ .owner = THIS_MODULE,
+ .open = audamrnb_in_open,
+ .release = audamrnb_in_release,
+ .read = audamrnb_in_read,
+ .write = audamrnb_in_write,
+ .unlocked_ioctl = audamrnb_in_ioctl,
+};
+
+struct miscdevice audio_amrnb_in_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_amrnb_in",
+ .fops = &audio_in_fops,
+};
+
+static int __init audamrnb_in_init(void)
+{
+ mutex_init(&the_audio_amrnb_in.lock);
+ mutex_init(&the_audio_amrnb_in.read_lock);
+ spin_lock_init(&the_audio_amrnb_in.dsp_lock);
+ spin_lock_init(&the_audio_amrnb_in.dev_lock);
+ init_waitqueue_head(&the_audio_amrnb_in.wait);
+ init_waitqueue_head(&the_audio_amrnb_in.wait_enable);
+ return misc_register(&audio_amrnb_in_misc);
+}
+
+device_initcall(audamrnb_in_init);
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_amrwb.c b/arch/arm/mach-msm/qdsp5v2/audio_amrwb.c
new file mode 100644
index 0000000..4793e6e
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_amrwb.c
@@ -0,0 +1,1712 @@
+/* amrwb audio decoder device
+ *
+ * Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Based on the mp3 native driver in arch/arm/mach-msm/qdsp5v2/audio_mp3.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ *
+ * All source code in this file is licensed under the following license except
+ * where indicated.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/earlysuspend.h>
+#include <linux/android_pmem.h>
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+#include <linux/msm_audio.h>
+#include <linux/slab.h>
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/qdsp5audplaycmdi.h>
+#include <mach/qdsp5v2/qdsp5audplaymsg.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/qdsp5v2/audpp.h>
+#include <mach/debug_mm.h>
+
+#define BUFSZ 4110 /* Hold minimum 700ms voice data and 14 bytes of meta in*/
+#define DMASZ (BUFSZ * 2)
+
+#define AUDPLAY_INVALID_READ_PTR_OFFSET 0xFFFF
+#define AUDDEC_DEC_AMRWB 11
+
+#define PCM_BUFSZ_MIN 8216 /* 100ms worth of data and 24 bytes of meta out*/
+#define PCM_BUF_MAX_COUNT 5 /* DSP only accepts 5 buffers at most
+ but support 2 buffers currently */
+#define ROUTING_MODE_FTRT 1
+#define ROUTING_MODE_RT 2
+/* Decoder status received from AUDPPTASK */
+#define AUDPP_DEC_STATUS_SLEEP 0
+#define AUDPP_DEC_STATUS_INIT 1
+#define AUDPP_DEC_STATUS_CFG 2
+#define AUDPP_DEC_STATUS_PLAY 3
+
+#define AUDAMRWB_METAFIELD_MASK 0xFFFF0000
+#define AUDAMRWB_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */
+#define AUDAMRWB_EOS_FLG_MASK 0x01
+#define AUDAMRWB_EOS_NONE 0x0 /* No EOS detected */
+#define AUDAMRWB_EOS_SET 0x1 /* EOS set in meta field */
+
+#define AUDAMRWB_EVENT_NUM 10 /* Default number of pre-allocated event pkts */
+
+struct buffer {
+ void *data;
+ unsigned size;
+ unsigned used; /* Input usage actual DSP produced PCM size */
+ unsigned addr;
+ unsigned short mfield_sz; /*only useful for data has meta field */
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+struct audamrwb_suspend_ctl {
+ struct early_suspend node;
+ struct audio *audio;
+};
+#endif
+
+struct audamrwb_event{
+ struct list_head list;
+ int event_type;
+ union msm_audio_event_payload payload;
+};
+
+struct audio {
+ struct buffer out[2];
+
+ spinlock_t dsp_lock;
+
+ uint8_t out_head;
+ uint8_t out_tail;
+ uint8_t out_needed; /* number of buffers the dsp is waiting for */
+
+ atomic_t out_bytes;
+
+ struct mutex lock;
+ struct mutex write_lock;
+ wait_queue_head_t write_wait;
+
+ /* Host PCM section */
+ struct buffer in[PCM_BUF_MAX_COUNT];
+ struct mutex read_lock;
+ wait_queue_head_t read_wait; /* Wait queue for read */
+ char *read_data; /* pointer to reader buffer */
+ int32_t read_phys; /* physical address of reader buffer */
+ uint8_t read_next; /* index to input buffers to be read next */
+ uint8_t fill_next; /* index to buffer that DSP should be filling */
+ uint8_t pcm_buf_count; /* number of pcm buffer allocated */
+ /* ---- End of Host PCM section */
+
+ struct msm_adsp_module *audplay;
+
+ /* configuration to use on next enable */
+ uint32_t out_sample_rate;
+ uint32_t out_channel_mode;
+
+ /* data allocated for various buffers */
+ char *data;
+ int32_t phys; /* physical address of write buffer */
+
+ int mfield; /* meta field embedded in data */
+ int rflush; /* Read flush */
+ int wflush; /* Write flush */
+ int opened;
+ int enabled;
+ int running;
+ int stopped; /* set when stopped, cleared on flush */
+ int pcm_feedback;
+ int buf_refresh;
+ int teos; /* valid only if tunnel mode & no data left for decoder */
+ enum msm_aud_decoder_state dec_state; /* Represents decoder state */
+ int reserved; /* A byte is being reserved */
+ char rsv_byte; /* Handle odd length user data */
+
+ const char *module_name;
+ unsigned queue_id;
+ uint16_t dec_id;
+ uint32_t read_ptr_offset;
+ int16_t source;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct audamrwb_suspend_ctl suspend_ctl;
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dentry;
+#endif
+
+ wait_queue_head_t wait;
+ struct list_head free_event_queue;
+ struct list_head event_queue;
+ wait_queue_head_t event_wait;
+ spinlock_t event_queue_lock;
+ struct mutex get_event_lock;
+ int event_abort;
+ /* AV sync Info */
+ int avsync_flag; /* Flag to indicate feedback from DSP */
+ wait_queue_head_t avsync_wait;/* Wait queue for AV Sync Message */
+ /* flags, 48 bits sample/bytes counter per channel */
+ uint16_t avsync[AUDPP_AVSYNC_CH_COUNT * AUDPP_AVSYNC_NUM_WORDS + 1];
+
+ uint32_t device_events;
+
+ int eq_enable;
+ int eq_needs_commit;
+ struct audpp_cmd_cfg_object_params_eqalizer eq;
+ struct audpp_cmd_cfg_object_params_volume vol_pan;
+};
+
+static int auddec_dsp_config(struct audio *audio, int enable);
+static void audpp_cmd_cfg_adec_params(struct audio *audio);
+static void audpp_cmd_cfg_routing_mode(struct audio *audio);
+static void audamrwb_send_data(struct audio *audio, unsigned needed);
+static void audamrwb_config_hostpcm(struct audio *audio);
+static void audamrwb_buffer_refresh(struct audio *audio);
+static void audamrwb_dsp_event(void *private, unsigned id, uint16_t *msg);
+static void audamrwb_post_event(struct audio *audio, int type,
+ union msm_audio_event_payload payload);
+
+/* must be called with audio->lock held */
+static int audamrwb_enable(struct audio *audio)
+{
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+
+ if (audio->enabled)
+ return 0;
+
+ audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+ audio->out_tail = 0;
+ audio->out_needed = 0;
+
+ if (msm_adsp_enable(audio->audplay)) {
+ MM_ERR("msm_adsp_enable(audplay) failed\n");
+ return -ENODEV;
+ }
+
+ if (audpp_enable(audio->dec_id, audamrwb_dsp_event, audio)) {
+ MM_ERR("audpp_enable() failed\n");
+ msm_adsp_disable(audio->audplay);
+ return -ENODEV;
+ }
+ audio->enabled = 1;
+ return 0;
+}
+
+static void amrwb_listner(u32 evt_id, union auddev_evt_data *evt_payload,
+ void *private_data)
+{
+ struct audio *audio = (struct audio *) private_data;
+ switch (evt_id) {
+ case AUDDEV_EVT_DEV_RDY:
+ MM_DBG("AUDDEV_EVT_DEV_RDY\n");
+ audio->source |= (0x1 << evt_payload->routing_id);
+ if (audio->running == 1 && audio->enabled == 1)
+ audpp_route_stream(audio->dec_id, audio->source);
+ break;
+ case AUDDEV_EVT_DEV_RLS:
+ MM_DBG("AUDDEV_EVT_DEV_RLS\n");
+ audio->source &= ~(0x1 << evt_payload->routing_id);
+ if (audio->running == 1 && audio->enabled == 1)
+ audpp_route_stream(audio->dec_id, audio->source);
+ break;
+ case AUDDEV_EVT_STREAM_VOL_CHG:
+ audio->vol_pan.volume = evt_payload->session_vol;
+ MM_DBG("AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n",
+ audio->vol_pan.volume);
+ if (audio->running)
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ break;
+ default:
+ MM_ERR("ERROR:wrong event\n");
+ break;
+ }
+}
+
+/* must be called with audio->lock held */
+static int audamrwb_disable(struct audio *audio)
+{
+ int rc = 0;
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ if (audio->enabled) {
+ audio->enabled = 0;
+ audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+ auddec_dsp_config(audio, 0);
+ rc = wait_event_interruptible_timeout(audio->wait,
+ audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+ msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE)
+ rc = -EFAULT;
+ else
+ rc = 0;
+ wake_up(&audio->write_wait);
+ wake_up(&audio->read_wait);
+ msm_adsp_disable(audio->audplay);
+ audpp_disable(audio->dec_id, audio);
+ audio->out_needed = 0;
+ }
+ return rc;
+}
+
+/* ------------------- dsp --------------------- */
+static void audamrwb_update_pcm_buf_entry(struct audio *audio,
+ uint32_t *payload)
+{
+ uint8_t index;
+ unsigned long flags;
+
+ if (audio->rflush)
+ return;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ for (index = 0; index < payload[1]; index++) {
+ if (audio->in[audio->fill_next].addr ==
+ payload[2 + index * 2]) {
+ MM_DBG("audamrwb_update_pcm_buf_entry: \
+ in[%d] ready\n", audio->fill_next);
+ audio->in[audio->fill_next].used =
+ payload[3 + index * 2];
+ if ((++audio->fill_next) == audio->pcm_buf_count)
+ audio->fill_next = 0;
+
+ } else {
+ MM_ERR("expected=%x ret=%x\n",
+ audio->in[audio->fill_next].addr,
+ payload[1 + index * 2]);
+ break;
+ }
+ }
+ if (audio->in[audio->fill_next].used == 0) {
+ audamrwb_buffer_refresh(audio);
+ } else {
+ MM_DBG("audamrwb_update_pcm_buf_entry: \
+ read cannot keep up\n");
+ audio->buf_refresh = 1;
+ }
+ wake_up(&audio->read_wait);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+static void audplay_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent) (void *ptr, size_t len))
+{
+ struct audio *audio = data;
+ uint32_t msg[28];
+ getevent(msg, sizeof(msg));
+
+ MM_DBG("audplay_dsp_event: msg_id=%x\n", id);
+
+ switch (id) {
+ case AUDPLAY_MSG_DEC_NEEDS_DATA:
+ audamrwb_send_data(audio, 1);
+ break;
+
+ case AUDPLAY_MSG_BUFFER_UPDATE:
+ audamrwb_update_pcm_buf_entry(audio, msg);
+ break;
+
+ case ADSP_MESSAGE_ID:
+ MM_DBG("Received ADSP event:module audplaytask\n");
+ break;
+
+ default:
+ MM_DBG("unexpected message from decoder\n");
+ }
+}
+
+static void audamrwb_dsp_event(void *private, unsigned id, uint16_t *msg)
+{
+ struct audio *audio = private;
+
+ switch (id) {
+ case AUDPP_MSG_STATUS_MSG:{
+ unsigned status = msg[1];
+
+ switch (status) {
+ case AUDPP_DEC_STATUS_SLEEP: {
+ uint16_t reason = msg[2];
+ MM_DBG("decoder status:sleep reason=0x%04x\n",
+ reason);
+ if ((reason == AUDPP_MSG_REASON_MEM)
+ || (reason ==
+ AUDPP_MSG_REASON_NODECODER)) {
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_FAILURE;
+ wake_up(&audio->wait);
+ } else if (reason == AUDPP_MSG_REASON_NONE) {
+ /* decoder is in disable state */
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_CLOSE;
+ wake_up(&audio->wait);
+ }
+ break;
+ }
+ case AUDPP_DEC_STATUS_INIT:
+ MM_DBG("decoder status: init\n");
+ if (audio->pcm_feedback)
+ audpp_cmd_cfg_routing_mode(audio);
+ else
+ audpp_cmd_cfg_adec_params(audio);
+ break;
+
+ case AUDPP_DEC_STATUS_CFG:
+ MM_DBG("decoder status: cfg\n");
+ break;
+ case AUDPP_DEC_STATUS_PLAY:
+ MM_DBG("decoder status: play\n");
+ /* send mixer command */
+ audpp_route_stream(audio->dec_id,
+ audio->source);
+ if (audio->pcm_feedback) {
+ audamrwb_config_hostpcm(audio);
+ audamrwb_buffer_refresh(audio);
+ }
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_SUCCESS;
+ wake_up(&audio->wait);
+ break;
+ default:
+ MM_DBG("unknown decoder status\n");
+ break;
+ }
+ break;
+ }
+ case AUDPP_MSG_CFG_MSG:
+ if (msg[0] == AUDPP_MSG_ENA_ENA) {
+ MM_DBG("CFG_MSG ENABLE\n");
+ auddec_dsp_config(audio, 1);
+ audio->out_needed = 0;
+ audio->running = 1;
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ audpp_dsp_set_eq(audio->dec_id, audio->eq_enable,
+ &audio->eq, POPP);
+ } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+ MM_DBG("CFG_MSG DISABLE\n");
+ audio->running = 0;
+ } else {
+ MM_DBG("CFG_MSG %d?\n", msg[0]);
+ }
+ break;
+ case AUDPP_MSG_ROUTING_ACK:
+ MM_DBG("ROUTING_ACK mode=%d\n", msg[1]);
+ audpp_cmd_cfg_adec_params(audio);
+ break;
+ case AUDPP_MSG_FLUSH_ACK:
+ MM_DBG("FLUSH_ACK\n");
+ audio->wflush = 0;
+ audio->rflush = 0;
+ wake_up(&audio->write_wait);
+ if (audio->pcm_feedback)
+ audamrwb_buffer_refresh(audio);
+ break;
+ case AUDPP_MSG_PCMDMAMISSED:
+ MM_DBG("PCMDMAMISSED\n");
+ audio->teos = 1;
+ wake_up(&audio->write_wait);
+ break;
+
+ case AUDPP_MSG_AVSYNC_MSG:
+ MM_DBG("AUDPP_MSG_AVSYNC_MSG\n");
+ memcpy(&audio->avsync[0], msg, sizeof(audio->avsync));
+ audio->avsync_flag = 1;
+ wake_up(&audio->avsync_wait);
+ break;
+
+ default:
+ MM_DBG("UNKNOWN (%d)\n", id);
+ }
+
+}
+
+struct msm_adsp_ops audplay_adsp_ops_amrwb = {
+ .event = audplay_dsp_event,
+};
+
+#define audplay_send_queue0(audio, cmd, len) \
+ msm_adsp_write(audio->audplay, audio->queue_id, \
+ cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+ struct audpp_cmd_cfg_dec_type cfg_dec_cmd;
+
+ memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd));
+
+ cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+ if (enable)
+ cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_AMRWB;
+ else
+ cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_DIS_DEC_V;
+ cfg_dec_cmd.dm_mode = 0x0;
+ cfg_dec_cmd.stream_id = audio->dec_id;
+ return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd));
+}
+
+static void audpp_cmd_cfg_adec_params(struct audio *audio)
+{
+ struct audpp_cmd_cfg_adec_params_amrwb cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+ cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_AMRWB_LEN;
+ cmd.common.dec_id = audio->dec_id;
+ cmd.common.input_sampling_frequency = audio->out_sample_rate;
+ cmd.stereo_cfg = audio->out_channel_mode;
+ audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_routing_mode(struct audio *audio)
+{
+ struct audpp_cmd_routing_mode cmd;
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_ROUTING_MODE;
+ cmd.object_number = audio->dec_id;
+ if (audio->pcm_feedback)
+ cmd.routing_mode = ROUTING_MODE_FTRT;
+ else
+ cmd.routing_mode = ROUTING_MODE_RT;
+
+ audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static int audplay_dsp_send_data_avail(struct audio *audio,
+ unsigned idx, unsigned len)
+{
+ struct audplay_cmd_bitstream_data_avail_nt2 cmd;
+
+ cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2;
+ if (audio->mfield)
+ cmd.decoder_id = AUDAMRWB_METAFIELD_MASK |
+ (audio->out[idx].mfield_sz >> 1);
+ else
+ cmd.decoder_id = audio->dec_id;
+ cmd.buf_ptr = audio->out[idx].addr;
+ cmd.buf_size = len / 2;
+ cmd.partition_number = 0;
+ return audplay_send_queue0(audio, &cmd, sizeof(cmd));
+}
+
+static void audamrwb_buffer_refresh(struct audio *audio)
+{
+ struct audplay_cmd_buffer_refresh refresh_cmd;
+
+ refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH;
+ refresh_cmd.num_buffers = 1;
+ refresh_cmd.buf0_address = audio->in[audio->fill_next].addr;
+ refresh_cmd.buf0_length = audio->in[audio->fill_next].size;
+ refresh_cmd.buf_read_count = 0;
+ MM_DBG("buf0_addr=%x buf0_len=%d\n", refresh_cmd.buf0_address,
+ refresh_cmd.buf0_length);
+ (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd));
+}
+
+static void audamrwb_config_hostpcm(struct audio *audio)
+{
+ struct audplay_cmd_hpcm_buf_cfg cfg_cmd;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG;
+ cfg_cmd.max_buffers = audio->pcm_buf_count;
+ cfg_cmd.byte_swap = 0;
+ cfg_cmd.hostpcm_config = (0x8000) | (0x4000);
+ cfg_cmd.feedback_frequency = 1;
+ cfg_cmd.partition_number = 0;
+ (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd));
+
+}
+
+static void audamrwb_send_data(struct audio *audio, unsigned needed)
+{
+ struct buffer *frame;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (!audio->running)
+ goto done;
+
+ if (needed && !audio->wflush) {
+ /* We were called from the callback because the DSP
+ * requested more data. Note that the DSP does want
+ * more data, and if a buffer was in-flight, mark it
+ * as available (since the DSP must now be done with
+ * it).
+ */
+ audio->out_needed = 1;
+ frame = audio->out + audio->out_tail;
+ if (frame->used == 0xffffffff) {
+ frame->used = 0;
+ audio->out_tail ^= 1;
+ wake_up(&audio->write_wait);
+ }
+ }
+
+ if (audio->out_needed) {
+ /* If the DSP currently wants data and we have a
+ * buffer available, we will send it and reset
+ * the needed flag. We'll mark the buffer as in-flight
+ * so that it won't be recycled until the next buffer
+ * is requested
+ */
+
+ frame = audio->out + audio->out_tail;
+ if (frame->used) {
+ BUG_ON(frame->used == 0xffffffff);
+ MM_DBG("frame %d busy\n", audio->out_tail);
+ audplay_dsp_send_data_avail(audio, audio->out_tail,
+ frame->used);
+ frame->used = 0xffffffff;
+ audio->out_needed = 0;
+ }
+ }
+ done:
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+/* ------------------- device --------------------- */
+
+static void audamrwb_flush(struct audio *audio)
+{
+ audio->out[0].used = 0;
+ audio->out[1].used = 0;
+ audio->out_head = 0;
+ audio->out_tail = 0;
+ audio->reserved = 0;
+ audio->out_needed = 0;
+ atomic_set(&audio->out_bytes, 0);
+}
+
+static void audamrwb_flush_pcm_buf(struct audio *audio)
+{
+ uint8_t index;
+
+ for (index = 0; index < PCM_BUF_MAX_COUNT; index++)
+ audio->in[index].used = 0;
+
+ audio->buf_refresh = 0;
+ audio->read_next = 0;
+ audio->fill_next = 0;
+}
+
+static void audamrwb_ioport_reset(struct audio *audio)
+{
+ /* Make sure read/write thread are free from
+ * sleep and knowing that system is not able
+ * to process io request at the moment
+ */
+ wake_up(&audio->write_wait);
+ mutex_lock(&audio->write_lock);
+ audamrwb_flush(audio);
+ mutex_unlock(&audio->write_lock);
+ wake_up(&audio->read_wait);
+ mutex_lock(&audio->read_lock);
+ audamrwb_flush_pcm_buf(audio);
+ mutex_unlock(&audio->read_lock);
+ audio->avsync_flag = 1;
+ wake_up(&audio->avsync_wait);
+}
+
+static int audamrwb_events_pending(struct audio *audio)
+{
+ unsigned long flags;
+ int empty;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ empty = !list_empty(&audio->event_queue);
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+ return empty || audio->event_abort;
+}
+
+static void audamrwb_reset_event_queue(struct audio *audio)
+{
+ unsigned long flags;
+ struct audamrwb_event *drv_evt;
+ struct list_head *ptr, *next;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ list_for_each_safe(ptr, next, &audio->event_queue) {
+ drv_evt = list_first_entry(&audio->event_queue,
+ struct audamrwb_event, list);
+ list_del(&drv_evt->list);
+ kfree(drv_evt);
+ }
+ list_for_each_safe(ptr, next, &audio->free_event_queue) {
+ drv_evt = list_first_entry(&audio->free_event_queue,
+ struct audamrwb_event, list);
+ list_del(&drv_evt->list);
+ kfree(drv_evt);
+ }
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+ return;
+}
+
+static long audamrwb_process_event_req(struct audio *audio, void __user *arg)
+{
+ long rc;
+ struct msm_audio_event usr_evt;
+ struct audamrwb_event *drv_evt = NULL;
+ int timeout;
+ unsigned long flags;
+
+ if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event)))
+ return -EFAULT;
+
+ timeout = (int) usr_evt.timeout_ms;
+
+ if (timeout > 0) {
+ rc = wait_event_interruptible_timeout(
+ audio->event_wait, audamrwb_events_pending(audio),
+ msecs_to_jiffies(timeout));
+ if (rc == 0)
+ return -ETIMEDOUT;
+ } else {
+ rc = wait_event_interruptible(
+ audio->event_wait, audamrwb_events_pending(audio));
+ }
+
+ if (rc < 0)
+ return rc;
+
+ if (audio->event_abort) {
+ audio->event_abort = 0;
+ return -ENODEV;
+ }
+
+ rc = 0;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ if (!list_empty(&audio->event_queue)) {
+ drv_evt = list_first_entry(&audio->event_queue,
+ struct audamrwb_event, list);
+ list_del(&drv_evt->list);
+ }
+
+ if (drv_evt) {
+ usr_evt.event_type = drv_evt->event_type;
+ usr_evt.event_payload = drv_evt->payload;
+ list_add_tail(&drv_evt->list, &audio->free_event_queue);
+ } else
+ rc = -1;
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+ if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt)))
+ rc = -EFAULT;
+
+ return rc;
+}
+
+static int audio_enable_eq(struct audio *audio, int enable)
+{
+ if (audio->eq_enable == enable && !audio->eq_needs_commit)
+ return 0;
+
+ audio->eq_enable = enable;
+
+ if (audio->running) {
+ audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq, POPP);
+ audio->eq_needs_commit = 0;
+ }
+ return 0;
+}
+
+static int audio_get_avsync_data(struct audio *audio,
+ struct msm_audio_stats *stats)
+{
+ int rc = -EINVAL;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) {
+ /* av_sync sample count */
+ stats->sample_count = (audio->avsync[2] << 16) |
+ (audio->avsync[3]);
+
+ /* av_sync byte_count */
+ stats->byte_count = (audio->avsync[5] << 16) |
+ (audio->avsync[6]);
+
+ audio->avsync_flag = 0;
+ rc = 0;
+ }
+ local_irq_restore(flags);
+ return rc;
+
+}
+
+static long audamrwb_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct audio *audio = file->private_data;
+ int rc = -EINVAL;
+ unsigned long flags = 0;
+ uint16_t enable_mask;
+ int enable;
+ int prev_state;
+
+ MM_DBG("cmd = %d\n", cmd);
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+
+ audio->avsync_flag = 0;
+ memset(&stats, 0, sizeof(stats));
+ if (audpp_query_avsync(audio->dec_id) < 0)
+ return rc;
+
+ rc = wait_event_interruptible_timeout(audio->avsync_wait,
+ (audio->avsync_flag == 1),
+ msecs_to_jiffies(AUDPP_AVSYNC_EVENT_TIMEOUT));
+
+ if (rc < 0)
+ return rc;
+ else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) {
+ if (audio_get_avsync_data(audio, &stats) < 0)
+ return rc;
+
+ if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ } else
+ return -EAGAIN;
+ }
+
+ switch (cmd) {
+ case AUDIO_ENABLE_AUDPP:
+ if (copy_from_user(&enable_mask, (void *) arg,
+ sizeof(enable_mask))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ enable = (enable_mask & EQ_ENABLE) ? 1 : 0;
+ audio_enable_eq(audio, enable);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ rc = 0;
+ break;
+ case AUDIO_SET_VOLUME:
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->vol_pan.volume = arg;
+ if (audio->running)
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ rc = 0;
+ break;
+
+ case AUDIO_SET_PAN:
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->vol_pan.pan = arg;
+ if (audio->running)
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ rc = 0;
+ break;
+
+ case AUDIO_SET_EQ:
+ prev_state = audio->eq_enable;
+ audio->eq_enable = 0;
+ if (copy_from_user(&audio->eq.num_bands, (void *) arg,
+ sizeof(audio->eq) -
+ (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) {
+ rc = -EFAULT;
+ break;
+ }
+ audio->eq_enable = prev_state;
+ audio->eq_needs_commit = 1;
+ rc = 0;
+ break;
+ }
+
+ if (-EINVAL != rc)
+ return rc;
+
+ if (cmd == AUDIO_GET_EVENT) {
+ MM_DBG("AUDIO_GET_EVENT\n");
+ if (mutex_trylock(&audio->get_event_lock)) {
+ rc = audamrwb_process_event_req(audio,
+ (void __user *) arg);
+ mutex_unlock(&audio->get_event_lock);
+ } else
+ rc = -EBUSY;
+ return rc;
+ }
+
+ if (cmd == AUDIO_ABORT_GET_EVENT) {
+ audio->event_abort = 1;
+ wake_up(&audio->event_wait);
+ return 0;
+ }
+
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START:
+ MM_DBG("AUDIO_START\n");
+ rc = audamrwb_enable(audio);
+ if (!rc) {
+ rc = wait_event_interruptible_timeout(audio->wait,
+ audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+ msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+ MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc);
+
+ if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS)
+ rc = -ENODEV;
+ else
+ rc = 0;
+ }
+ break;
+ case AUDIO_STOP:
+ MM_DBG("AUDIO_STOP\n");
+ rc = audamrwb_disable(audio);
+ audio->stopped = 1;
+ audamrwb_ioport_reset(audio);
+ audio->stopped = 0;
+ break;
+ case AUDIO_FLUSH:
+ MM_DBG("AUDIO_FLUSH\n");
+ audio->rflush = 1;
+ audio->wflush = 1;
+ audamrwb_ioport_reset(audio);
+ if (audio->running) {
+ audpp_flush(audio->dec_id);
+ rc = wait_event_interruptible(audio->write_wait,
+ !audio->wflush);
+ if (rc < 0) {
+ MM_ERR("AUDIO_FLUSH interrupted\n");
+ rc = -EINTR;
+ }
+ } else {
+ audio->rflush = 0;
+ audio->wflush = 0;
+ }
+ break;
+ case AUDIO_SET_CONFIG:{
+ struct msm_audio_config config;
+ if (copy_from_user
+ (&config, (void *)arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (config.channel_count == 1)
+ config.channel_count =
+ AUDPP_CMD_PCM_INTF_MONO_V;
+ else if (config.channel_count == 2)
+ config.channel_count =
+ AUDPP_CMD_PCM_INTF_STEREO_V;
+ else
+ rc = -EINVAL;
+ audio->out_channel_mode = config.channel_count;
+ audio->out_sample_rate = config.sample_rate;
+ audio->mfield = config.meta_field;
+ rc = 0;
+ break;
+ }
+ case AUDIO_GET_CONFIG:{
+ struct msm_audio_config config;
+ config.buffer_size = BUFSZ;
+ config.buffer_count = 2;
+ config.sample_rate = audio->out_sample_rate;
+ if (audio->out_channel_mode ==
+ AUDPP_CMD_PCM_INTF_MONO_V)
+ config.channel_count = 1;
+ else
+ config.channel_count = 2;
+ config.meta_field = 0;
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+ config.unused[2] = 0;
+ if (copy_to_user((void *)arg, &config,
+ sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+
+ break;
+ }
+ case AUDIO_GET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+ config.pcm_feedback = 0;
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+ config.buffer_size = PCM_BUFSZ_MIN;
+ if (copy_to_user((void *)arg, &config,
+ sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ case AUDIO_SET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+ if (copy_from_user
+ (&config, (void *)arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if ((config.buffer_count > PCM_BUF_MAX_COUNT) ||
+ (config.buffer_count == 1))
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+
+ if (config.buffer_size < PCM_BUFSZ_MIN)
+ config.buffer_size = PCM_BUFSZ_MIN;
+
+ /* Check if pcm feedback is required */
+ if ((config.pcm_feedback) && (!audio->read_data)) {
+ MM_DBG("allocate PCM buf %d\n", config.buffer_count *
+ config.buffer_size);
+ audio->read_phys = pmem_kalloc(
+ config.buffer_size *
+ config.buffer_count,
+ PMEM_MEMTYPE_EBI1|
+ PMEM_ALIGNMENT_4K);
+ if (IS_ERR((void *)audio->read_phys)) {
+ rc = -ENOMEM;
+ break;
+ }
+ audio->read_data = ioremap(audio->read_phys,
+ config.buffer_size *
+ config.buffer_count);
+ if (!audio->read_data) {
+ MM_ERR("no mem for read buf\n");
+ rc = -ENOMEM;
+ pmem_kfree(audio->read_phys);
+ } else {
+ uint8_t index;
+ uint32_t offset = 0;
+ audio->pcm_feedback = 1;
+ audio->buf_refresh = 0;
+ audio->pcm_buf_count =
+ config.buffer_count;
+ audio->read_next = 0;
+ audio->fill_next = 0;
+
+ for (index = 0;
+ index < config.buffer_count; index++) {
+ audio->in[index].data =
+ audio->read_data + offset;
+ audio->in[index].addr =
+ audio->read_phys + offset;
+ audio->in[index].size =
+ config.buffer_size;
+ audio->in[index].used = 0;
+ offset += config.buffer_size;
+ }
+ MM_DBG("read buf: phy addr 0x%08x \
+ kernel addr 0x%08x\n",
+ audio->read_phys,
+ (int)audio->read_data);
+ rc = 0;
+ }
+ } else {
+ rc = 0;
+ }
+ break;
+ }
+ case AUDIO_GET_SESSION_ID:
+ if (copy_to_user((void *) arg, &audio->dec_id,
+ sizeof(unsigned short)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+/* Only useful in tunnel-mode */
+static int audamrwb_fsync(struct file *file, int datasync)
+{
+ struct audio *audio = file->private_data;
+ struct buffer *frame;
+ int rc = 0;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+
+ if (!audio->running || audio->pcm_feedback) {
+ rc = -EINVAL;
+ goto done_nolock;
+ }
+
+ mutex_lock(&audio->write_lock);
+
+ rc = wait_event_interruptible(audio->write_wait,
+ (!audio->out[0].used &&
+ !audio->out[1].used &&
+ audio->out_needed) || audio->wflush);
+
+ if (rc < 0)
+ goto done;
+ else if (audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+
+ if (audio->reserved) {
+ MM_DBG("send reserved byte\n");
+ frame = audio->out + audio->out_tail;
+ ((char *) frame->data)[0] = audio->rsv_byte;
+ ((char *) frame->data)[1] = 0;
+ frame->used = 2;
+ audamrwb_send_data(audio, 0);
+
+ rc = wait_event_interruptible(audio->write_wait,
+ (!audio->out[0].used &&
+ !audio->out[1].used &&
+ audio->out_needed) || audio->wflush);
+
+ if (rc < 0)
+ goto done;
+ else if (audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+ }
+
+ /* pcm dmamiss message is sent continously
+ * when decoder is starved so no race
+ * condition concern
+ */
+ audio->teos = 0;
+
+ rc = wait_event_interruptible(audio->write_wait,
+ audio->teos || audio->wflush);
+
+ if (audio->wflush)
+ rc = -EBUSY;
+
+done:
+ mutex_unlock(&audio->write_lock);
+done_nolock:
+ return rc;
+}
+
+static ssize_t audamrwb_read(struct file *file, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ int rc = 0;
+
+ if (!audio->pcm_feedback)
+ return 0; /* PCM feedback is not enabled. Nothing to read */
+
+ mutex_lock(&audio->read_lock);
+ MM_DBG("count %d\n", count);
+ while (count > 0) {
+ rc = wait_event_interruptible(audio->read_wait,
+ (audio->in[audio->read_next].used > 0) ||
+ (audio->stopped) || (audio->rflush));
+
+ if (rc < 0)
+ break;
+
+ if (audio->stopped || audio->rflush) {
+ rc = -EBUSY;
+ break;
+ }
+
+ if (count < audio->in[audio->read_next].used) {
+ /* Read must happen in frame boundary. Since driver does
+ * not know frame size, read count must be greater or
+ * equal to size of PCM samples
+ */
+ MM_DBG("read stop - partial frame\n");
+ break;
+ } else {
+ MM_DBG("read from in[%d]\n", audio->read_next);
+
+ if (copy_to_user
+ (buf, audio->in[audio->read_next].data,
+ audio->in[audio->read_next].used)) {
+ MM_ERR("invalid addr %x\n", (unsigned int)buf);
+ rc = -EFAULT;
+ break;
+ }
+ count -= audio->in[audio->read_next].used;
+ buf += audio->in[audio->read_next].used;
+ audio->in[audio->read_next].used = 0;
+ if ((++audio->read_next) == audio->pcm_buf_count)
+ audio->read_next = 0;
+ break;
+ }
+ }
+
+ /* don't feed output buffer to HW decoder during flushing
+ * buffer refresh command will be sent once flush completes
+ * send buf refresh command here can confuse HW decoder
+ */
+ if (audio->buf_refresh && !audio->rflush) {
+ audio->buf_refresh = 0;
+ MM_DBG("kick start pcm feedback again\n");
+ audamrwb_buffer_refresh(audio);
+ }
+
+ mutex_unlock(&audio->read_lock);
+
+ if (buf > start)
+ rc = buf - start;
+
+ MM_DBG("read %d bytes\n", rc);
+ return rc;
+}
+
+static int audamrwb_process_eos(struct audio *audio,
+ const char __user *buf_start, unsigned short mfield_size)
+{
+ struct buffer *frame;
+ char *buf_ptr;
+ int rc = 0;
+
+ MM_DBG("signal input EOS reserved=%d\n", audio->reserved);
+ if (audio->reserved) {
+ MM_DBG("Pass reserve byte\n");
+ frame = audio->out + audio->out_head;
+ buf_ptr = frame->data;
+ rc = wait_event_interruptible(audio->write_wait,
+ (frame->used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+ if (rc < 0)
+ goto done;
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+ buf_ptr[0] = audio->rsv_byte;
+ buf_ptr[1] = 0;
+ audio->out_head ^= 1;
+ frame->mfield_sz = 0;
+ audio->reserved = 0;
+ frame->used = 2;
+ audamrwb_send_data(audio, 0);
+ }
+
+ MM_DBG("Now signal input EOS after reserved bytes %d %d %d\n",
+ audio->out[0].used, audio->out[1].used, audio->out_needed);
+
+ frame = audio->out + audio->out_head;
+
+ rc = wait_event_interruptible(audio->write_wait,
+ (audio->out_needed &&
+ audio->out[0].used == 0 &&
+ audio->out[1].used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+
+ if (rc < 0)
+ goto done;
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+
+ if (copy_from_user(frame->data, buf_start, mfield_size)) {
+ rc = -EFAULT;
+ goto done;
+ }
+
+ frame->mfield_sz = mfield_size;
+ audio->out_head ^= 1;
+ frame->used = mfield_size;
+ audamrwb_send_data(audio, 0);
+
+done:
+ return rc;
+}
+
+static ssize_t audamrwb_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ struct buffer *frame;
+ size_t xfer;
+ char *cpy_ptr;
+ int rc = 0, eos_condition = AUDAMRWB_EOS_NONE;
+ unsigned short mfield_size = 0;
+ unsigned dsize;
+
+ MM_DBG("cnt=%d\n", count);
+
+ mutex_lock(&audio->write_lock);
+ while (count > 0) {
+ frame = audio->out + audio->out_head;
+ cpy_ptr = frame->data;
+ dsize = 0;
+ rc = wait_event_interruptible(audio->write_wait,
+ (frame->used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+
+ MM_DBG("buffer available\n");
+ if (rc < 0)
+ break;
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ break;
+ }
+
+ if (audio->mfield) {
+ if (buf == start) {
+ /* Processing beginning of user buffer */
+ if (__get_user(mfield_size,
+ (unsigned short __user *) buf)) {
+ rc = -EFAULT;
+ break;
+ } else if (mfield_size > count) {
+ rc = -EINVAL;
+ break;
+ }
+ MM_DBG("mf offset_val %x\n", mfield_size);
+ if (copy_from_user(cpy_ptr, buf, mfield_size)) {
+ rc = -EFAULT;
+ break;
+ }
+ /* Check if EOS flag is set and buffer
+ * contains just meta field
+ */
+ if (cpy_ptr[AUDAMRWB_EOS_FLG_OFFSET] &
+ AUDAMRWB_EOS_FLG_MASK) {
+ MM_DBG("eos set\n");
+ eos_condition = AUDAMRWB_EOS_SET;
+ if (mfield_size == count) {
+ buf += mfield_size;
+ break;
+ } else
+ cpy_ptr[AUDAMRWB_EOS_FLG_OFFSET] &=
+ ~AUDAMRWB_EOS_FLG_MASK;
+ }
+ cpy_ptr += mfield_size;
+ count -= mfield_size;
+ dsize += mfield_size;
+ buf += mfield_size;
+ } else {
+ mfield_size = 0;
+ MM_DBG("continuous buffer\n");
+ }
+ frame->mfield_sz = mfield_size;
+ }
+
+ if (audio->reserved) {
+ MM_DBG("append reserved byte %x\n", audio->rsv_byte);
+ *cpy_ptr = audio->rsv_byte;
+ xfer = (count > ((frame->size - mfield_size) - 1)) ?
+ ((frame->size - mfield_size) - 1) : count;
+ cpy_ptr++;
+ dsize += 1;
+ audio->reserved = 0;
+ } else
+ xfer = (count > (frame->size - mfield_size)) ?
+ (frame->size - mfield_size) : count;
+
+ if (copy_from_user(cpy_ptr, buf, xfer)) {
+ rc = -EFAULT;
+ break;
+ }
+
+ dsize += xfer;
+ if (dsize & 1) {
+ audio->rsv_byte = ((char *) frame->data)[dsize - 1];
+ MM_DBG("odd length buf reserve last byte %x\n",
+ audio->rsv_byte);
+ audio->reserved = 1;
+ dsize--;
+ }
+ count -= xfer;
+ buf += xfer;
+
+ if (dsize > 0) {
+ audio->out_head ^= 1;
+ frame->used = dsize;
+ audamrwb_send_data(audio, 0);
+ }
+ }
+ MM_DBG("eos_condition %x buf[0x%x] start[0x%x]\n", eos_condition,
+ (int) buf, (int) start);
+ if (eos_condition == AUDAMRWB_EOS_SET)
+ rc = audamrwb_process_eos(audio, start, mfield_size);
+ mutex_unlock(&audio->write_lock);
+ if (!rc) {
+ if (buf > start)
+ return buf - start;
+ }
+ return rc;
+}
+
+static int audamrwb_release(struct inode *inode, struct file *file)
+{
+ struct audio *audio = file->private_data;
+
+ MM_INFO("audio instance 0x%08x freeing\n", (int)audio);
+
+ mutex_lock(&audio->lock);
+ auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id);
+ audamrwb_disable(audio);
+ audamrwb_flush(audio);
+ audamrwb_flush_pcm_buf(audio);
+ msm_adsp_put(audio->audplay);
+ audpp_adec_free(audio->dec_id);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&audio->suspend_ctl.node);
+#endif
+ audio->event_abort = 1;
+ wake_up(&audio->event_wait);
+ audamrwb_reset_event_queue(audio);
+ iounmap(audio->data);
+ pmem_kfree(audio->phys);
+ if (audio->read_data) {
+ iounmap(audio->read_data);
+ pmem_kfree(audio->read_phys);
+ }
+ mutex_unlock(&audio->lock);
+#ifdef CONFIG_DEBUG_FS
+ if (audio->dentry)
+ debugfs_remove(audio->dentry);
+#endif
+ kfree(audio);
+ return 0;
+}
+
+static void audamrwb_post_event(struct audio *audio, int type,
+ union msm_audio_event_payload payload)
+{
+ struct audamrwb_event *e_node = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+
+ if (!list_empty(&audio->free_event_queue)) {
+ e_node = list_first_entry(&audio->free_event_queue,
+ struct audamrwb_event, list);
+ list_del(&e_node->list);
+ } else {
+ e_node = kmalloc(sizeof(struct audamrwb_event), GFP_ATOMIC);
+ if (!e_node) {
+ MM_ERR("No mem to post event %d\n", type);
+ return;
+ }
+ }
+
+ e_node->event_type = type;
+ e_node->payload = payload;
+
+ list_add_tail(&e_node->list, &audio->event_queue);
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+ wake_up(&audio->event_wait);
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void audamrwb_suspend(struct early_suspend *h)
+{
+ struct audamrwb_suspend_ctl *ctl =
+ container_of(h, struct audamrwb_suspend_ctl, node);
+ union msm_audio_event_payload payload;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ audamrwb_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload);
+}
+
+static void audamrwb_resume(struct early_suspend *h)
+{
+ struct audamrwb_suspend_ctl *ctl =
+ container_of(h, struct audamrwb_suspend_ctl, node);
+ union msm_audio_event_payload payload;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ audamrwb_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload);
+}
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t audamrwb_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t audamrwb_debug_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ const int debug_bufmax = 1024;
+ static char buffer[1024];
+ int n = 0, i;
+ struct audio *audio = file->private_data;
+
+ mutex_lock(&audio->lock);
+ n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "enabled %d\n", audio->enabled);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "stopped %d\n", audio->stopped);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "pcm_feedback %d\n", audio->pcm_feedback);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_buf_sz %d\n", audio->out[0].size);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "pcm_buf_count %d \n", audio->pcm_buf_count);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "pcm_buf_sz %d \n", audio->in[0].size);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "volume %x \n", audio->vol_pan.volume);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "sample rate %d \n", audio->out_sample_rate);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "channel mode %d \n", audio->out_channel_mode);
+ mutex_unlock(&audio->lock);
+ /* Following variables are only useful for debugging when
+ * when playback halts unexpectedly. Thus, no mutual exclusion
+ * enforced
+ */
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "wflush %d\n", audio->wflush);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "rflush %d\n", audio->rflush);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "running %d \n", audio->running);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "dec state %d \n", audio->dec_state);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_needed %d \n", audio->out_needed);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_head %d \n", audio->out_head);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_tail %d \n", audio->out_tail);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out[0].used %d \n", audio->out[0].used);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out[1].used %d \n", audio->out[1].used);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "buffer_refresh %d \n", audio->buf_refresh);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "read_next %d \n", audio->read_next);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "fill_next %d \n", audio->fill_next);
+ for (i = 0; i < audio->pcm_buf_count; i++)
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "in[%d].used %d \n", i, audio->in[i].used);
+ buffer[n] = 0;
+ return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static const struct file_operations audamrwb_debug_fops = {
+ .read = audamrwb_debug_read,
+ .open = audamrwb_debug_open,
+};
+#endif
+
+static int audamrwb_open(struct inode *inode, struct file *file)
+{
+ struct audio *audio = NULL;
+ int rc, dec_attrb, decid, i;
+ struct audamrwb_event *e_node = NULL;
+#ifdef CONFIG_DEBUG_FS
+ /* 4 bytes represents decoder number, 1 byte for terminate string */
+ char name[sizeof "msm_amrwb_" + 5];
+#endif
+
+ /* Allocate Mem for audio instance */
+ audio = kzalloc(sizeof(struct audio), GFP_KERNEL);
+ if (!audio) {
+ MM_ERR("no memory to allocate audio instance\n");
+ rc = -ENOMEM;
+ goto done;
+ }
+ MM_INFO("audio instance 0x%08x created\n", (int)audio);
+
+ /* Allocate the decoder */
+ dec_attrb = AUDDEC_DEC_AMRWB;
+ if (file->f_mode & FMODE_READ)
+ dec_attrb |= MSM_AUD_MODE_NONTUNNEL;
+ else
+ dec_attrb |= MSM_AUD_MODE_TUNNEL;
+
+ decid = audpp_adec_alloc(dec_attrb, &audio->module_name,
+ &audio->queue_id);
+
+ if (decid < 0) {
+ MM_ERR("No free decoder available, freeing instance 0x%08x\n",
+ (int)audio);
+ rc = -ENODEV;
+ kfree(audio);
+ goto done;
+ }
+
+ audio->dec_id = decid & MSM_AUD_DECODER_MASK;
+
+ audio->phys = pmem_kalloc(DMASZ, PMEM_MEMTYPE_EBI1|PMEM_ALIGNMENT_4K);
+ if (IS_ERR((void *)audio->phys)) {
+ MM_ERR("could not allocate write buffers, freeing instance \
+ 0x%08x\n", (int)audio);
+ rc = -ENOMEM;
+ audpp_adec_free(audio->dec_id);
+ kfree(audio);
+ goto done;
+ } else {
+ audio->data = ioremap(audio->phys, DMASZ);
+ if (!audio->data) {
+ MM_ERR("could not allocate write buffers, freeing \
+ instance 0x%08x\n", (int)audio);
+ rc = -ENOMEM;
+ pmem_kfree(audio->phys);
+ audpp_adec_free(audio->dec_id);
+ kfree(audio);
+ goto done;
+ }
+ MM_DBG("write buf: phy addr 0x%08x kernel addr 0x%08x\n",
+ audio->phys, (int)audio->data);
+ }
+
+ rc = msm_adsp_get(audio->module_name, &audio->audplay,
+ &audplay_adsp_ops_amrwb, audio);
+ if (rc) {
+ MM_ERR("failed to get %s module freeing instance 0x%08x\n",
+ audio->module_name, (int)audio);
+ goto err;
+ }
+
+ mutex_init(&audio->lock);
+ mutex_init(&audio->write_lock);
+ mutex_init(&audio->read_lock);
+ mutex_init(&audio->get_event_lock);
+ spin_lock_init(&audio->dsp_lock);
+ spin_lock_init(&audio->event_queue_lock);
+ INIT_LIST_HEAD(&audio->free_event_queue);
+ INIT_LIST_HEAD(&audio->event_queue);
+ init_waitqueue_head(&audio->write_wait);
+ init_waitqueue_head(&audio->read_wait);
+ init_waitqueue_head(&audio->wait);
+ init_waitqueue_head(&audio->event_wait);
+ init_waitqueue_head(&audio->avsync_wait);
+
+ audio->out[0].data = audio->data + 0;
+ audio->out[0].addr = audio->phys + 0;
+ audio->out[0].size = BUFSZ;
+
+ audio->out[1].data = audio->data + BUFSZ;
+ audio->out[1].addr = audio->phys + BUFSZ;
+ audio->out[1].size = BUFSZ;
+
+ audio->vol_pan.volume = 0x2000;
+ audio->vol_pan.pan = 0x0;
+ audio->eq_enable = 0;
+ audio->out_sample_rate = 44100;
+ audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V;
+
+ audamrwb_flush(audio);
+
+ file->private_data = audio;
+ audio->opened = 1;
+ audio->event_abort = 0;
+ audio->device_events = AUDDEV_EVT_DEV_RDY
+ |AUDDEV_EVT_DEV_RLS|
+ AUDDEV_EVT_STREAM_VOL_CHG;
+
+ rc = auddev_register_evt_listner(audio->device_events,
+ AUDDEV_CLNT_DEC,
+ audio->dec_id,
+ amrwb_listner,
+ (void *)audio);
+ if (rc) {
+ MM_ERR("failed to register listner\n");
+ goto event_err;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ snprintf(name, sizeof name, "msm_amrwb_%04x", audio->dec_id);
+ audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *) audio, &audamrwb_debug_fops);
+
+ if (IS_ERR(audio->dentry))
+ MM_DBG("debugfs_create_file failed\n");
+#endif
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+ audio->suspend_ctl.node.resume = audamrwb_resume;
+ audio->suspend_ctl.node.suspend = audamrwb_suspend;
+ audio->suspend_ctl.audio = audio;
+ register_early_suspend(&audio->suspend_ctl.node);
+#endif
+ for (i = 0; i < AUDAMRWB_EVENT_NUM; i++) {
+ e_node = kmalloc(sizeof(struct audamrwb_event), GFP_KERNEL);
+ if (e_node)
+ list_add_tail(&e_node->list, &audio->free_event_queue);
+ else {
+ MM_ERR("event pkt alloc failed\n");
+ break;
+ }
+ }
+done:
+ return rc;
+event_err:
+ msm_adsp_put(audio->audplay);
+err:
+ iounmap(audio->data);
+ pmem_kfree(audio->phys);
+ audpp_adec_free(audio->dec_id);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_amrwb_fops = {
+ .owner = THIS_MODULE,
+ .open = audamrwb_open,
+ .release = audamrwb_release,
+ .read = audamrwb_read,
+ .write = audamrwb_write,
+ .unlocked_ioctl = audamrwb_ioctl,
+ .fsync = audamrwb_fsync,
+};
+
+struct miscdevice audio_amrwb_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_amrwb",
+ .fops = &audio_amrwb_fops,
+};
+
+static int __init audamrwb_init(void)
+{
+ return misc_register(&audio_amrwb_misc);
+}
+
+static void __exit audamrwb_exit(void)
+{
+ misc_deregister(&audio_amrwb_misc);
+}
+
+module_init(audamrwb_init);
+module_exit(audamrwb_exit);
+
+MODULE_DESCRIPTION("MSM AMR-WB driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_dev_ctl.c b/arch/arm/mach-msm/qdsp5v2/audio_dev_ctl.c
new file mode 100644
index 0000000..b6d6e5e
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_dev_ctl.c
@@ -0,0 +1,1328 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/msm_audio.h>
+#include <asm/uaccess.h>
+#include <asm/atomic.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <mach/debug_mm.h>
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/audpp.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+
+#ifndef MAX
+#define MAX(x, y) (((x) > (y)) ? (x) : (y))
+#endif
+
+
+static DEFINE_MUTEX(session_lock);
+
+struct audio_dev_ctrl_state {
+ struct msm_snddev_info *devs[AUDIO_DEV_CTL_MAX_DEV];
+ u32 num_dev;
+ atomic_t opened;
+ struct msm_snddev_info *voice_rx_dev;
+ struct msm_snddev_info *voice_tx_dev;
+ wait_queue_head_t wait;
+};
+
+static struct audio_dev_ctrl_state audio_dev_ctrl;
+struct event_listner event;
+#define MAX_DEC_SESSIONS 7
+#define MAX_ENC_SESSIONS 3
+
+struct session_freq {
+ int freq;
+ int evt;
+};
+
+
+struct audio_routing_info {
+ unsigned short mixer_mask[MAX_DEC_SESSIONS];
+ unsigned short audrec_mixer_mask[MAX_ENC_SESSIONS];
+ struct session_freq dec_freq[MAX_DEC_SESSIONS];
+ struct session_freq enc_freq[MAX_ENC_SESSIONS];
+ int dual_mic_setting[MAX_ENC_SESSIONS];
+ int voice_tx_dev_id;
+ int voice_rx_dev_id;
+ int voice_tx_sample_rate;
+ int voice_rx_sample_rate;
+ signed int voice_tx_vol;
+ signed int voice_rx_vol;
+ int tx_mute;
+ int rx_mute;
+ int voice_state;
+};
+
+static struct audio_routing_info routing_info;
+
+#ifdef CONFIG_DEBUG_FS
+
+static struct dentry *dentry;
+static int rtc_getdevice_dbg_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ MM_INFO("debug intf %s\n", (char *) file->private_data);
+ return 0;
+}
+bool is_dev_opened(u32 adb_id)
+{
+
+ int dev_id = 0;
+ struct msm_snddev_info *dev_info = NULL;
+
+ for (dev_id = 0; dev_id < audio_dev_ctrl.num_dev; dev_id++) {
+ dev_info = audio_dev_ctrl_find_dev(dev_id);
+ if (IS_ERR(dev_info)) {
+ MM_ERR("pass invalid dev_id %d\n", dev_id);
+ return false;
+ }
+ if (dev_info->opened && (dev_info->acdb_id == adb_id))
+ return true;
+ }
+
+ return false;
+}
+static ssize_t rtc_getdevice_dbg_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ static char buffer[1024];
+ static char swap_buf[1024];
+ const int debug_bufmax = sizeof(buffer);
+ int n = 0;
+ int swap_count = 0;
+ int rc = 0;
+ int dev_count = 0;
+ int dev_id = 0;
+ struct msm_snddev_info *dev_info = NULL;
+
+
+ if (audio_dev_ctrl.num_dev <= 0) {
+ MM_ERR("Invalid no Device present\n");
+ dev_count = 0;
+ n = scnprintf(buffer, debug_bufmax, "DEV_NO:0x%x\n", dev_count);
+ } else {
+ for (dev_id = 0; dev_id < audio_dev_ctrl.num_dev; dev_id++) {
+ dev_info = audio_dev_ctrl_find_dev(dev_id);
+ if (IS_ERR(dev_info)) {
+ MM_ERR("pass invalid dev_id %d\n", dev_id);
+ rc = PTR_ERR(dev_info);
+ return rc;
+ }
+ if (dev_info->opened) {
+ n += scnprintf(swap_buf + n, debug_bufmax - n,
+ "ACDB_ID:0x%x;CAPB:0x%x\n",
+ dev_info->acdb_id,
+ dev_info->capability);
+ dev_count++;
+ MM_DBG("RTC Get Device %x COPP %x Session Mask \
+ %x Capb %x Dev Count %x\n",
+ dev_id , dev_info->copp_id, dev_info->sessions,
+ dev_info->capability, dev_count);
+
+ }
+ }
+
+ swap_count = scnprintf(buffer, debug_bufmax, \
+ "DEV_NO:0x%x\n", dev_count);
+
+ memcpy(buffer+swap_count, swap_buf, n*sizeof(char));
+ n = n+swap_count;
+
+ buffer[n] = 0;
+ }
+ return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static const struct file_operations rtc_acdb_debug_fops = {
+ .open = rtc_getdevice_dbg_open,
+ .read = rtc_getdevice_dbg_read
+};
+#endif
+int msm_reset_all_device(void)
+{
+ int rc = 0;
+ int dev_id = 0;
+ struct msm_snddev_info *dev_info = NULL;
+
+ for (dev_id = 0; dev_id < audio_dev_ctrl.num_dev; dev_id++) {
+ dev_info = audio_dev_ctrl_find_dev(dev_id);
+ if (IS_ERR(dev_info)) {
+ MM_ERR("pass invalid dev_id %d\n", dev_id);
+ rc = PTR_ERR(dev_info);
+ return rc;
+ }
+ if (!dev_info->opened)
+ continue;
+ MM_DBG("Resetting device %d active on COPP %d"
+ "with 0x%08x as routing\n",
+ dev_id, dev_info->copp_id, dev_info->sessions);
+ broadcast_event(AUDDEV_EVT_REL_PENDING,
+ dev_id,
+ SESSION_IGNORE);
+ rc = dev_info->dev_ops.close(dev_info);
+ if (rc < 0) {
+ MM_ERR("Snd device %d failed close!\n", dev_id);
+ return rc;
+ } else {
+ dev_info->opened = 0;
+ broadcast_event(AUDDEV_EVT_DEV_RLS,
+ dev_id,
+ SESSION_IGNORE);
+ }
+ dev_info->sessions = 0;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(msm_reset_all_device);
+
+int msm_set_dual_mic_config(int enc_session_id, int config)
+{
+ int i;
+ if (enc_session_id >= MAX_ENC_SESSIONS)
+ return -EINVAL;
+ /*config is set(1) dual mic recording is selected */
+ /*config is reset (0) dual mic recording is not selected*/
+ routing_info.dual_mic_setting[enc_session_id] = config;
+ for (i = 0; i < MAX_ENC_SESSIONS; i++)
+ MM_DBG("dual_mic_setting[%d] = %d\n",
+ i, routing_info.dual_mic_setting[i]);
+ return 0;
+}
+EXPORT_SYMBOL(msm_set_dual_mic_config);
+
+int msm_get_dual_mic_config(int enc_session_id)
+{
+ if (enc_session_id >= MAX_ENC_SESSIONS)
+ return -EINVAL;
+ return routing_info.dual_mic_setting[enc_session_id];
+}
+EXPORT_SYMBOL(msm_get_dual_mic_config);
+
+int msm_get_voice_state(void)
+{
+ MM_DBG("voice state %d\n", routing_info.voice_state);
+ return routing_info.voice_state;
+}
+EXPORT_SYMBOL(msm_get_voice_state);
+
+int msm_set_voice_mute(int dir, int mute)
+{
+ MM_DBG("dir %x mute %x\n", dir, mute);
+ if (!audio_dev_ctrl.voice_rx_dev
+ || !audio_dev_ctrl.voice_tx_dev)
+ return -EPERM;
+ if (dir == DIR_TX) {
+ routing_info.tx_mute = mute;
+ broadcast_event(AUDDEV_EVT_DEVICE_VOL_MUTE_CHG,
+ routing_info.voice_tx_dev_id, SESSION_IGNORE);
+ } else
+ return -EPERM;
+ return 0;
+}
+EXPORT_SYMBOL(msm_set_voice_mute);
+
+int msm_set_voice_vol(int dir, s32 volume)
+{
+ if (!audio_dev_ctrl.voice_rx_dev
+ || !audio_dev_ctrl.voice_tx_dev)
+ return -EPERM;
+ if (dir == DIR_TX) {
+ routing_info.voice_tx_vol = volume;
+ broadcast_event(AUDDEV_EVT_DEVICE_VOL_MUTE_CHG,
+ routing_info.voice_tx_dev_id,
+ SESSION_IGNORE);
+ } else if (dir == DIR_RX) {
+ routing_info.voice_rx_vol = volume;
+ broadcast_event(AUDDEV_EVT_DEVICE_VOL_MUTE_CHG,
+ routing_info.voice_rx_dev_id,
+ SESSION_IGNORE);
+ } else
+ return -EINVAL;
+ return 0;
+}
+EXPORT_SYMBOL(msm_set_voice_vol);
+
+void msm_snddev_register(struct msm_snddev_info *dev_info)
+{
+ mutex_lock(&session_lock);
+ if (audio_dev_ctrl.num_dev < AUDIO_DEV_CTL_MAX_DEV) {
+ audio_dev_ctrl.devs[audio_dev_ctrl.num_dev] = dev_info;
+ dev_info->dev_volume = 50; /* 50% */
+ dev_info->sessions = 0x0;
+ dev_info->usage_count = 0;
+ dev_info->set_sample_rate = 0;
+ audio_dev_ctrl.num_dev++;
+ } else
+ MM_ERR("%s: device registry max out\n", __func__);
+ mutex_unlock(&session_lock);
+}
+EXPORT_SYMBOL(msm_snddev_register);
+
+int msm_snddev_devcount(void)
+{
+ return audio_dev_ctrl.num_dev;
+}
+EXPORT_SYMBOL(msm_snddev_devcount);
+
+int msm_snddev_query(int dev_id)
+{
+ if (dev_id <= audio_dev_ctrl.num_dev)
+ return 0;
+ return -ENODEV;
+}
+EXPORT_SYMBOL(msm_snddev_query);
+
+int msm_snddev_is_set(int popp_id, int copp_id)
+{
+ return routing_info.mixer_mask[popp_id] & (0x1 << copp_id);
+}
+EXPORT_SYMBOL(msm_snddev_is_set);
+
+unsigned short msm_snddev_route_enc(int enc_id)
+{
+ if (enc_id >= MAX_ENC_SESSIONS)
+ return -EINVAL;
+ return routing_info.audrec_mixer_mask[enc_id];
+}
+EXPORT_SYMBOL(msm_snddev_route_enc);
+
+unsigned short msm_snddev_route_dec(int popp_id)
+{
+ if (popp_id >= MAX_DEC_SESSIONS)
+ return -EINVAL;
+ return routing_info.mixer_mask[popp_id];
+}
+EXPORT_SYMBOL(msm_snddev_route_dec);
+
+int msm_snddev_set_dec(int popp_id, int copp_id, int set)
+{
+ if (set)
+ routing_info.mixer_mask[popp_id] |= (0x1 << copp_id);
+ else
+ routing_info.mixer_mask[popp_id] &= ~(0x1 << copp_id);
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_snddev_set_dec);
+
+int msm_snddev_set_enc(int popp_id, int copp_id, int set)
+{
+ if (set)
+ routing_info.audrec_mixer_mask[popp_id] |= (0x1 << copp_id);
+ else
+ routing_info.audrec_mixer_mask[popp_id] &= ~(0x1 << copp_id);
+ return 0;
+}
+EXPORT_SYMBOL(msm_snddev_set_enc);
+
+int msm_device_is_voice(int dev_id)
+{
+ if ((dev_id == routing_info.voice_rx_dev_id)
+ || (dev_id == routing_info.voice_tx_dev_id))
+ return 0;
+ else
+ return -EINVAL;
+}
+EXPORT_SYMBOL(msm_device_is_voice);
+
+int msm_set_voc_route(struct msm_snddev_info *dev_info,
+ int stream_type, int dev_id)
+{
+ int rc = 0;
+ u32 session_mask = 0;
+
+ mutex_lock(&session_lock);
+ switch (stream_type) {
+ case AUDIO_ROUTE_STREAM_VOICE_RX:
+ if (audio_dev_ctrl.voice_rx_dev)
+ audio_dev_ctrl.voice_rx_dev->sessions &= ~0xFF;
+
+ if (!(dev_info->capability & SNDDEV_CAP_RX) |
+ !(dev_info->capability & SNDDEV_CAP_VOICE)) {
+ rc = -EINVAL;
+ break;
+ }
+ audio_dev_ctrl.voice_rx_dev = dev_info;
+ if (audio_dev_ctrl.voice_rx_dev) {
+ session_mask =
+ 0x1 << (8 * ((int)AUDDEV_CLNT_VOC-1));
+ audio_dev_ctrl.voice_rx_dev->sessions |=
+ session_mask;
+ }
+ routing_info.voice_rx_dev_id = dev_id;
+ break;
+ case AUDIO_ROUTE_STREAM_VOICE_TX:
+ if (audio_dev_ctrl.voice_tx_dev)
+ audio_dev_ctrl.voice_tx_dev->sessions &= ~0xFF;
+
+ if (!(dev_info->capability & SNDDEV_CAP_TX) |
+ !(dev_info->capability & SNDDEV_CAP_VOICE)) {
+ rc = -EINVAL;
+ break;
+ }
+
+ audio_dev_ctrl.voice_tx_dev = dev_info;
+ if (audio_dev_ctrl.voice_rx_dev) {
+ session_mask =
+ 0x1 << (8 * ((int)AUDDEV_CLNT_VOC-1));
+ audio_dev_ctrl.voice_tx_dev->sessions |=
+ session_mask;
+ }
+ routing_info.voice_tx_dev_id = dev_id;
+ break;
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&session_lock);
+ return rc;
+}
+EXPORT_SYMBOL(msm_set_voc_route);
+
+void msm_release_voc_thread(void)
+{
+ wake_up(&audio_dev_ctrl.wait);
+}
+EXPORT_SYMBOL(msm_release_voc_thread);
+
+int msm_snddev_get_enc_freq(session_id)
+{
+ return routing_info.enc_freq[session_id].freq;
+}
+EXPORT_SYMBOL(msm_snddev_get_enc_freq);
+
+int msm_get_voc_freq(int *tx_freq, int *rx_freq)
+{
+ *tx_freq = routing_info.voice_tx_sample_rate;
+ *rx_freq = routing_info.voice_rx_sample_rate;
+ return 0;
+}
+EXPORT_SYMBOL(msm_get_voc_freq);
+
+int msm_get_voc_route(u32 *rx_id, u32 *tx_id)
+{
+ int rc = 0;
+
+ if (!rx_id || !tx_id)
+ return -EINVAL;
+
+ mutex_lock(&session_lock);
+ if (!audio_dev_ctrl.voice_rx_dev || !audio_dev_ctrl.voice_tx_dev) {
+ rc = -ENODEV;
+ mutex_unlock(&session_lock);
+ return rc;
+ }
+
+ *rx_id = audio_dev_ctrl.voice_rx_dev->acdb_id;
+ *tx_id = audio_dev_ctrl.voice_tx_dev->acdb_id;
+
+ mutex_unlock(&session_lock);
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_get_voc_route);
+
+struct msm_snddev_info *audio_dev_ctrl_find_dev(u32 dev_id)
+{
+ struct msm_snddev_info *info;
+
+ if ((audio_dev_ctrl.num_dev - 1) < dev_id) {
+ info = ERR_PTR(-ENODEV);
+ goto error;
+ }
+
+ info = audio_dev_ctrl.devs[dev_id];
+error:
+ return info;
+
+}
+EXPORT_SYMBOL(audio_dev_ctrl_find_dev);
+
+int snddev_voice_set_volume(int vol, int path)
+{
+ if (audio_dev_ctrl.voice_rx_dev
+ && audio_dev_ctrl.voice_tx_dev) {
+ if (path)
+ audio_dev_ctrl.voice_tx_dev->dev_volume = vol;
+ else
+ audio_dev_ctrl.voice_rx_dev->dev_volume = vol;
+ } else
+ return -ENODEV;
+ return 0;
+}
+EXPORT_SYMBOL(snddev_voice_set_volume);
+
+static int audio_dev_ctrl_get_devices(struct audio_dev_ctrl_state *dev_ctrl,
+ void __user *arg)
+{
+ int rc = 0;
+ u32 index;
+ struct msm_snd_device_list work_list;
+ struct msm_snd_device_info *work_tbl;
+
+ if (copy_from_user(&work_list, arg, sizeof(work_list))) {
+ rc = -EFAULT;
+ goto error;
+ }
+
+ if (work_list.num_dev > dev_ctrl->num_dev) {
+ rc = -EINVAL;
+ goto error;
+ }
+
+ work_tbl = kmalloc(work_list.num_dev *
+ sizeof(struct msm_snd_device_info), GFP_KERNEL);
+ if (!work_tbl) {
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ for (index = 0; index < dev_ctrl->num_dev; index++) {
+ work_tbl[index].dev_id = index;
+ work_tbl[index].dev_cap = dev_ctrl->devs[index]->capability;
+ strlcpy(work_tbl[index].dev_name, dev_ctrl->devs[index]->name,
+ 64);
+ }
+
+ if (copy_to_user((void *) (work_list.list), work_tbl,
+ work_list.num_dev * sizeof(struct msm_snd_device_info)))
+ rc = -EFAULT;
+ kfree(work_tbl);
+error:
+ return rc;
+}
+
+
+int auddev_register_evt_listner(u32 evt_id, u32 clnt_type, u32 clnt_id,
+ void (*listner)(u32 evt_id,
+ union auddev_evt_data *evt_payload,
+ void *private_data),
+ void *private_data)
+{
+ int rc;
+ struct msm_snd_evt_listner *callback = NULL;
+ struct msm_snd_evt_listner *new_cb;
+
+ new_cb = kzalloc(sizeof(struct msm_snd_evt_listner), GFP_KERNEL);
+ if (!new_cb) {
+ MM_ERR("No memory to add new listener node\n");
+ return -ENOMEM;
+ }
+
+ mutex_lock(&session_lock);
+ new_cb->cb_next = NULL;
+ new_cb->auddev_evt_listener = listner;
+ new_cb->evt_id = evt_id;
+ new_cb->clnt_type = clnt_type;
+ new_cb->clnt_id = clnt_id;
+ new_cb->private_data = private_data;
+ if (event.cb == NULL) {
+ event.cb = new_cb;
+ new_cb->cb_prev = NULL;
+ } else {
+ callback = event.cb;
+ for (; ;) {
+ if (callback->cb_next == NULL)
+ break;
+ else {
+ callback = callback->cb_next;
+ continue;
+ }
+ }
+ callback->cb_next = new_cb;
+ new_cb->cb_prev = callback;
+ }
+ event.num_listner++;
+ mutex_unlock(&session_lock);
+ rc = 0;
+ return rc;
+}
+EXPORT_SYMBOL(auddev_register_evt_listner);
+
+int auddev_unregister_evt_listner(u32 clnt_type, u32 clnt_id)
+{
+ struct msm_snd_evt_listner *callback = event.cb;
+ struct msm_snddev_info *info;
+ u32 session_mask = 0;
+ int i = 0;
+
+ mutex_lock(&session_lock);
+ while (callback != NULL) {
+ if ((callback->clnt_type == clnt_type)
+ && (callback->clnt_id == clnt_id))
+ break;
+ callback = callback->cb_next;
+ }
+ if (callback == NULL) {
+ mutex_unlock(&session_lock);
+ return -EINVAL;
+ }
+
+ if ((callback->cb_next == NULL) && (callback->cb_prev == NULL))
+ event.cb = NULL;
+ else if (callback->cb_next == NULL)
+ callback->cb_prev->cb_next = NULL;
+ else if (callback->cb_prev == NULL) {
+ callback->cb_next->cb_prev = NULL;
+ event.cb = callback->cb_next;
+ } else {
+ callback->cb_prev->cb_next = callback->cb_next;
+ callback->cb_next->cb_prev = callback->cb_prev;
+ }
+ kfree(callback);
+
+ session_mask = (0x1 << (clnt_id)) << (8 * ((int)clnt_type-1));
+ for (i = 0; i < audio_dev_ctrl.num_dev; i++) {
+ info = audio_dev_ctrl.devs[i];
+ info->sessions &= ~session_mask;
+ }
+ if (clnt_type == AUDDEV_CLNT_ENC)
+ msm_set_dual_mic_config(clnt_id, 0);
+ mutex_unlock(&session_lock);
+ return 0;
+}
+EXPORT_SYMBOL(auddev_unregister_evt_listner);
+
+int msm_snddev_withdraw_freq(u32 session_id, u32 capability, u32 clnt_type)
+{
+ int i = 0;
+ struct msm_snddev_info *info;
+ u32 session_mask = 0;
+
+ if ((clnt_type == AUDDEV_CLNT_VOC) && (session_id != 0))
+ return -EINVAL;
+ if ((clnt_type == AUDDEV_CLNT_DEC)
+ && (session_id >= MAX_DEC_SESSIONS))
+ return -EINVAL;
+ if ((clnt_type == AUDDEV_CLNT_ENC)
+ && (session_id >= MAX_ENC_SESSIONS))
+ return -EINVAL;
+
+ session_mask = (0x1 << (session_id)) << (8 * ((int)clnt_type-1));
+
+ for (i = 0; i < audio_dev_ctrl.num_dev; i++) {
+ info = audio_dev_ctrl.devs[i];
+ if ((info->sessions & session_mask)
+ && (info->capability & capability)) {
+ if (!(info->sessions & ~(session_mask)))
+ info->set_sample_rate = 0;
+ }
+ }
+ if (clnt_type == AUDDEV_CLNT_DEC)
+ routing_info.dec_freq[session_id].freq
+ = 0;
+ else if (clnt_type == AUDDEV_CLNT_ENC)
+ routing_info.enc_freq[session_id].freq
+ = 0;
+ else if (capability == SNDDEV_CAP_TX)
+ routing_info.voice_tx_sample_rate = 0;
+ else
+ routing_info.voice_rx_sample_rate = 48000;
+ return 0;
+}
+
+int msm_snddev_request_freq(int *freq, u32 session_id,
+ u32 capability, u32 clnt_type)
+{
+ int i = 0;
+ int rc = 0;
+ struct msm_snddev_info *info;
+ u32 set_freq;
+ u32 session_mask = 0;
+ u32 clnt_type_mask = 0;
+
+ MM_DBG(": clnt_type 0x%08x\n", clnt_type);
+
+ if ((clnt_type == AUDDEV_CLNT_VOC) && (session_id != 0))
+ return -EINVAL;
+ if ((clnt_type == AUDDEV_CLNT_DEC)
+ && (session_id >= MAX_DEC_SESSIONS))
+ return -EINVAL;
+ if ((clnt_type == AUDDEV_CLNT_ENC)
+ && (session_id >= MAX_ENC_SESSIONS))
+ return -EINVAL;
+ session_mask = ((0x1 << session_id)) << (8 * (clnt_type-1));
+ clnt_type_mask = (0xFF << (8 * (clnt_type-1)));
+ if (!(*freq == 8000) && !(*freq == 11025) &&
+ !(*freq == 12000) && !(*freq == 16000) &&
+ !(*freq == 22050) && !(*freq == 24000) &&
+ !(*freq == 32000) && !(*freq == 44100) &&
+ !(*freq == 48000))
+ return -EINVAL;
+
+ for (i = 0; i < audio_dev_ctrl.num_dev; i++) {
+ info = audio_dev_ctrl.devs[i];
+ if ((info->sessions & session_mask)
+ && (info->capability & capability)) {
+ rc = 0;
+ if ((info->sessions & ~clnt_type_mask)
+ && ((*freq != 8000) && (*freq != 16000)
+ && (*freq != 48000))) {
+ if (clnt_type == AUDDEV_CLNT_ENC) {
+ routing_info.enc_freq[session_id].freq
+ = 0;
+ return -EPERM;
+ } else if (clnt_type == AUDDEV_CLNT_DEC) {
+ routing_info.dec_freq[session_id].freq
+ = 0;
+ return -EPERM;
+ }
+ }
+ if (*freq == info->set_sample_rate) {
+ rc = info->set_sample_rate;
+ continue;
+ }
+ set_freq = MAX(*freq, info->set_sample_rate);
+
+
+ if (clnt_type == AUDDEV_CLNT_DEC)
+ routing_info.dec_freq[session_id].freq
+ = set_freq;
+ else if (clnt_type == AUDDEV_CLNT_ENC)
+ routing_info.enc_freq[session_id].freq
+ = set_freq;
+ else if (capability == SNDDEV_CAP_TX)
+ routing_info.voice_tx_sample_rate = set_freq;
+
+ rc = set_freq;
+ *freq = set_freq;
+ /* There is difference in device sample rate to
+ * requested sample rate. So update device sample rate
+ * and propagate sample rate change event to active
+ * sessions of the device.
+ */
+ if (info->set_sample_rate != set_freq) {
+ info->set_sample_rate = set_freq;
+ if (info->opened) {
+ /* Ignore propagating sample rate
+ * change event to requested client
+ * session
+ */
+ if (clnt_type == AUDDEV_CLNT_DEC)
+ routing_info.\
+ dec_freq[session_id].evt = 1;
+ else if (clnt_type == AUDDEV_CLNT_ENC)
+ routing_info.\
+ enc_freq[session_id].evt = 1;
+ broadcast_event(AUDDEV_EVT_FREQ_CHG, i,
+ SESSION_IGNORE);
+ set_freq = info->dev_ops.set_freq(info,
+ set_freq);
+ broadcast_event(AUDDEV_EVT_DEV_RDY, i,
+ SESSION_IGNORE);
+ }
+ }
+ }
+ MM_DBG("info->set_sample_rate = %d\n", info->set_sample_rate);
+ MM_DBG("routing_info.enc_freq.freq = %d\n",
+ routing_info.enc_freq[session_id].freq);
+ }
+ return rc;
+}
+EXPORT_SYMBOL(msm_snddev_request_freq);
+
+int msm_snddev_enable_sidetone(u32 dev_id, u32 enable)
+{
+ int rc;
+ struct msm_snddev_info *dev_info;
+
+ MM_DBG("dev_id %d enable %d\n", dev_id, enable);
+
+ dev_info = audio_dev_ctrl_find_dev(dev_id);
+
+ if (IS_ERR(dev_info)) {
+ MM_ERR("bad dev_id %d\n", dev_id);
+ rc = -EINVAL;
+ } else if (!dev_info->dev_ops.enable_sidetone) {
+ MM_DBG("dev %d no sidetone support\n", dev_id);
+ rc = -EPERM;
+ } else
+ rc = dev_info->dev_ops.enable_sidetone(dev_info, enable);
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_snddev_enable_sidetone);
+
+static long audio_dev_ctrl_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int rc = 0;
+ struct audio_dev_ctrl_state *dev_ctrl = file->private_data;
+
+ mutex_lock(&session_lock);
+ switch (cmd) {
+ case AUDIO_GET_NUM_SND_DEVICE:
+ rc = put_user(dev_ctrl->num_dev, (uint32_t __user *) arg);
+ break;
+ case AUDIO_GET_SND_DEVICES:
+ rc = audio_dev_ctrl_get_devices(dev_ctrl, (void __user *) arg);
+ break;
+ case AUDIO_ENABLE_SND_DEVICE: {
+ struct msm_snddev_info *dev_info;
+ u32 dev_id;
+
+ if (get_user(dev_id, (u32 __user *) arg)) {
+ rc = -EFAULT;
+ break;
+ }
+ dev_info = audio_dev_ctrl_find_dev(dev_id);
+ if (IS_ERR(dev_info))
+ rc = PTR_ERR(dev_info);
+ else {
+ rc = dev_info->dev_ops.open(dev_info);
+ if (!rc)
+ dev_info->opened = 1;
+ wake_up(&audio_dev_ctrl.wait);
+ }
+ break;
+
+ }
+
+ case AUDIO_DISABLE_SND_DEVICE: {
+ struct msm_snddev_info *dev_info;
+ u32 dev_id;
+
+ if (get_user(dev_id, (u32 __user *) arg)) {
+ rc = -EFAULT;
+ break;
+ }
+ dev_info = audio_dev_ctrl_find_dev(dev_id);
+ if (IS_ERR(dev_info))
+ rc = PTR_ERR(dev_info);
+ else {
+ rc = dev_info->dev_ops.close(dev_info);
+ dev_info->opened = 0;
+ }
+ break;
+ }
+
+ case AUDIO_ROUTE_STREAM: {
+ struct msm_audio_route_config route_cfg;
+ struct msm_snddev_info *dev_info;
+
+ if (copy_from_user(&route_cfg, (void __user *) arg,
+ sizeof(struct msm_audio_route_config))) {
+ rc = -EFAULT;
+ break;
+ }
+ MM_DBG("%s: route cfg %d %d type\n", __func__,
+ route_cfg.dev_id, route_cfg.stream_type);
+ dev_info = audio_dev_ctrl_find_dev(route_cfg.dev_id);
+ if (IS_ERR(dev_info)) {
+ MM_ERR("%s: pass invalid dev_id\n", __func__);
+ rc = PTR_ERR(dev_info);
+ break;
+ }
+
+ switch (route_cfg.stream_type) {
+
+ case AUDIO_ROUTE_STREAM_VOICE_RX:
+ if (!(dev_info->capability & SNDDEV_CAP_RX) |
+ !(dev_info->capability & SNDDEV_CAP_VOICE)) {
+ rc = -EINVAL;
+ break;
+ }
+ dev_ctrl->voice_rx_dev = dev_info;
+ break;
+ case AUDIO_ROUTE_STREAM_VOICE_TX:
+ if (!(dev_info->capability & SNDDEV_CAP_TX) |
+ !(dev_info->capability & SNDDEV_CAP_VOICE)) {
+ rc = -EINVAL;
+ break;
+ }
+ dev_ctrl->voice_tx_dev = dev_info;
+ break;
+ }
+ break;
+ }
+
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&session_lock);
+ return rc;
+}
+
+static int audio_dev_ctrl_open(struct inode *inode, struct file *file)
+{
+ MM_DBG("open audio_dev_ctrl\n");
+ atomic_inc(&audio_dev_ctrl.opened);
+ file->private_data = &audio_dev_ctrl;
+ return 0;
+}
+
+static int audio_dev_ctrl_release(struct inode *inode, struct file *file)
+{
+ MM_DBG("release audio_dev_ctrl\n");
+ atomic_dec(&audio_dev_ctrl.opened);
+ return 0;
+}
+
+static const struct file_operations audio_dev_ctrl_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_dev_ctrl_open,
+ .release = audio_dev_ctrl_release,
+ .unlocked_ioctl = audio_dev_ctrl_ioctl,
+};
+
+
+struct miscdevice audio_dev_ctrl_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_audio_dev_ctrl",
+ .fops = &audio_dev_ctrl_fops,
+};
+
+/* session id is 32 bit routing mask per device
+ * 0-7 for voice clients
+ * 8-15 for Decoder clients
+ * 16-23 for Encoder clients
+ * 24-31 Do not care
+ */
+void broadcast_event(u32 evt_id, u32 dev_id, u32 session_id)
+{
+ int clnt_id = 0, i;
+ union auddev_evt_data *evt_payload;
+ struct msm_snd_evt_listner *callback;
+ struct msm_snddev_info *dev_info = NULL;
+ u32 session_mask = 0;
+ static int pending_sent;
+
+ MM_DBG(": evt_id = %d\n", evt_id);
+
+ if ((evt_id != AUDDEV_EVT_START_VOICE)
+ && (evt_id != AUDDEV_EVT_END_VOICE)
+ && (evt_id != AUDDEV_EVT_STREAM_VOL_CHG)
+ && (evt_id != AUDDEV_EVT_VOICE_STATE_CHG)) {
+ dev_info = audio_dev_ctrl_find_dev(dev_id);
+ if (IS_ERR(dev_info)) {
+ MM_ERR("pass invalid dev_id\n");
+ return;
+ }
+ }
+
+ if (event.cb != NULL)
+ callback = event.cb;
+ else
+ return;
+
+ evt_payload = kzalloc(sizeof(union auddev_evt_data),
+ GFP_KERNEL);
+ if (evt_payload == NULL) {
+ MM_ERR("Memory allocation for event payload failed\n");
+ return;
+ }
+
+ mutex_lock(&session_lock);
+
+ if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG)
+ routing_info.voice_state = dev_id;
+
+ for (; ;) {
+ if (!(evt_id & callback->evt_id)) {
+ if (callback->cb_next == NULL)
+ break;
+ else {
+ callback = callback->cb_next;
+ continue;
+ }
+ }
+ clnt_id = callback->clnt_id;
+ memset(evt_payload, 0, sizeof(union auddev_evt_data));
+
+ if ((evt_id == AUDDEV_EVT_START_VOICE)
+ || (evt_id == AUDDEV_EVT_END_VOICE))
+ goto skip_check;
+ if (callback->clnt_type == AUDDEV_CLNT_AUDIOCAL)
+ goto aud_cal;
+
+ session_mask = (0x1 << (clnt_id))
+ << (8 * ((int)callback->clnt_type-1));
+
+ if ((evt_id == AUDDEV_EVT_STREAM_VOL_CHG) || \
+ (evt_id == AUDDEV_EVT_VOICE_STATE_CHG)) {
+ MM_DBG("AUDDEV_EVT_STREAM_VOL_CHG or\
+ AUDDEV_EVT_VOICE_STATE_CHG\n");
+ goto volume_strm;
+ }
+
+ MM_DBG("dev_info->sessions = %08x\n", dev_info->sessions);
+
+ if ((!session_id && !(dev_info->sessions & session_mask)) ||
+ (session_id && ((dev_info->sessions & session_mask) !=
+ session_id))) {
+ if (callback->cb_next == NULL)
+ break;
+ else {
+ callback = callback->cb_next;
+ continue;
+ }
+ }
+ if (evt_id == AUDDEV_EVT_DEV_CHG_VOICE)
+ goto voc_events;
+
+volume_strm:
+ if (callback->clnt_type == AUDDEV_CLNT_DEC) {
+ MM_DBG("AUDDEV_CLNT_DEC\n");
+ if (evt_id == AUDDEV_EVT_STREAM_VOL_CHG) {
+ MM_DBG("clnt_id = %d, session_id = 0x%8x\n",
+ clnt_id, session_id);
+ if (session_mask != session_id)
+ goto sent_dec;
+ else
+ evt_payload->session_vol =
+ msm_vol_ctl.volume;
+ } else if (evt_id == AUDDEV_EVT_FREQ_CHG) {
+ if (routing_info.dec_freq[clnt_id].evt) {
+ routing_info.dec_freq[clnt_id].evt
+ = 0;
+ goto sent_dec;
+ } else if (routing_info.dec_freq[clnt_id].freq
+ == dev_info->set_sample_rate)
+ goto sent_dec;
+ else {
+ evt_payload->freq_info.sample_rate
+ = dev_info->set_sample_rate;
+ evt_payload->freq_info.dev_type
+ = dev_info->capability;
+ evt_payload->freq_info.acdb_dev_id
+ = dev_info->acdb_id;
+ }
+ /* Propogate device information to client */
+ } else if (evt_id == AUDDEV_EVT_DEVICE_INFO) {
+ evt_payload->devinfo.dev_id
+ = dev_info->copp_id;
+ evt_payload->devinfo.acdb_id
+ = dev_info->acdb_id;
+ evt_payload->devinfo.dev_type =
+ (dev_info->capability & SNDDEV_CAP_TX) ?
+ SNDDEV_CAP_TX : SNDDEV_CAP_RX;
+ evt_payload->devinfo.sample_rate
+ = dev_info->sample_rate;
+ if (session_id == SESSION_IGNORE)
+ evt_payload->devinfo.sessions
+ = dev_info->sessions;
+ else
+ evt_payload->devinfo.sessions
+ = session_id;
+ evt_payload->devinfo.sessions =
+ (evt_payload->devinfo.sessions >>
+ ((AUDDEV_CLNT_DEC-1) * 8));
+ } else if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG)
+ evt_payload->voice_state =
+ routing_info.voice_state;
+ else
+ evt_payload->routing_id = dev_info->copp_id;
+ callback->auddev_evt_listener(
+ evt_id,
+ evt_payload,
+ callback->private_data);
+sent_dec:
+ if ((evt_id != AUDDEV_EVT_STREAM_VOL_CHG) &&
+ (evt_id != AUDDEV_EVT_VOICE_STATE_CHG))
+ routing_info.dec_freq[clnt_id].freq
+ = dev_info->set_sample_rate;
+
+ if (callback->cb_next == NULL)
+ break;
+ else {
+ callback = callback->cb_next;
+ continue;
+ }
+ }
+ if (callback->clnt_type == AUDDEV_CLNT_ENC) {
+
+ MM_DBG("AUDDEV_CLNT_ENC\n");
+ if (evt_id == AUDDEV_EVT_FREQ_CHG) {
+ if (routing_info.enc_freq[clnt_id].evt) {
+ routing_info.enc_freq[clnt_id].evt
+ = 0;
+ goto sent_enc;
+ } else {
+ evt_payload->freq_info.sample_rate
+ = dev_info->set_sample_rate;
+ evt_payload->freq_info.dev_type
+ = dev_info->capability;
+ evt_payload->freq_info.acdb_dev_id
+ = dev_info->acdb_id;
+ }
+ /* Propogate device information to client */
+ } else if (evt_id == AUDDEV_EVT_DEVICE_INFO) {
+ evt_payload->devinfo.dev_id
+ = dev_info->copp_id;
+ evt_payload->devinfo.acdb_id
+ = dev_info->acdb_id;
+ evt_payload->devinfo.dev_type =
+ (dev_info->capability & SNDDEV_CAP_TX) ?
+ SNDDEV_CAP_TX : SNDDEV_CAP_RX;
+ evt_payload->devinfo.sample_rate
+ = dev_info->sample_rate;
+ if (session_id == SESSION_IGNORE)
+ evt_payload->devinfo.sessions
+ = dev_info->sessions;
+ else
+ evt_payload->devinfo.sessions
+ = session_id;
+ evt_payload->devinfo.sessions =
+ (evt_payload->devinfo.sessions >>
+ ((AUDDEV_CLNT_ENC-1) * 8));
+ } else if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG)
+ evt_payload->voice_state =
+ routing_info.voice_state;
+ else
+ evt_payload->routing_id = dev_info->copp_id;
+ callback->auddev_evt_listener(
+ evt_id,
+ evt_payload,
+ callback->private_data);
+sent_enc:
+ if (callback->cb_next == NULL)
+ break;
+ else {
+ callback = callback->cb_next;
+ continue;
+ }
+ }
+aud_cal:
+ if (callback->clnt_type == AUDDEV_CLNT_AUDIOCAL) {
+ int temp_sessions;
+ MM_DBG("AUDDEV_CLNT_AUDIOCAL\n");
+ if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG)
+ evt_payload->voice_state =
+ routing_info.voice_state;
+ else if (!dev_info->sessions)
+ goto sent_aud_cal;
+ else {
+ evt_payload->audcal_info.dev_id =
+ dev_info->copp_id;
+ evt_payload->audcal_info.acdb_id =
+ dev_info->acdb_id;
+ evt_payload->audcal_info.dev_type =
+ (dev_info->capability & SNDDEV_CAP_TX) ?
+ SNDDEV_CAP_TX : SNDDEV_CAP_RX;
+ evt_payload->audcal_info.sample_rate =
+ dev_info->set_sample_rate ?
+ dev_info->set_sample_rate :
+ dev_info->sample_rate;
+ }
+ if (evt_payload->audcal_info.dev_type ==
+ SNDDEV_CAP_TX) {
+ if (session_id == SESSION_IGNORE)
+ temp_sessions = dev_info->sessions;
+ else
+ temp_sessions = session_id;
+ evt_payload->audcal_info.sessions =
+ (temp_sessions >>
+ ((AUDDEV_CLNT_ENC-1) * 8));
+ } else {
+ if (session_id == SESSION_IGNORE)
+ temp_sessions = dev_info->sessions;
+ else
+ temp_sessions = session_id;
+ evt_payload->audcal_info.sessions =
+ (temp_sessions >>
+ ((AUDDEV_CLNT_DEC-1) * 8));
+ }
+ callback->auddev_evt_listener(
+ evt_id,
+ evt_payload,
+ callback->private_data);
+
+sent_aud_cal:
+ if (callback->cb_next == NULL)
+ break;
+ else {
+ callback = callback->cb_next;
+ continue;
+ }
+ }
+skip_check:
+voc_events:
+ if (callback->clnt_type == AUDDEV_CLNT_VOC) {
+ MM_DBG("AUDDEV_CLNT_VOC\n");
+ if (evt_id == AUDDEV_EVT_DEV_RLS) {
+ if (!pending_sent)
+ goto sent_voc;
+ else
+ pending_sent = 0;
+ }
+ if (evt_id == AUDDEV_EVT_REL_PENDING)
+ pending_sent = 1;
+
+ if (evt_id == AUDDEV_EVT_DEVICE_VOL_MUTE_CHG) {
+ if (dev_info->capability & SNDDEV_CAP_TX) {
+ evt_payload->voc_vm_info.dev_type =
+ SNDDEV_CAP_TX;
+ evt_payload->voc_vm_info.acdb_dev_id =
+ dev_info->acdb_id;
+ evt_payload->
+ voc_vm_info.dev_vm_val.mute =
+ routing_info.tx_mute;
+ } else {
+ evt_payload->voc_vm_info.dev_type =
+ SNDDEV_CAP_RX;
+ evt_payload->voc_vm_info.acdb_dev_id =
+ dev_info->acdb_id;
+ evt_payload->
+ voc_vm_info.dev_vm_val.vol =
+ routing_info.voice_rx_vol;
+ }
+ } else if ((evt_id == AUDDEV_EVT_START_VOICE)
+ || (evt_id == AUDDEV_EVT_END_VOICE))
+ memset(evt_payload, 0,
+ sizeof(union auddev_evt_data));
+ else if (evt_id == AUDDEV_EVT_FREQ_CHG) {
+ if (routing_info.voice_tx_sample_rate
+ != dev_info->set_sample_rate) {
+ routing_info.voice_tx_sample_rate
+ = dev_info->set_sample_rate;
+ evt_payload->freq_info.sample_rate
+ = dev_info->set_sample_rate;
+ evt_payload->freq_info.dev_type
+ = dev_info->capability;
+ evt_payload->freq_info.acdb_dev_id
+ = dev_info->acdb_id;
+ } else
+ goto sent_voc;
+ } else if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG)
+ evt_payload->voice_state =
+ routing_info.voice_state;
+ else {
+ evt_payload->voc_devinfo.dev_type =
+ (dev_info->capability & SNDDEV_CAP_TX) ?
+ SNDDEV_CAP_TX : SNDDEV_CAP_RX;
+ evt_payload->voc_devinfo.acdb_dev_id =
+ dev_info->acdb_id;
+ evt_payload->voc_devinfo.dev_sample =
+ dev_info->set_sample_rate ?
+ dev_info->set_sample_rate :
+ dev_info->sample_rate;
+ evt_payload->voc_devinfo.dev_id = dev_id;
+ if (dev_info->capability & SNDDEV_CAP_RX) {
+ for (i = 0; i < VOC_RX_VOL_ARRAY_NUM;
+ i++) {
+ evt_payload->
+ voc_devinfo.max_rx_vol[i] =
+ dev_info->max_voc_rx_vol[i];
+ evt_payload
+ ->voc_devinfo.min_rx_vol[i] =
+ dev_info->min_voc_rx_vol[i];
+ }
+ }
+ }
+ callback->auddev_evt_listener(
+ evt_id,
+ evt_payload,
+ callback->private_data);
+ if (evt_id == AUDDEV_EVT_DEV_RLS)
+ dev_info->sessions &= ~(0xFF);
+sent_voc:
+ if (callback->cb_next == NULL)
+ break;
+ else {
+ callback = callback->cb_next;
+ continue;
+ }
+ }
+ }
+ kfree(evt_payload);
+ mutex_unlock(&session_lock);
+}
+EXPORT_SYMBOL(broadcast_event);
+
+
+void mixer_post_event(u32 evt_id, u32 id)
+{
+
+ MM_DBG("evt_id = %d\n", evt_id);
+ switch (evt_id) {
+ case AUDDEV_EVT_DEV_CHG_VOICE: /* Called from Voice_route */
+ broadcast_event(AUDDEV_EVT_DEV_CHG_VOICE, id, SESSION_IGNORE);
+ break;
+ case AUDDEV_EVT_DEV_RDY:
+ broadcast_event(AUDDEV_EVT_DEV_RDY, id, SESSION_IGNORE);
+ break;
+ case AUDDEV_EVT_DEV_RLS:
+ broadcast_event(AUDDEV_EVT_DEV_RLS, id, SESSION_IGNORE);
+ break;
+ case AUDDEV_EVT_REL_PENDING:
+ broadcast_event(AUDDEV_EVT_REL_PENDING, id, SESSION_IGNORE);
+ break;
+ case AUDDEV_EVT_DEVICE_VOL_MUTE_CHG:
+ broadcast_event(AUDDEV_EVT_DEVICE_VOL_MUTE_CHG, id,
+ SESSION_IGNORE);
+ break;
+ case AUDDEV_EVT_STREAM_VOL_CHG:
+ broadcast_event(AUDDEV_EVT_STREAM_VOL_CHG, id,
+ SESSION_IGNORE);
+ break;
+ case AUDDEV_EVT_START_VOICE:
+ broadcast_event(AUDDEV_EVT_START_VOICE,
+ id, SESSION_IGNORE);
+ break;
+ case AUDDEV_EVT_END_VOICE:
+ broadcast_event(AUDDEV_EVT_END_VOICE,
+ id, SESSION_IGNORE);
+ break;
+ case AUDDEV_EVT_FREQ_CHG:
+ broadcast_event(AUDDEV_EVT_FREQ_CHG, id, SESSION_IGNORE);
+ break;
+ default:
+ break;
+ }
+}
+EXPORT_SYMBOL(mixer_post_event);
+
+static int __init audio_dev_ctrl_init(void)
+{
+#ifdef CONFIG_DEBUG_FS
+ char name[sizeof "rtc_get_device"+1];
+#endif
+
+ init_waitqueue_head(&audio_dev_ctrl.wait);
+
+ event.cb = NULL;
+
+ atomic_set(&audio_dev_ctrl.opened, 0);
+ audio_dev_ctrl.num_dev = 0;
+ audio_dev_ctrl.voice_tx_dev = NULL;
+ audio_dev_ctrl.voice_rx_dev = NULL;
+ routing_info.voice_state = VOICE_STATE_INVALID;
+#ifdef CONFIG_DEBUG_FS
+ snprintf(name, sizeof name, "rtc_get_device");
+ dentry = debugfs_create_file(name, S_IFREG | S_IRUGO | S_IWUGO,
+ NULL, NULL, &rtc_acdb_debug_fops);
+ if (IS_ERR(dentry))
+ MM_DBG("debugfs_create_file failed\n");
+#endif
+
+ return misc_register(&audio_dev_ctrl_misc);
+}
+
+static void __exit audio_dev_ctrl_exit(void)
+{
+#ifdef CONFIG_DEBUG_FS
+ if (dentry)
+ debugfs_remove(dentry);
+#endif
+
+}
+module_init(audio_dev_ctrl_init);
+module_exit(audio_dev_ctrl_exit);
+
+MODULE_DESCRIPTION("MSM 7K Audio Device Control driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_evrc.c b/arch/arm/mach-msm/qdsp5v2/audio_evrc.c
new file mode 100644
index 0000000..8e7db90
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_evrc.c
@@ -0,0 +1,1624 @@
+/*
+ * Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
+ *
+ * This code also borrows from audio_aac.c, which is
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ *
+ * 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org.
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/earlysuspend.h>
+#include <linux/android_pmem.h>
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+#include <linux/msm_audio.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <linux/slab.h>
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/qdsp5audplaycmdi.h>
+#include <mach/qdsp5v2/qdsp5audplaymsg.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/qdsp5v2/audpp.h>
+#include <mach/debug_mm.h>
+
+/* Hold 30 packets of 24 bytes each and 14 bytes of meta in */
+#define BUFSZ 734
+#define DMASZ (BUFSZ * 2)
+
+#define AUDDEC_DEC_EVRC 12
+
+#define PCM_BUFSZ_MIN 1624 /* 100ms worth of data and
+ and 24 bytes of meta out */
+#define PCM_BUF_MAX_COUNT 5
+/* DSP only accepts 5 buffers at most
+ * but support 2 buffers currently
+ */
+#define EVRC_DECODED_FRSZ 320 /* EVRC 20ms 8KHz mono PCM size */
+
+#define ROUTING_MODE_FTRT 1
+#define ROUTING_MODE_RT 2
+/* Decoder status received from AUDPPTASK */
+#define AUDPP_DEC_STATUS_SLEEP 0
+#define AUDPP_DEC_STATUS_INIT 1
+#define AUDPP_DEC_STATUS_CFG 2
+#define AUDPP_DEC_STATUS_PLAY 3
+
+#define AUDEVRC_METAFIELD_MASK 0xFFFF0000
+#define AUDEVRC_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */
+#define AUDEVRC_EOS_FLG_MASK 0x01
+#define AUDEVRC_EOS_NONE 0x0 /* No EOS detected */
+#define AUDEVRC_EOS_SET 0x1 /* EOS set in meta field */
+
+#define AUDEVRC_EVENT_NUM 10 /* Default number of pre-allocated event packets */
+
+struct buffer {
+ void *data;
+ unsigned size;
+ unsigned used; /* Input usage actual DSP produced PCM size */
+ unsigned addr;
+ unsigned short mfield_sz; /*only useful for data has meta field */
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+struct audevrc_suspend_ctl {
+ struct early_suspend node;
+ struct audio *audio;
+};
+#endif
+
+struct audevrc_event{
+ struct list_head list;
+ int event_type;
+ union msm_audio_event_payload payload;
+};
+
+struct audio {
+ struct buffer out[2];
+
+ spinlock_t dsp_lock;
+
+ uint8_t out_head;
+ uint8_t out_tail;
+ uint8_t out_needed; /* number of buffers the dsp is waiting for */
+
+ atomic_t out_bytes;
+
+ struct mutex lock;
+ struct mutex write_lock;
+ wait_queue_head_t write_wait;
+
+ /* Host PCM section */
+ struct buffer in[PCM_BUF_MAX_COUNT];
+ struct mutex read_lock;
+ wait_queue_head_t read_wait; /* Wait queue for read */
+ char *read_data; /* pointer to reader buffer */
+ int32_t read_phys; /* physical address of reader buffer */
+ uint8_t read_next; /* index to input buffers to be read next */
+ uint8_t fill_next; /* index to buffer that DSP should be filling */
+ uint8_t pcm_buf_count; /* number of pcm buffer allocated */
+ /* ---- End of Host PCM section */
+
+ struct msm_adsp_module *audplay;
+
+ /* data allocated for various buffers */
+ char *data;
+ int32_t phys; /* physical address of write buffer */
+
+ int mfield; /* meta field embedded in data */
+ int rflush; /* Read flush */
+ int wflush; /* Write flush */
+ uint8_t opened:1;
+ uint8_t enabled:1;
+ uint8_t running:1;
+ uint8_t stopped:1; /* set when stopped, cleared on flush */
+ uint8_t pcm_feedback:1;
+ uint8_t buf_refresh:1;
+ int teos; /* valid only if tunnel mode & no data left for decoder */
+ enum msm_aud_decoder_state dec_state; /* Represents decoder state */
+
+ const char *module_name;
+ unsigned queue_id;
+ uint16_t dec_id;
+ uint32_t read_ptr_offset;
+ int16_t source;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct audevrc_suspend_ctl suspend_ctl;
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dentry;
+#endif
+
+ wait_queue_head_t wait;
+ struct list_head free_event_queue;
+ struct list_head event_queue;
+ wait_queue_head_t event_wait;
+ spinlock_t event_queue_lock;
+ struct mutex get_event_lock;
+ int event_abort;
+ /* AV sync Info */
+ int avsync_flag; /* Flag to indicate feedback from DSP */
+ wait_queue_head_t avsync_wait;/* Wait queue for AV Sync Message */
+ /* flags, 48 bits sample/bytes counter per channel */
+ uint16_t avsync[AUDPP_AVSYNC_CH_COUNT * AUDPP_AVSYNC_NUM_WORDS + 1];
+
+ uint32_t device_events;
+
+ int eq_enable;
+ int eq_needs_commit;
+ struct audpp_cmd_cfg_object_params_eqalizer eq;
+ struct audpp_cmd_cfg_object_params_volume vol_pan;
+};
+
+static int auddec_dsp_config(struct audio *audio, int enable);
+static void audpp_cmd_cfg_adec_params(struct audio *audio);
+static void audpp_cmd_cfg_routing_mode(struct audio *audio);
+static void audevrc_send_data(struct audio *audio, unsigned needed);
+static void audevrc_dsp_event(void *private, unsigned id, uint16_t *msg);
+static void audevrc_config_hostpcm(struct audio *audio);
+static void audevrc_buffer_refresh(struct audio *audio);
+static void audevrc_post_event(struct audio *audio, int type,
+ union msm_audio_event_payload payload);
+
+/* must be called with audio->lock held */
+static int audevrc_enable(struct audio *audio)
+{
+ if (audio->enabled)
+ return 0;
+
+ audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+ audio->out_tail = 0;
+ audio->out_needed = 0;
+
+ if (msm_adsp_enable(audio->audplay)) {
+ MM_ERR("msm_adsp_enable(audplay) failed\n");
+ return -ENODEV;
+ }
+
+ if (audpp_enable(audio->dec_id, audevrc_dsp_event, audio)) {
+ MM_ERR("audpp_enable() failed\n");
+ msm_adsp_disable(audio->audplay);
+ return -ENODEV;
+ }
+ audio->enabled = 1;
+ return 0;
+}
+
+static void evrc_listner(u32 evt_id, union auddev_evt_data *evt_payload,
+ void *private_data)
+{
+ struct audio *audio = (struct audio *) private_data;
+ switch (evt_id) {
+ case AUDDEV_EVT_DEV_RDY:
+ MM_DBG(":AUDDEV_EVT_DEV_RDY\n");
+ audio->source |= (0x1 << evt_payload->routing_id);
+ if (audio->running == 1 && audio->enabled == 1)
+ audpp_route_stream(audio->dec_id, audio->source);
+ break;
+ case AUDDEV_EVT_DEV_RLS:
+ MM_DBG(":AUDDEV_EVT_DEV_RLS\n");
+ audio->source &= ~(0x1 << evt_payload->routing_id);
+ if (audio->running == 1 && audio->enabled == 1)
+ audpp_route_stream(audio->dec_id, audio->source);
+ break;
+ case AUDDEV_EVT_STREAM_VOL_CHG:
+ audio->vol_pan.volume = evt_payload->session_vol;
+ MM_DBG(":AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n",
+ audio->vol_pan.volume);
+ if (audio->running)
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ break;
+ default:
+ MM_ERR(":ERROR:wrong event\n");
+ break;
+ }
+}
+/* must be called with audio->lock held */
+static int audevrc_disable(struct audio *audio)
+{
+ int rc = 0;
+ if (audio->enabled) {
+ audio->enabled = 0;
+ audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+ auddec_dsp_config(audio, 0);
+ rc = wait_event_interruptible_timeout(audio->wait,
+ audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+ msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE)
+ rc = -EFAULT;
+ else
+ rc = 0;
+ wake_up(&audio->write_wait);
+ wake_up(&audio->read_wait);
+ msm_adsp_disable(audio->audplay);
+ audpp_disable(audio->dec_id, audio);
+ audio->out_needed = 0;
+ }
+ return rc;
+}
+
+/* ------------------- dsp --------------------- */
+
+static void audevrc_update_pcm_buf_entry(struct audio *audio,
+ uint32_t *payload)
+{
+ uint8_t index;
+ unsigned long flags;
+
+ if (audio->rflush)
+ return;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ for (index = 0; index < payload[1]; index++) {
+ if (audio->in[audio->fill_next].addr
+ == payload[2 + index * 2]) {
+ MM_DBG("in[%d] ready\n", audio->fill_next);
+ audio->in[audio->fill_next].used =
+ payload[3 + index * 2];
+ if ((++audio->fill_next) == audio->pcm_buf_count)
+ audio->fill_next = 0;
+
+ } else {
+ MM_ERR("expected=%x ret=%x\n",
+ audio->in[audio->fill_next].addr,
+ payload[1 + index * 2]);
+ break;
+ }
+ }
+ if (audio->in[audio->fill_next].used == 0) {
+ audevrc_buffer_refresh(audio);
+ } else {
+ MM_DBG("read cannot keep up\n");
+ audio->buf_refresh = 1;
+ }
+ wake_up(&audio->read_wait);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+static void audplay_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent) (void *ptr, size_t len))
+{
+ struct audio *audio = data;
+ uint32_t msg[28];
+ getevent(msg, sizeof(msg));
+
+ MM_DBG("msg_id=%x\n", id);
+ switch (id) {
+ case AUDPLAY_MSG_DEC_NEEDS_DATA:
+ audevrc_send_data(audio, 1);
+ break;
+ case AUDPLAY_MSG_BUFFER_UPDATE:
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ audevrc_update_pcm_buf_entry(audio, msg);
+ break;
+ case ADSP_MESSAGE_ID:
+ MM_DBG("Received ADSP event: module enable(audplaytask)\n");
+ break;
+ default:
+ MM_ERR("unexpected message from decoder \n");
+ }
+}
+
+static void audevrc_dsp_event(void *private, unsigned id, uint16_t *msg)
+{
+ struct audio *audio = private;
+
+ switch (id) {
+ case AUDPP_MSG_STATUS_MSG:{
+ unsigned status = msg[1];
+
+ switch (status) {
+ case AUDPP_DEC_STATUS_SLEEP: {
+ uint16_t reason = msg[2];
+ MM_DBG("decoder status:sleep reason = \
+ 0x%04x\n", reason);
+ if ((reason == AUDPP_MSG_REASON_MEM)
+ || (reason ==
+ AUDPP_MSG_REASON_NODECODER)) {
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_FAILURE;
+ wake_up(&audio->wait);
+ } else if (reason == AUDPP_MSG_REASON_NONE) {
+ /* decoder is in disable state */
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_CLOSE;
+ wake_up(&audio->wait);
+ }
+ break;
+ }
+ case AUDPP_DEC_STATUS_INIT:
+ MM_DBG("decoder status: init \n");
+ if (audio->pcm_feedback)
+ audpp_cmd_cfg_routing_mode(audio);
+ else
+ audpp_cmd_cfg_adec_params(audio);
+ break;
+
+ case AUDPP_DEC_STATUS_CFG:
+ MM_DBG("decoder status: cfg \n");
+ break;
+ case AUDPP_DEC_STATUS_PLAY:
+ MM_DBG("decoder status: play \n");
+ audpp_route_stream(audio->dec_id,
+ audio->source);
+ if (audio->pcm_feedback) {
+ audevrc_config_hostpcm(audio);
+ audevrc_buffer_refresh(audio);
+ }
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_SUCCESS;
+ wake_up(&audio->wait);
+ break;
+ default:
+ MM_ERR("unknown decoder status \n");
+ }
+ break;
+ }
+ case AUDPP_MSG_CFG_MSG:
+ if (msg[0] == AUDPP_MSG_ENA_ENA) {
+ MM_DBG("CFG_MSG ENABLE\n");
+ auddec_dsp_config(audio, 1);
+ audio->out_needed = 0;
+ audio->running = 1;
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ audpp_dsp_set_eq(audio->dec_id, audio->eq_enable,
+ &audio->eq, POPP);
+ } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+ MM_DBG("CFG_MSG DISABLE\n");
+ audio->running = 0;
+ } else {
+ MM_DBG("CFG_MSG %d?\n", msg[0]);
+ }
+ break;
+ case AUDPP_MSG_ROUTING_ACK:
+ MM_DBG("ROUTING_ACK\n");
+ audpp_cmd_cfg_adec_params(audio);
+ break;
+ case AUDPP_MSG_FLUSH_ACK:
+ MM_DBG("FLUSH_ACK\n");
+ audio->wflush = 0;
+ audio->rflush = 0;
+ wake_up(&audio->write_wait);
+ if (audio->pcm_feedback)
+ audevrc_buffer_refresh(audio);
+ break;
+ case AUDPP_MSG_PCMDMAMISSED:
+ MM_DBG("PCMDMAMISSED\n");
+ audio->teos = 1;
+ wake_up(&audio->write_wait);
+ break;
+
+ case AUDPP_MSG_AVSYNC_MSG:
+ MM_DBG("AUDPP_MSG_AVSYNC_MSG\n");
+ memcpy(&audio->avsync[0], msg, sizeof(audio->avsync));
+ audio->avsync_flag = 1;
+ wake_up(&audio->avsync_wait);
+ break;
+
+ default:
+ MM_ERR("UNKNOWN (%d)\n", id);
+ }
+
+}
+
+struct msm_adsp_ops audplay_adsp_ops_evrc = {
+ .event = audplay_dsp_event,
+};
+
+#define audplay_send_queue0(audio, cmd, len) \
+ msm_adsp_write(audio->audplay, audio->queue_id, \
+ cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+ struct audpp_cmd_cfg_dec_type cfg_dec_cmd;
+
+ memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd));
+
+ cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+ if (enable)
+ cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_EVRC;
+ else
+ cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_DIS_DEC_V;
+ cfg_dec_cmd.dm_mode = 0x0;
+ cfg_dec_cmd.stream_id = audio->dec_id;
+
+ return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd));
+}
+
+static void audpp_cmd_cfg_adec_params(struct audio *audio)
+{
+ struct audpp_cmd_cfg_adec_params_evrc cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+ cmd.common.length = sizeof(cmd);
+ cmd.common.dec_id = audio->dec_id;
+ cmd.common.input_sampling_frequency = 8000;
+ cmd.stereo_cfg = AUDPP_CMD_PCM_INTF_MONO_V;
+
+ audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_routing_mode(struct audio *audio)
+{
+ struct audpp_cmd_routing_mode cmd;
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_ROUTING_MODE;
+ cmd.object_number = audio->dec_id;
+ if (audio->pcm_feedback)
+ cmd.routing_mode = ROUTING_MODE_FTRT;
+ else
+ cmd.routing_mode = ROUTING_MODE_RT;
+
+ audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static int audplay_dsp_send_data_avail(struct audio *audio,
+ unsigned idx, unsigned len)
+{
+ struct audplay_cmd_bitstream_data_avail_nt2 cmd;
+
+ cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2;
+ if (audio->mfield)
+ cmd.decoder_id = AUDEVRC_METAFIELD_MASK |
+ (audio->out[idx].mfield_sz >> 1);
+ else
+ cmd.decoder_id = audio->dec_id;
+ cmd.buf_ptr = audio->out[idx].addr;
+ cmd.buf_size = len / 2;
+ cmd.partition_number = 0;
+ return audplay_send_queue0(audio, &cmd, sizeof(cmd));
+}
+
+static void audevrc_buffer_refresh(struct audio *audio)
+{
+ struct audplay_cmd_buffer_refresh refresh_cmd;
+
+ refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH;
+ refresh_cmd.num_buffers = 1;
+ refresh_cmd.buf0_address = audio->in[audio->fill_next].addr;
+ refresh_cmd.buf0_length = audio->in[audio->fill_next].size;
+
+ refresh_cmd.buf_read_count = 0;
+ MM_DBG("buf0_addr=%x buf0_len=%d\n", refresh_cmd.buf0_address,
+ refresh_cmd.buf0_length);
+ audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd));
+}
+
+static void audevrc_config_hostpcm(struct audio *audio)
+{
+ struct audplay_cmd_hpcm_buf_cfg cfg_cmd;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG;
+ cfg_cmd.max_buffers = 1;
+ cfg_cmd.byte_swap = 0;
+ cfg_cmd.hostpcm_config = (0x8000) | (0x4000);
+ cfg_cmd.feedback_frequency = 1;
+ cfg_cmd.partition_number = 0;
+ audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd));
+
+}
+
+static void audevrc_send_data(struct audio *audio, unsigned needed)
+{
+ struct buffer *frame;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (!audio->running)
+ goto done;
+
+ if (needed && !audio->wflush) {
+ /* We were called from the callback because the DSP
+ * requested more data. Note that the DSP does want
+ * more data, and if a buffer was in-flight, mark it
+ * as available (since the DSP must now be done with
+ * it).
+ */
+ audio->out_needed = 1;
+ frame = audio->out + audio->out_tail;
+ if (frame->used == 0xffffffff) {
+ MM_DBG("frame %d free\n", audio->out_tail);
+ frame->used = 0;
+ audio->out_tail ^= 1;
+ wake_up(&audio->write_wait);
+ }
+ }
+
+ if (audio->out_needed) {
+ /* If the DSP currently wants data and we have a
+ * buffer available, we will send it and reset
+ * the needed flag. We'll mark the buffer as in-flight
+ * so that it won't be recycled until the next buffer
+ * is requested
+ */
+
+ frame = audio->out + audio->out_tail;
+ if (frame->used) {
+ BUG_ON(frame->used == 0xffffffff);
+ MM_DBG("frame %d busy\n", audio->out_tail);
+ audplay_dsp_send_data_avail(audio, audio->out_tail,
+ frame->used);
+ frame->used = 0xffffffff;
+ audio->out_needed = 0;
+ }
+ }
+done:
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+/* ------------------- device --------------------- */
+
+static void audevrc_flush(struct audio *audio)
+{
+ audio->out[0].used = 0;
+ audio->out[1].used = 0;
+ audio->out_head = 0;
+ audio->out_tail = 0;
+ audio->out_needed = 0;
+ atomic_set(&audio->out_bytes, 0);
+}
+
+static void audevrc_flush_pcm_buf(struct audio *audio)
+{
+ uint8_t index;
+
+ for (index = 0; index < PCM_BUF_MAX_COUNT; index++)
+ audio->in[index].used = 0;
+
+ audio->buf_refresh = 0;
+ audio->read_next = 0;
+ audio->fill_next = 0;
+}
+
+static void audevrc_ioport_reset(struct audio *audio)
+{
+ /* Make sure read/write thread are free from
+ * sleep and knowing that system is not able
+ * to process io request at the moment
+ */
+ wake_up(&audio->write_wait);
+ mutex_lock(&audio->write_lock);
+ audevrc_flush(audio);
+ mutex_unlock(&audio->write_lock);
+ wake_up(&audio->read_wait);
+ mutex_lock(&audio->read_lock);
+ audevrc_flush_pcm_buf(audio);
+ mutex_unlock(&audio->read_lock);
+ audio->avsync_flag = 1;
+ wake_up(&audio->avsync_wait);
+}
+
+static int audevrc_events_pending(struct audio *audio)
+{
+ unsigned long flags;
+ int empty;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ empty = !list_empty(&audio->event_queue);
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+ return empty || audio->event_abort;
+}
+
+static void audevrc_reset_event_queue(struct audio *audio)
+{
+ unsigned long flags;
+ struct audevrc_event *drv_evt;
+ struct list_head *ptr, *next;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ list_for_each_safe(ptr, next, &audio->event_queue) {
+ drv_evt = list_first_entry(&audio->event_queue,
+ struct audevrc_event, list);
+ list_del(&drv_evt->list);
+ kfree(drv_evt);
+ }
+ list_for_each_safe(ptr, next, &audio->free_event_queue) {
+ drv_evt = list_first_entry(&audio->free_event_queue,
+ struct audevrc_event, list);
+ list_del(&drv_evt->list);
+ kfree(drv_evt);
+ }
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+ return;
+}
+
+
+static long audevrc_process_event_req(struct audio *audio, void __user *arg)
+{
+ long rc;
+ struct msm_audio_event usr_evt;
+ struct audevrc_event *drv_evt = NULL;
+ int timeout;
+ unsigned long flags;
+
+ if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event)))
+ return -EFAULT;
+
+ timeout = (int) usr_evt.timeout_ms;
+
+ if (timeout > 0) {
+ rc = wait_event_interruptible_timeout(
+ audio->event_wait, audevrc_events_pending(audio),
+ msecs_to_jiffies(timeout));
+ if (rc == 0)
+ return -ETIMEDOUT;
+ } else {
+ rc = wait_event_interruptible(
+ audio->event_wait, audevrc_events_pending(audio));
+ }
+
+ if (rc < 0)
+ return rc;
+
+ if (audio->event_abort) {
+ audio->event_abort = 0;
+ return -ENODEV;
+ }
+
+ rc = 0;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ if (!list_empty(&audio->event_queue)) {
+ drv_evt = list_first_entry(&audio->event_queue,
+ struct audevrc_event, list);
+ list_del(&drv_evt->list);
+ }
+ if (drv_evt) {
+ usr_evt.event_type = drv_evt->event_type;
+ usr_evt.event_payload = drv_evt->payload;
+ list_add_tail(&drv_evt->list, &audio->free_event_queue);
+ } else
+ rc = -1;
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+ if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt)))
+ rc = -EFAULT;
+
+ return rc;
+}
+
+static int audio_enable_eq(struct audio *audio, int enable)
+{
+ if (audio->eq_enable == enable && !audio->eq_needs_commit)
+ return 0;
+
+ audio->eq_enable = enable;
+
+ if (audio->running) {
+ audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq, POPP);
+ audio->eq_needs_commit = 0;
+ }
+ return 0;
+}
+
+static int audio_get_avsync_data(struct audio *audio,
+ struct msm_audio_stats *stats)
+{
+ int rc = -EINVAL;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) {
+ /* av_sync sample count */
+ stats->sample_count = (audio->avsync[2] << 16) |
+ (audio->avsync[3]);
+
+ /* av_sync byte_count */
+ stats->byte_count = (audio->avsync[5] << 16) |
+ (audio->avsync[6]);
+
+ audio->avsync_flag = 0;
+ rc = 0;
+ }
+ local_irq_restore(flags);
+ return rc;
+
+}
+
+static long audevrc_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct audio *audio = file->private_data;
+ int rc = -EINVAL;
+ unsigned long flags = 0;
+ uint16_t enable_mask;
+ int enable;
+ int prev_state;
+
+ MM_DBG("cmd = %d\n", cmd);
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+
+ audio->avsync_flag = 0;
+ memset(&stats, 0, sizeof(stats));
+ if (audpp_query_avsync(audio->dec_id) < 0)
+ return rc;
+
+ rc = wait_event_interruptible_timeout(audio->avsync_wait,
+ (audio->avsync_flag == 1),
+ msecs_to_jiffies(AUDPP_AVSYNC_EVENT_TIMEOUT));
+
+ if (rc < 0)
+ return rc;
+ else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) {
+ if (audio_get_avsync_data(audio, &stats) < 0)
+ return rc;
+
+ if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ } else
+ return -EAGAIN;
+ }
+
+ switch (cmd) {
+ case AUDIO_ENABLE_AUDPP:
+ if (copy_from_user(&enable_mask, (void *) arg,
+ sizeof(enable_mask))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ enable = (enable_mask & EQ_ENABLE) ? 1 : 0;
+ audio_enable_eq(audio, enable);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ rc = 0;
+ break;
+ case AUDIO_SET_VOLUME:
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->vol_pan.volume = arg;
+ if (audio->running)
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ rc = 0;
+ break;
+
+ case AUDIO_SET_PAN:
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->vol_pan.pan = arg;
+ if (audio->running)
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ rc = 0;
+ break;
+
+ case AUDIO_SET_EQ:
+ prev_state = audio->eq_enable;
+ audio->eq_enable = 0;
+ if (copy_from_user(&audio->eq.num_bands, (void *) arg,
+ sizeof(audio->eq) -
+ (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) {
+ rc = -EFAULT;
+ break;
+ }
+ audio->eq_enable = prev_state;
+ audio->eq_needs_commit = 1;
+ rc = 0;
+ break;
+ }
+
+ if (-EINVAL != rc)
+ return rc;
+
+ if (cmd == AUDIO_GET_EVENT) {
+ MM_DBG("AUDIO_GET_EVENT\n");
+ if (mutex_trylock(&audio->get_event_lock)) {
+ rc = audevrc_process_event_req(audio,
+ (void __user *) arg);
+ mutex_unlock(&audio->get_event_lock);
+ } else
+ rc = -EBUSY;
+ return rc;
+ }
+
+ if (cmd == AUDIO_ABORT_GET_EVENT) {
+ audio->event_abort = 1;
+ wake_up(&audio->event_wait);
+ return 0;
+ }
+
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START:
+ MM_DBG("AUDIO_START\n");
+ rc = audevrc_enable(audio);
+ if (!rc) {
+ rc = wait_event_interruptible_timeout(audio->wait,
+ audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+ msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+ MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc);
+
+ if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS)
+ rc = -ENODEV;
+ else
+ rc = 0;
+ }
+ break;
+ case AUDIO_STOP:
+ MM_DBG("AUDIO_STOP\n");
+ rc = audevrc_disable(audio);
+ audio->stopped = 1;
+ audevrc_ioport_reset(audio);
+ audio->stopped = 0;
+ break;
+ case AUDIO_FLUSH:
+ MM_DBG("AUDIO_FLUSH\n");
+ audio->rflush = 1;
+ audio->wflush = 1;
+ audevrc_ioport_reset(audio);
+ if (audio->running) {
+ audpp_flush(audio->dec_id);
+ rc = wait_event_interruptible(audio->write_wait,
+ !audio->wflush);
+ if (rc < 0) {
+ MM_ERR("AUDIO_FLUSH interrupted\n");
+ rc = -EINTR;
+ }
+ } else {
+ audio->rflush = 0;
+ audio->wflush = 0;
+ }
+ break;
+ case AUDIO_SET_CONFIG:{
+ struct msm_audio_config config;
+ if (copy_from_user
+ (&config, (void *)arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ audio->mfield = config.meta_field;
+ rc = 0;
+ MM_DBG("AUDIO_SET_CONFIG applicable only \
+ for meta field configuration\n");
+ break;
+ }
+ case AUDIO_GET_CONFIG:{
+ struct msm_audio_config config;
+ config.buffer_size = BUFSZ;
+ config.buffer_count = 2;
+ config.sample_rate = 8000;
+ config.channel_count = 1;
+ config.meta_field = 0;
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+ config.unused[2] = 0;
+ if (copy_to_user((void *)arg, &config, sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ case AUDIO_GET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+ config.pcm_feedback = audio->pcm_feedback;
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+ config.buffer_size = PCM_BUFSZ_MIN;
+ if (copy_to_user((void *)arg, &config, sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ case AUDIO_SET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+ if (copy_from_user
+ (&config, (void *)arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (config.pcm_feedback != audio->pcm_feedback) {
+ MM_ERR("Not sufficient permission to"
+ "change the playback mode\n");
+ rc = -EACCES;
+ break;
+ }
+ if ((config.buffer_count > PCM_BUF_MAX_COUNT) ||
+ (config.buffer_count == 1))
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+
+ if (config.buffer_size < PCM_BUFSZ_MIN)
+ config.buffer_size = PCM_BUFSZ_MIN;
+
+ /* Check if pcm feedback is required */
+ if ((config.pcm_feedback) && (!audio->read_data)) {
+ MM_DBG("allocate PCM buf %d\n",
+ config.buffer_count *
+ config.buffer_size);
+ audio->read_phys = pmem_kalloc(
+ config.buffer_size *
+ config.buffer_count,
+ PMEM_MEMTYPE_EBI1|
+ PMEM_ALIGNMENT_4K);
+ if (IS_ERR((void *)audio->read_phys)) {
+ rc = -ENOMEM;
+ break;
+ }
+ audio->read_data = ioremap(audio->read_phys,
+ config.buffer_size *
+ config.buffer_count);
+ if (!audio->read_data) {
+ MM_ERR("no mem for read buf\n");
+ rc = -ENOMEM;
+ pmem_kfree(audio->read_phys);
+ } else {
+ uint8_t index;
+ uint32_t offset = 0;
+ audio->buf_refresh = 0;
+ audio->pcm_buf_count =
+ config.buffer_count;
+ audio->read_next = 0;
+ audio->fill_next = 0;
+
+ for (index = 0;
+ index < config.buffer_count;
+ index++) {
+ audio->in[index].data =
+ audio->read_data + offset;
+ audio->in[index].addr =
+ audio->read_phys + offset;
+ audio->in[index].size =
+ config.buffer_size;
+ audio->in[index].used = 0;
+ offset += config.buffer_size;
+ }
+ MM_DBG("read buf: phy addr \
+ 0x%08x kernel addr 0x%08x\n",
+ audio->read_phys,
+ (int)audio->read_data);
+ rc = 0;
+ }
+ } else {
+ rc = 0;
+ }
+ break;
+ }
+ case AUDIO_PAUSE:
+ MM_DBG("AUDIO_PAUSE %ld\n", arg);
+ rc = audpp_pause(audio->dec_id, (int) arg);
+ break;
+ case AUDIO_GET_SESSION_ID:
+ if (copy_to_user((void *) arg, &audio->dec_id,
+ sizeof(unsigned short)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+/* Only useful in tunnel-mode */
+static int audevrc_fsync(struct file *file, int datasync)
+{
+ struct audio *audio = file->private_data;
+ int rc = 0;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ if (!audio->running || audio->pcm_feedback) {
+ rc = -EINVAL;
+ goto done_nolock;
+ }
+
+ mutex_lock(&audio->write_lock);
+
+ rc = wait_event_interruptible(audio->write_wait,
+ (!audio->out[0].used &&
+ !audio->out[1].used &&
+ audio->out_needed) || audio->wflush);
+
+ if (rc < 0)
+ goto done;
+ else if (audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+
+ /* pcm dmamiss message is sent continously
+ * when decoder is starved so no race
+ * condition concern
+ */
+ audio->teos = 0;
+
+ rc = wait_event_interruptible(audio->write_wait,
+ audio->teos || audio->wflush);
+
+ if (audio->wflush)
+ rc = -EBUSY;
+
+done:
+ mutex_unlock(&audio->write_lock);
+done_nolock:
+ return rc;
+}
+
+static ssize_t audevrc_read(struct file *file, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ int rc = 0;
+ if (!audio->pcm_feedback) {
+ return 0;
+ /* PCM feedback is not enabled. Nothing to read */
+ }
+ mutex_lock(&audio->read_lock);
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ while (count > 0) {
+ rc = wait_event_interruptible(audio->read_wait,
+ (audio->in[audio->read_next].used > 0) ||
+ (audio->stopped) || (audio->rflush));
+
+ MM_DBG("wait terminated \n");
+ if (rc < 0)
+ break;
+ if (audio->stopped || audio->rflush) {
+ rc = -EBUSY;
+ break;
+ }
+ if (count < audio->in[audio->read_next].used) {
+ /* Read must happen in frame boundary. Since driver does
+ * not know frame size, read count must be greater or
+ * equal to size of PCM samples
+ */
+ MM_DBG("read stop - partial frame\n");
+ break;
+ } else {
+ MM_DBG("read from in[%d]\n", audio->read_next);
+ if (copy_to_user
+ (buf, audio->in[audio->read_next].data,
+ audio->in[audio->read_next].used)) {
+ MM_ERR("invalid addr %x \n",
+ (unsigned int)buf);
+ rc = -EFAULT;
+ break;
+ }
+ count -= audio->in[audio->read_next].used;
+ buf += audio->in[audio->read_next].used;
+ audio->in[audio->read_next].used = 0;
+ if ((++audio->read_next) == audio->pcm_buf_count)
+ audio->read_next = 0;
+ break;
+ /* Force to exit while loop
+ * to prevent output thread
+ * sleep too long if data is
+ * not ready at this moment
+ */
+
+ }
+ }
+ /* don't feed output buffer to HW decoder during flushing
+ * buffer refresh command will be sent once flush completes
+ * send buf refresh command here can confuse HW decoder
+ */
+ if (audio->buf_refresh && !audio->rflush) {
+ audio->buf_refresh = 0;
+ MM_DBG("kick start pcm feedback again\n");
+ audevrc_buffer_refresh(audio);
+ }
+ mutex_unlock(&audio->read_lock);
+ if (buf > start)
+ rc = buf - start;
+ MM_DBG("read %d bytes\n", rc);
+ return rc;
+}
+
+static int audevrc_process_eos(struct audio *audio,
+ const char __user *buf_start, unsigned short mfield_size)
+{
+ int rc = 0;
+ struct buffer *frame;
+
+ frame = audio->out + audio->out_head;
+
+ rc = wait_event_interruptible(audio->write_wait,
+ (audio->out_needed &&
+ audio->out[0].used == 0 &&
+ audio->out[1].used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+
+ if (rc < 0)
+ goto done;
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+
+ if (copy_from_user(frame->data, buf_start, mfield_size)) {
+ rc = -EFAULT;
+ goto done;
+ }
+
+ frame->mfield_sz = mfield_size;
+ audio->out_head ^= 1;
+ frame->used = mfield_size;
+ audevrc_send_data(audio, 0);
+
+done:
+ return rc;
+}
+
+static ssize_t audevrc_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ struct buffer *frame;
+ size_t xfer;
+ char *cpy_ptr;
+ unsigned short mfield_size = 0;
+ int rc = 0, eos_condition = AUDEVRC_EOS_NONE;
+
+ MM_DBG("cnt=%d\n", count);
+
+ if (count & 1)
+ return -EINVAL;
+
+ mutex_lock(&audio->write_lock);
+ while (count > 0) {
+ frame = audio->out + audio->out_head;
+ cpy_ptr = frame->data;
+ rc = wait_event_interruptible(audio->write_wait,
+ (frame->used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+ if (rc < 0)
+ break;
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ break;
+ }
+
+ if (audio->mfield) {
+ if (buf == start) {
+ /* Processing beginning of user buffer */
+ if (__get_user(mfield_size,
+ (unsigned short __user *) buf)) {
+ rc = -EFAULT;
+ break;
+ } else if (mfield_size > count) {
+ rc = -EINVAL;
+ break;
+ }
+ MM_DBG("mf offset_val %x\n", mfield_size);
+ if (copy_from_user(cpy_ptr, buf,
+ mfield_size)) {
+ rc = -EFAULT;
+ break;
+ }
+ /* Check if EOS flag is set and buffer has
+ * contains just meta field
+ */
+ if (cpy_ptr[AUDEVRC_EOS_FLG_OFFSET] &
+ AUDEVRC_EOS_FLG_MASK) {
+ MM_DBG("eos set\n");
+ eos_condition = AUDEVRC_EOS_SET;
+ if (mfield_size == count) {
+ buf += mfield_size;
+ break;
+ } else
+ cpy_ptr[AUDEVRC_EOS_FLG_OFFSET] &=
+ ~AUDEVRC_EOS_FLG_MASK;
+ }
+ /* Check EOS to see if */
+ cpy_ptr += mfield_size;
+ count -= mfield_size;
+ buf += mfield_size;
+ } else {
+ mfield_size = 0;
+ MM_DBG("continuous buffer\n");
+ }
+ frame->mfield_sz = mfield_size;
+ }
+
+ xfer = (count > (frame->size - mfield_size)) ?
+ (frame->size - mfield_size) : count;
+ if (copy_from_user(cpy_ptr, buf, xfer)) {
+ rc = -EFAULT;
+ break;
+ }
+
+ frame->used = xfer + mfield_size;
+ audio->out_head ^= 1;
+ count -= xfer;
+ buf += xfer;
+ audevrc_send_data(audio, 0);
+ }
+ if (eos_condition == AUDEVRC_EOS_SET)
+ rc = audevrc_process_eos(audio, start, mfield_size);
+ mutex_unlock(&audio->write_lock);
+ if (!rc) {
+ if (buf > start)
+ return buf - start;
+ }
+ return rc;
+}
+
+static int audevrc_release(struct inode *inode, struct file *file)
+{
+ struct audio *audio = file->private_data;
+
+ MM_INFO("audio instance 0x%08x freeing\n", (int)audio);
+ mutex_lock(&audio->lock);
+ auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id);
+ audevrc_disable(audio);
+ audevrc_flush(audio);
+ audevrc_flush_pcm_buf(audio);
+ msm_adsp_put(audio->audplay);
+ audpp_adec_free(audio->dec_id);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&audio->suspend_ctl.node);
+#endif
+ audio->event_abort = 1;
+ wake_up(&audio->event_wait);
+ audevrc_reset_event_queue(audio);
+ iounmap(audio->data);
+ pmem_kfree(audio->phys);
+ if (audio->read_data) {
+ iounmap(audio->read_data);
+ pmem_kfree(audio->read_phys);
+ }
+ mutex_unlock(&audio->lock);
+#ifdef CONFIG_DEBUG_FS
+ if (audio->dentry)
+ debugfs_remove(audio->dentry);
+#endif
+ kfree(audio);
+ return 0;
+}
+
+static void audevrc_post_event(struct audio *audio, int type,
+ union msm_audio_event_payload payload)
+{
+ struct audevrc_event *e_node = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+
+ if (!list_empty(&audio->free_event_queue)) {
+ e_node = list_first_entry(&audio->free_event_queue,
+ struct audevrc_event, list);
+ list_del(&e_node->list);
+ } else {
+ e_node = kmalloc(sizeof(struct audevrc_event), GFP_ATOMIC);
+ if (!e_node) {
+ MM_ERR("No mem to post event %d\n", type);
+ return;
+ }
+ }
+
+ e_node->event_type = type;
+ e_node->payload = payload;
+
+ list_add_tail(&e_node->list, &audio->event_queue);
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+ wake_up(&audio->event_wait);
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void audevrc_suspend(struct early_suspend *h)
+{
+ struct audevrc_suspend_ctl *ctl =
+ container_of(h, struct audevrc_suspend_ctl, node);
+ union msm_audio_event_payload payload;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ audevrc_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload);
+}
+
+static void audevrc_resume(struct early_suspend *h)
+{
+ struct audevrc_suspend_ctl *ctl =
+ container_of(h, struct audevrc_suspend_ctl, node);
+ union msm_audio_event_payload payload;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ audevrc_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload);
+}
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t audevrc_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t audevrc_debug_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ const int debug_bufmax = 1024;
+ static char buffer[1024];
+ int n = 0, i;
+ struct audio *audio = file->private_data;
+
+ mutex_lock(&audio->lock);
+ n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "enabled %d\n", audio->enabled);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "stopped %d\n", audio->stopped);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "pcm_feedback %d\n", audio->pcm_feedback);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_buf_sz %d\n", audio->out[0].size);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "pcm_buf_count %d \n", audio->pcm_buf_count);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "pcm_buf_sz %d \n", audio->in[0].size);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "volume %x \n", audio->vol_pan.volume);
+ mutex_unlock(&audio->lock);
+ /* Following variables are only useful for debugging when
+ * when playback halts unexpectedly. Thus, no mutual exclusion
+ * enforced
+ */
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "wflush %d\n", audio->wflush);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "rflush %d\n", audio->rflush);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "running %d \n", audio->running);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "dec state %d \n", audio->dec_state);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_needed %d \n", audio->out_needed);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_head %d \n", audio->out_head);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_tail %d \n", audio->out_tail);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out[0].used %d \n", audio->out[0].used);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out[1].used %d \n", audio->out[1].used);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "buffer_refresh %d \n", audio->buf_refresh);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "read_next %d \n", audio->read_next);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "fill_next %d \n", audio->fill_next);
+ for (i = 0; i < audio->pcm_buf_count; i++)
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "in[%d].size %d \n", i, audio->in[i].used);
+ buffer[n] = 0;
+ return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static const struct file_operations audevrc_debug_fops = {
+ .read = audevrc_debug_read,
+ .open = audevrc_debug_open,
+};
+#endif
+
+static int audevrc_open(struct inode *inode, struct file *file)
+{
+ struct audio *audio = NULL;
+ int rc, dec_attrb, decid, i;
+ struct audevrc_event *e_node = NULL;
+#ifdef CONFIG_DEBUG_FS
+ /* 4 bytes represents decoder number, 1 byte for terminate string */
+ char name[sizeof "msm_evrc_" + 5];
+#endif
+
+ /* Allocate audio instance, set to zero */
+ audio = kzalloc(sizeof(struct audio), GFP_KERNEL);
+ if (!audio) {
+ MM_ERR("no memory to allocate audio instance\n");
+ rc = -ENOMEM;
+ goto done;
+ }
+ MM_INFO("audio instance 0x%08x created\n", (int)audio);
+
+ /* Allocate the decoder */
+ dec_attrb = AUDDEC_DEC_EVRC;
+ if ((file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ dec_attrb |= MSM_AUD_MODE_NONTUNNEL;
+ audio->pcm_feedback = NON_TUNNEL_MODE_PLAYBACK;
+ } else if ((file->f_mode & FMODE_WRITE) &&
+ !(file->f_mode & FMODE_READ)) {
+ dec_attrb |= MSM_AUD_MODE_TUNNEL;
+ audio->pcm_feedback = TUNNEL_MODE_PLAYBACK;
+ } else {
+ kfree(audio);
+ rc = -EACCES;
+ goto done;
+ }
+ decid = audpp_adec_alloc(dec_attrb, &audio->module_name,
+ &audio->queue_id);
+
+ if (decid < 0) {
+ MM_ERR("No free decoder available, freeing instance 0x%08x\n",
+ (int)audio);
+ rc = -ENODEV;
+ kfree(audio);
+ goto done;
+ }
+
+ audio->dec_id = decid & MSM_AUD_DECODER_MASK;
+
+ audio->phys = pmem_kalloc(DMASZ, PMEM_MEMTYPE_EBI1|PMEM_ALIGNMENT_4K);
+ if (IS_ERR((void *)audio->phys)) {
+ MM_ERR("could not allocate write buffers, freeing instance \
+ 0x%08x\n", (int)audio);
+ rc = -ENOMEM;
+ audpp_adec_free(audio->dec_id);
+ kfree(audio);
+ goto done;
+ } else {
+ audio->data = ioremap(audio->phys, DMASZ);
+ if (!audio->data) {
+ MM_ERR("could not allocate write buffers, freeing \
+ instance 0x%08x\n", (int)audio);
+ rc = -ENOMEM;
+ pmem_kfree(audio->phys);
+ audpp_adec_free(audio->dec_id);
+ kfree(audio);
+ goto done;
+ }
+ MM_DBG("write buf: phy addr 0x%08x kernel addr 0x%08x\n",
+ audio->phys, (int)audio->data);
+ }
+
+ rc = msm_adsp_get(audio->module_name, &audio->audplay,
+ &audplay_adsp_ops_evrc, audio);
+
+ if (rc) {
+ MM_ERR("failed to get %s module, freeing instance 0x%08x\n",
+ audio->module_name, (int)audio);
+ goto err;
+ }
+
+ /* Initialize all locks of audio instance */
+ mutex_init(&audio->lock);
+ mutex_init(&audio->write_lock);
+ mutex_init(&audio->read_lock);
+ mutex_init(&audio->get_event_lock);
+ spin_lock_init(&audio->dsp_lock);
+ init_waitqueue_head(&audio->write_wait);
+ init_waitqueue_head(&audio->read_wait);
+ INIT_LIST_HEAD(&audio->free_event_queue);
+ INIT_LIST_HEAD(&audio->event_queue);
+ init_waitqueue_head(&audio->wait);
+ init_waitqueue_head(&audio->event_wait);
+ spin_lock_init(&audio->event_queue_lock);
+ init_waitqueue_head(&audio->avsync_wait);
+
+ audio->out[0].data = audio->data + 0;
+ audio->out[0].addr = audio->phys + 0;
+ audio->out[0].size = BUFSZ;
+
+ audio->out[1].data = audio->data + BUFSZ;
+ audio->out[1].addr = audio->phys + BUFSZ;
+ audio->out[1].size = BUFSZ;
+
+ audio->vol_pan.volume = 0x3FFF;
+
+ audevrc_flush(audio);
+
+ file->private_data = audio;
+ audio->opened = 1;
+
+ audio->device_events = AUDDEV_EVT_DEV_RDY
+ |AUDDEV_EVT_DEV_RLS|
+ AUDDEV_EVT_STREAM_VOL_CHG;
+
+ rc = auddev_register_evt_listner(audio->device_events,
+ AUDDEV_CLNT_DEC,
+ audio->dec_id,
+ evrc_listner,
+ (void *)audio);
+ if (rc) {
+ MM_ERR("%s: failed to register listner\n", __func__);
+ goto event_err;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ snprintf(name, sizeof name, "msm_evrc_%04x", audio->dec_id);
+ audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *) audio, &audevrc_debug_fops);
+
+ if (IS_ERR(audio->dentry))
+ MM_DBG("debugfs_create_file failed\n");
+#endif
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+ audio->suspend_ctl.node.resume = audevrc_resume;
+ audio->suspend_ctl.node.suspend = audevrc_suspend;
+ audio->suspend_ctl.audio = audio;
+ register_early_suspend(&audio->suspend_ctl.node);
+#endif
+ for (i = 0; i < AUDEVRC_EVENT_NUM; i++) {
+ e_node = kmalloc(sizeof(struct audevrc_event), GFP_KERNEL);
+ if (e_node)
+ list_add_tail(&e_node->list, &audio->free_event_queue);
+ else {
+ MM_ERR("event pkt alloc failed\n");
+ break;
+ }
+ }
+done:
+ return rc;
+event_err:
+ msm_adsp_put(audio->audplay);
+err:
+ iounmap(audio->data);
+ pmem_kfree(audio->phys);
+ audpp_adec_free(audio->dec_id);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_evrc_fops = {
+ .owner = THIS_MODULE,
+ .open = audevrc_open,
+ .release = audevrc_release,
+ .read = audevrc_read,
+ .write = audevrc_write,
+ .unlocked_ioctl = audevrc_ioctl,
+ .fsync = audevrc_fsync,
+};
+
+struct miscdevice audio_evrc_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_evrc",
+ .fops = &audio_evrc_fops,
+};
+
+static int __init audevrc_init(void)
+{
+ return misc_register(&audio_evrc_misc);
+
+}
+
+static void __exit audevrc_exit(void)
+{
+ misc_deregister(&audio_evrc_misc);
+}
+
+module_init(audevrc_init);
+module_exit(audevrc_exit);
+
+MODULE_DESCRIPTION("MSM EVRC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_evrc_in.c b/arch/arm/mach-msm/qdsp5v2/audio_evrc_in.c
new file mode 100644
index 0000000..763856e
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_evrc_in.c
@@ -0,0 +1,1490 @@
+/*
+ * evrc audio input device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/msm_audio_qcp.h>
+#include <linux/android_pmem.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+
+#include <mach/msm_adsp.h>
+#include <mach/qdsp5v2/qdsp5audreccmdi.h>
+#include <mach/qdsp5v2/qdsp5audrecmsg.h>
+#include <mach/qdsp5v2/audpreproc.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/debug_mm.h>
+
+#define META_OUT_SIZE 24
+/* FRAME_NUM must be a power of two */
+#define FRAME_NUM 8
+#define EVRC_FRAME_SIZE 36 /* 36 bytes data */
+#define FRAME_SIZE (22 * 2) /* 36 bytes data */
+ /* 36 bytes data + 24 meta field*/
+#define NT_FRAME_SIZE (EVRC_FRAME_SIZE + META_OUT_SIZE)
+#define DMASZ (NT_FRAME_SIZE * FRAME_NUM)
+#define OUT_FRAME_NUM 2
+#define OUT_BUFFER_SIZE (4 * 1024 + META_OUT_SIZE)
+#define BUFFER_SIZE (OUT_BUFFER_SIZE * OUT_FRAME_NUM)
+
+#define AUDPREPROC_EVRC_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer*/
+#define AUDPREPROC_EVRC_EOS_FLG_MASK 0x01
+#define AUDPREPROC_EVRC_EOS_NONE 0x0 /* No EOS detected */
+#define AUDPREPROC_EVRC_EOS_SET 0x1 /* EOS set in meta field */
+
+struct buffer {
+ void *data;
+ uint32_t size;
+ uint32_t read;
+ uint32_t addr;
+ uint32_t used;
+ uint32_t mfield_sz;
+};
+
+struct audio_in {
+ struct buffer in[FRAME_NUM];
+
+ spinlock_t dsp_lock;
+
+ atomic_t in_bytes;
+ atomic_t in_samples;
+
+ struct mutex lock;
+ struct mutex read_lock;
+ wait_queue_head_t wait;
+ wait_queue_head_t wait_enable;
+ /*write section*/
+ struct buffer out[OUT_FRAME_NUM];
+
+ uint8_t out_head;
+ uint8_t out_tail;
+ uint8_t out_needed; /* number of buffers the dsp is waiting for */
+ uint32_t out_count;
+
+ struct mutex write_lock;
+ wait_queue_head_t write_wait;
+ int32_t out_phys; /* physical address of write buffer */
+ char *out_data;
+ int mfield; /* meta field embedded in data */
+ int wflush; /*write flush */
+ int rflush; /*read flush*/
+ int out_frame_cnt;
+
+ struct msm_adsp_module *audrec;
+
+ struct audrec_session_info session_info; /*audrec session info*/
+
+ /* configuration to use on next enable */
+ uint32_t buffer_size; /* Frame size (36 bytes) */
+ uint32_t samp_rate;
+ uint32_t channel_mode;
+ uint32_t enc_type;
+
+ struct msm_audio_evrc_enc_config cfg;
+
+ uint32_t dsp_cnt;
+ uint32_t in_head; /* next buffer dsp will write */
+ uint32_t in_tail; /* next buffer read() will read */
+ uint32_t in_count; /* number of buffers available to read() */
+ uint32_t mode;
+ uint32_t eos_ack;
+ uint32_t flush_ack;
+
+ const char *module_name;
+ unsigned queue_ids;
+ uint16_t enc_id;
+
+ uint16_t source; /* Encoding source bit mask */
+ uint32_t device_events;
+ uint32_t in_call;
+ uint32_t dev_cnt;
+ int voice_state;
+ spinlock_t dev_lock;
+
+ /* data allocated for various buffers */
+ char *data;
+ dma_addr_t phys;
+
+ int opened;
+ int enabled;
+ int running;
+ int stopped; /* set when stopped, cleared on flush */
+};
+
+struct audio_frame {
+ uint16_t frame_count_lsw;
+ uint16_t frame_count_msw;
+ uint16_t frame_length;
+ uint16_t erased_pcm;
+ unsigned char raw_bitstream[]; /* samples */
+} __attribute__((packed));
+
+struct audio_frame_nt {
+ uint16_t metadata_len;
+ uint16_t frame_count_lsw;
+ uint16_t frame_count_msw;
+ uint16_t frame_length;
+ uint16_t erased_pcm;
+ uint16_t reserved;
+ uint16_t time_stamp_dword_lsw;
+ uint16_t time_stamp_dword_msw;
+ uint16_t time_stamp_lsw;
+ uint16_t time_stamp_msw;
+ uint16_t nflag_lsw;
+ uint16_t nflag_msw;
+ unsigned char raw_bitstream[]; /* samples */
+} __attribute__((packed));
+
+struct evrc_encoded_meta_out {
+ uint16_t metadata_len;
+ uint16_t time_stamp_dword_lsw;
+ uint16_t time_stamp_dword_msw;
+ uint16_t time_stamp_lsw;
+ uint16_t time_stamp_msw;
+ uint16_t nflag_lsw;
+ uint16_t nflag_msw;
+};
+
+/* Audrec Queue command sent macro's */
+#define audrec_send_bitstreamqueue(audio, cmd, len) \
+ msm_adsp_write(audio->audrec, ((audio->queue_ids & 0xFFFF0000) >> 16),\
+ cmd, len)
+
+#define audrec_send_audrecqueue(audio, cmd, len) \
+ msm_adsp_write(audio->audrec, (audio->queue_ids & 0x0000FFFF),\
+ cmd, len)
+
+/* DSP command send functions */
+static int audevrc_in_enc_config(struct audio_in *audio, int enable);
+static int audevrc_in_param_config(struct audio_in *audio);
+static int audevrc_in_mem_config(struct audio_in *audio);
+static int audevrc_in_record_config(struct audio_in *audio, int enable);
+static int audevrc_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt);
+
+static void audevrc_in_get_dsp_frames(struct audio_in *audio);
+static int audpcm_config(struct audio_in *audio);
+static void audevrc_out_flush(struct audio_in *audio);
+static int audpreproc_cmd_cfg_routing_mode(struct audio_in *audio);
+static void audpreproc_pcm_send_data(struct audio_in *audio, unsigned needed);
+static void audevrc_nt_in_get_dsp_frames(struct audio_in *audio);
+
+static void audevrc_in_flush(struct audio_in *audio);
+
+static void evrc_in_listener(u32 evt_id, union auddev_evt_data *evt_payload,
+ void *private_data)
+{
+ struct audio_in *audio = (struct audio_in *) private_data;
+ unsigned long flags;
+
+ MM_DBG("evt_id = 0x%8x\n", evt_id);
+ switch (evt_id) {
+ case AUDDEV_EVT_DEV_RDY: {
+ MM_DBG("AUDDEV_EVT_DEV_RDY\n");
+ spin_lock_irqsave(&audio->dev_lock, flags);
+ audio->dev_cnt++;
+ if (!audio->in_call)
+ audio->source |= (0x1 << evt_payload->routing_id);
+ spin_unlock_irqrestore(&audio->dev_lock, flags);
+
+ if ((audio->running == 1) && (audio->enabled == 1) &&
+ (audio->mode == MSM_AUD_ENC_MODE_TUNNEL))
+ audevrc_in_record_config(audio, 1);
+ }
+ break;
+ case AUDDEV_EVT_DEV_RLS: {
+ MM_DBG("AUDDEV_EVT_DEV_RLS\n");
+ spin_lock_irqsave(&audio->dev_lock, flags);
+ audio->dev_cnt--;
+ if (!audio->in_call)
+ audio->source &= ~(0x1 << evt_payload->routing_id);
+ spin_unlock_irqrestore(&audio->dev_lock, flags);
+
+ if ((!audio->running) || (!audio->enabled))
+ break;
+
+ if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+ /* Turn of as per source */
+ if (audio->source)
+ audevrc_in_record_config(audio, 1);
+ else
+ /* Turn off all */
+ audevrc_in_record_config(audio, 0);
+ }
+ }
+ break;
+ case AUDDEV_EVT_VOICE_STATE_CHG: {
+ MM_DBG("AUDDEV_EVT_VOICE_STATE_CHG, state = %d\n",
+ evt_payload->voice_state);
+ audio->voice_state = evt_payload->voice_state;
+ if (audio->in_call && audio->running &&
+ (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)) {
+ if (audio->voice_state == VOICE_STATE_INCALL)
+ audevrc_in_record_config(audio, 1);
+ else if (audio->voice_state == VOICE_STATE_OFFCALL) {
+ audevrc_in_record_config(audio, 0);
+ wake_up(&audio->wait);
+ }
+ }
+
+ break;
+ }
+ default:
+ MM_ERR("wrong event %d\n", evt_id);
+ break;
+ }
+}
+
+/* ------------------- dsp preproc event handler--------------------- */
+static void audpreproc_dsp_event(void *data, unsigned id, void *msg)
+{
+ struct audio_in *audio = data;
+
+ switch (id) {
+ case AUDPREPROC_ERROR_MSG: {
+ struct audpreproc_err_msg *err_msg = msg;
+
+ MM_ERR("ERROR_MSG: stream id %d err idx %d\n",
+ err_msg->stream_id, err_msg->aud_preproc_err_idx);
+ /* Error case */
+ wake_up(&audio->wait_enable);
+ break;
+ }
+ case AUDPREPROC_CMD_CFG_DONE_MSG: {
+ MM_DBG("CMD_CFG_DONE_MSG \n");
+ break;
+ }
+ case AUDPREPROC_CMD_ENC_CFG_DONE_MSG: {
+ struct audpreproc_cmd_enc_cfg_done_msg *enc_cfg_msg = msg;
+
+ MM_DBG("CMD_ENC_CFG_DONE_MSG: stream id %d enc type \
+ 0x%8x\n", enc_cfg_msg->stream_id,
+ enc_cfg_msg->rec_enc_type);
+ /* Encoder enable success */
+ if (enc_cfg_msg->rec_enc_type & ENCODE_ENABLE) {
+ if(audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) {
+ MM_DBG("routing command\n");
+ audpreproc_cmd_cfg_routing_mode(audio);
+ } else {
+ audevrc_in_param_config(audio);
+ }
+ } else { /* Encoder disable success */
+ audio->running = 0;
+ if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)
+ audevrc_in_record_config(audio, 0);
+ else
+ wake_up(&audio->wait_enable);
+ }
+ break;
+ }
+ case AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG: {
+ MM_DBG("CMD_ENC_PARAM_CFG_DONE_MSG\n");
+ if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)
+ audevrc_in_mem_config(audio);
+ else
+ audpcm_config(audio);
+ break;
+ }
+ case AUDPREPROC_CMD_ROUTING_MODE_DONE_MSG: {
+ struct audpreproc_cmd_routing_mode_done\
+ *routing_cfg_done_msg = msg;
+ if (routing_cfg_done_msg->configuration == 0) {
+ MM_INFO("routing configuration failed\n");
+ audio->running = 0;
+ } else
+ audevrc_in_param_config(audio);
+ break;
+ }
+ case AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG: {
+ MM_DBG("AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG \n");
+ wake_up(&audio->wait_enable);
+ break;
+ }
+ default:
+ MM_ERR("Unknown Event id %d\n", id);
+ }
+}
+
+/* ------------------- dsp audrec event handler--------------------- */
+static void audrec_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent)(void *ptr, size_t len))
+{
+ struct audio_in *audio = data;
+
+ switch (id) {
+ case AUDREC_CMD_MEM_CFG_DONE_MSG: {
+ MM_DBG("CMD_MEM_CFG_DONE MSG DONE\n");
+ audio->running = 1;
+ if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+ if ((!audio->in_call && (audio->dev_cnt > 0)) ||
+ (audio->in_call &&
+ (audio->voice_state \
+ == VOICE_STATE_INCALL)))
+ audevrc_in_record_config(audio, 1);
+ } else {
+ audpreproc_pcm_send_data(audio, 1);
+ wake_up(&audio->wait_enable);
+ }
+ break;
+ }
+ case AUDREC_FATAL_ERR_MSG: {
+ struct audrec_fatal_err_msg fatal_err_msg;
+
+ getevent(&fatal_err_msg, AUDREC_FATAL_ERR_MSG_LEN);
+ MM_ERR("FATAL_ERR_MSG: err id %d\n",
+ fatal_err_msg.audrec_err_id);
+ /* Error stop the encoder */
+ audio->stopped = 1;
+ wake_up(&audio->wait);
+ if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL)
+ wake_up(&audio->write_wait);
+ break;
+ }
+ case AUDREC_UP_PACKET_READY_MSG: {
+ struct audrec_up_pkt_ready_msg pkt_ready_msg;
+
+ getevent(&pkt_ready_msg, AUDREC_UP_PACKET_READY_MSG_LEN);
+ MM_DBG("UP_PACKET_READY_MSG: write cnt lsw %d \
+ write cnt msw %d read cnt lsw %d read cnt msw %d \n",\
+ pkt_ready_msg.audrec_packet_write_cnt_lsw, \
+ pkt_ready_msg.audrec_packet_write_cnt_msw, \
+ pkt_ready_msg.audrec_up_prev_read_cnt_lsw, \
+ pkt_ready_msg.audrec_up_prev_read_cnt_msw);
+
+ audevrc_in_get_dsp_frames(audio);
+ break;
+ }
+ case AUDREC_CMD_PCM_BUFFER_PTR_UPDATE_ARM_TO_ENC_MSG: {
+ MM_DBG("ptr_update recieved from DSP\n");
+ audpreproc_pcm_send_data(audio, 1);
+ break;
+ }
+ case AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG: {
+ MM_ERR("AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG");
+ audevrc_in_mem_config(audio);
+ break;
+ }
+ case AUDREC_UP_NT_PACKET_READY_MSG: {
+ struct audrec_up_nt_packet_ready_msg pkt_ready_msg;
+
+ getevent(&pkt_ready_msg, AUDREC_UP_NT_PACKET_READY_MSG_LEN);
+ MM_DBG("UP_NT_PACKET_READY_MSG: write cnt lsw %d \
+ write cnt msw %d read cnt lsw %d read cnt msw %d \n",\
+ pkt_ready_msg.audrec_packetwrite_cnt_lsw, \
+ pkt_ready_msg.audrec_packetwrite_cnt_msw, \
+ pkt_ready_msg.audrec_upprev_readcount_lsw, \
+ pkt_ready_msg.audrec_upprev_readcount_msw);
+
+ audevrc_nt_in_get_dsp_frames(audio);
+ break;
+ }
+ case AUDREC_CMD_EOS_ACK_MSG: {
+ MM_DBG("eos ack recieved\n");
+ break;
+ }
+ case AUDREC_CMD_FLUSH_DONE_MSG: {
+ audio->wflush = 0;
+ audio->rflush = 0;
+ audio->flush_ack = 1;
+ wake_up(&audio->write_wait);
+ MM_DBG("flush ack recieved\n");
+ break;
+ }
+ case ADSP_MESSAGE_ID: {
+ MM_DBG("Received ADSP event:module audrectask\n");
+ break;
+ }
+ default:
+ MM_ERR("Unknown Event id %d\n", id);
+ }
+}
+
+static void audevrc_in_get_dsp_frames(struct audio_in *audio)
+{
+ struct audio_frame *frame;
+ uint32_t index;
+ unsigned long flags;
+
+ index = audio->in_head;
+
+ frame = (void *) (((char *)audio->in[index].data) - \
+ sizeof(*frame));
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->in[index].size = frame->frame_length;
+
+ /* statistics of read */
+ atomic_add(audio->in[index].size, &audio->in_bytes);
+ atomic_add(1, &audio->in_samples);
+
+ audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1);
+
+ /* If overflow, move the tail index foward. */
+ if (audio->in_head == audio->in_tail) {
+ MM_ERR("Error! not able to keep up the read\n");
+ audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1);
+ MM_ERR("in_count = %d\n", audio->in_count);
+ } else
+ audio->in_count++;
+
+ audevrc_dsp_read_buffer(audio, audio->dsp_cnt++);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+
+ wake_up(&audio->wait);
+}
+
+static void audevrc_nt_in_get_dsp_frames(struct audio_in *audio)
+{
+ struct audio_frame_nt *nt_frame;
+ uint32_t index;
+ unsigned long flags;
+
+ index = audio->in_head;
+ nt_frame = (void *) (((char *)audio->in[index].data) - \
+ sizeof(struct audio_frame_nt));
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->in[index].size = nt_frame->frame_length;
+ /* statistics of read */
+ atomic_add(audio->in[index].size, &audio->in_bytes);
+ atomic_add(1, &audio->in_samples);
+
+ audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1);
+
+ /* If overflow, move the tail index foward. */
+ if (audio->in_head == audio->in_tail)
+ MM_DBG("Error! not able to keep up the read\n");
+ else
+ audio->in_count++;
+
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ wake_up(&audio->wait);
+}
+
+
+struct msm_adsp_ops audrec_evrc_adsp_ops = {
+ .event = audrec_dsp_event,
+};
+
+static int audpreproc_pcm_buffer_ptr_refresh(struct audio_in *audio,
+ unsigned idx, unsigned len)
+{
+ struct audrec_cmd_pcm_buffer_ptr_refresh_arm_enc cmd;
+
+ if (len == META_OUT_SIZE)
+ len = len / 2;
+ else
+ len = (len + META_OUT_SIZE) / 2;
+ MM_DBG("len = %d\n", len);
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDREC_CMD_PCM_BUFFER_PTR_REFRESH_ARM_TO_ENC;
+ cmd.num_buffers = 1;
+ if (cmd.num_buffers == 1) {
+ cmd.buf_address_length[0] = (audio->out[idx].addr &
+ 0xffff0000) >> 16;
+ cmd.buf_address_length[1] = (audio->out[idx].addr &
+ 0x0000ffff);
+ cmd.buf_address_length[2] = (len & 0xffff0000) >> 16;
+ cmd.buf_address_length[3] = (len & 0x0000ffff);
+ }
+ audio->out_frame_cnt++;
+ return audrec_send_audrecqueue(audio, (void *)&cmd,
+ (unsigned int)sizeof(cmd));
+}
+
+
+static int audpcm_config(struct audio_in *audio)
+{
+ struct audrec_cmd_pcm_cfg_arm_to_enc cmd;
+ MM_DBG("\n");
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDREC_CMD_PCM_CFG_ARM_TO_ENC;
+ cmd.config_update_flag = AUDREC_PCM_CONFIG_UPDATE_FLAG_ENABLE;
+ cmd.enable_flag = AUDREC_ENABLE_FLAG_VALUE;
+ cmd.sampling_freq = audio->samp_rate;
+ if (!audio->channel_mode)
+ cmd.channels = 1;
+ else
+ cmd.channels = 2;
+ cmd.frequency_of_intimation = 1;
+ cmd.max_number_of_buffers = OUT_FRAME_NUM;
+ return audrec_send_audrecqueue(audio, (void *)&cmd,
+ (unsigned int)sizeof(cmd));
+}
+
+
+static int audpreproc_cmd_cfg_routing_mode(struct audio_in *audio)
+{
+ struct audpreproc_audrec_cmd_routing_mode cmd;
+
+ MM_DBG("\n");
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ROUTING_MODE;
+ cmd.stream_id = audio->enc_id;
+ if (audio->mode == MSM_ADSP_ENC_MODE_NON_TUNNEL)
+ cmd.routing_mode = 1;
+ return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+
+
+static int audevrc_in_enc_config(struct audio_in *audio, int enable)
+{
+ struct audpreproc_audrec_cmd_enc_cfg cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ENC_CFG_2;
+ cmd.stream_id = audio->enc_id;
+
+ if (enable)
+ cmd.audrec_enc_type = audio->enc_type | ENCODE_ENABLE;
+ else
+ cmd.audrec_enc_type &= ~(ENCODE_ENABLE);
+
+ return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+static int audevrc_in_param_config(struct audio_in *audio)
+{
+ struct audpreproc_audrec_cmd_parm_cfg_evrc cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.cmd_id = AUDPREPROC_AUDREC_CMD_PARAM_CFG;
+ cmd.common.stream_id = audio->enc_id;
+
+ cmd.enc_min_rate = audio->cfg.min_bit_rate;
+ cmd.enc_max_rate = audio->cfg.max_bit_rate;
+ cmd.rate_modulation_cmd = 0; /* Default set to 0 */
+
+ return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+/* To Do: msm_snddev_route_enc(audio->enc_id); */
+static int audevrc_in_record_config(struct audio_in *audio, int enable)
+{
+ struct audpreproc_afe_cmd_audio_record_cfg cmd;
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG;
+ cmd.stream_id = audio->enc_id;
+ if (enable)
+ cmd.destination_activity = AUDIO_RECORDING_TURN_ON;
+ else
+ cmd.destination_activity = AUDIO_RECORDING_TURN_OFF;
+
+ cmd.source_mix_mask = audio->source;
+ if (audio->enc_id == 2) {
+ if ((cmd.source_mix_mask &
+ INTERNAL_CODEC_TX_SOURCE_MIX_MASK) ||
+ (cmd.source_mix_mask & AUX_CODEC_TX_SOURCE_MIX_MASK) ||
+ (cmd.source_mix_mask & VOICE_UL_SOURCE_MIX_MASK) ||
+ (cmd.source_mix_mask & VOICE_DL_SOURCE_MIX_MASK)) {
+ cmd.pipe_id = SOURCE_PIPE_1;
+ }
+ if (cmd.source_mix_mask &
+ AUDPP_A2DP_PIPE_SOURCE_MIX_MASK)
+ cmd.pipe_id |= SOURCE_PIPE_0;
+ }
+ MM_DBG("stream_id %x destination_activity %x \
+ source_mix_mask %x pipe_id %x",\
+ cmd.stream_id, cmd.destination_activity,
+ cmd.source_mix_mask, cmd.pipe_id);
+ return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+static int audevrc_in_mem_config(struct audio_in *audio)
+{
+ struct audrec_cmd_arecmem_cfg cmd;
+ uint16_t *data = (void *) audio->data;
+ int n;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDREC_CMD_MEM_CFG_CMD;
+ cmd.audrec_up_pkt_intm_count = 1;
+ cmd.audrec_ext_pkt_start_addr_msw = audio->phys >> 16;
+ cmd.audrec_ext_pkt_start_addr_lsw = audio->phys;
+ cmd.audrec_ext_pkt_buf_number = FRAME_NUM;
+ MM_DBG("audio->phys = %x\n", audio->phys);
+ /* prepare buffer pointers:
+ * T:36 bytes evrc packet + 4 halfword header
+ * NT:36 bytes evrc packet + 12 halfword header
+ */
+ for (n = 0; n < FRAME_NUM; n++) {
+ if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+ audio->in[n].data = data + 4;
+ data += (FRAME_SIZE/2);
+ MM_DBG("0x%8x\n", (int)(audio->in[n].data - 8));
+ } else {
+ audio->in[n].data = data + 12;
+ data += ((EVRC_FRAME_SIZE) / 2) + 12;
+ MM_DBG("0x%8x\n", (int)(audio->in[n].data - 24));
+ }
+ }
+ return audrec_send_audrecqueue(audio, &cmd, sizeof(cmd));
+}
+
+static int audevrc_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt)
+{
+ struct up_audrec_packet_ext_ptr cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = UP_AUDREC_PACKET_EXT_PTR;
+ cmd.audrec_up_curr_read_count_msw = read_cnt >> 16;
+ cmd.audrec_up_curr_read_count_lsw = read_cnt;
+
+ return audrec_send_bitstreamqueue(audio, &cmd, sizeof(cmd));
+}
+static int audevrc_flush_command(struct audio_in *audio)
+{
+ struct audrec_cmd_flush cmd;
+ MM_DBG("\n");
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDREC_CMD_FLUSH;
+ return audrec_send_audrecqueue(audio, &cmd, sizeof(cmd));
+}
+
+/* must be called with audio->lock held */
+static int audevrc_in_enable(struct audio_in *audio)
+{
+ if (audio->enabled)
+ return 0;
+
+ if (audpreproc_enable(audio->enc_id, &audpreproc_dsp_event, audio)) {
+ MM_ERR("msm_adsp_enable(audpreproc) failed\n");
+ return -ENODEV;
+ }
+
+ if (msm_adsp_enable(audio->audrec)) {
+ MM_ERR("msm_adsp_enable(audrec) failed\n");
+ audpreproc_disable(audio->enc_id, audio);
+ return -ENODEV;
+ }
+ audio->enabled = 1;
+ audevrc_in_enc_config(audio, 1);
+
+ return 0;
+}
+
+/* must be called with audio->lock held */
+static int audevrc_in_disable(struct audio_in *audio)
+{
+ if (audio->enabled) {
+ audio->enabled = 0;
+ audevrc_in_enc_config(audio, 0);
+ wake_up(&audio->wait);
+ wait_event_interruptible_timeout(audio->wait_enable,
+ audio->running == 0, 1*HZ);
+ msm_adsp_disable(audio->audrec);
+ audpreproc_disable(audio->enc_id, audio);
+ }
+ return 0;
+}
+
+static void audevrc_ioport_reset(struct audio_in *audio)
+{
+ /* Make sure read/write thread are free from
+ * sleep and knowing that system is not able
+ * to process io request at the moment
+ */
+ wake_up(&audio->write_wait);
+ mutex_lock(&audio->write_lock);
+ audevrc_in_flush(audio);
+ mutex_unlock(&audio->write_lock);
+ wake_up(&audio->wait);
+ mutex_lock(&audio->read_lock);
+ audevrc_out_flush(audio);
+ mutex_unlock(&audio->read_lock);
+}
+
+static void audevrc_in_flush(struct audio_in *audio)
+{
+ int i;
+
+ audio->dsp_cnt = 0;
+ audio->in_head = 0;
+ audio->in_tail = 0;
+ audio->in_count = 0;
+ audio->eos_ack = 0;
+ for (i = 0; i < FRAME_NUM; i++) {
+ audio->in[i].size = 0;
+ audio->in[i].read = 0;
+ }
+ MM_DBG("in_bytes %d\n", atomic_read(&audio->in_bytes));
+ MM_DBG("in_samples %d\n", atomic_read(&audio->in_samples));
+ atomic_set(&audio->in_bytes, 0);
+ atomic_set(&audio->in_samples, 0);
+}
+
+static void audevrc_out_flush(struct audio_in *audio)
+{
+ int i;
+
+ audio->out_head = 0;
+ audio->out_tail = 0;
+ audio->out_count = 0;
+ for (i = 0; i < OUT_FRAME_NUM; i++) {
+ audio->out[i].size = 0;
+ audio->out[i].read = 0;
+ audio->out[i].used = 0;
+ }
+}
+
+/* ------------------- device --------------------- */
+static long audevrc_in_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct audio_in *audio = file->private_data;
+ int rc = 0;
+
+ MM_DBG("\n");
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ stats.byte_count = atomic_read(&audio->in_bytes);
+ stats.sample_count = atomic_read(&audio->in_samples);
+ if (copy_to_user((void *) arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return rc;
+ }
+
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START: {
+ uint32_t freq;
+ freq = 48000;
+ MM_DBG("AUDIO_START\n");
+ if (audio->in_call && (audio->voice_state !=
+ VOICE_STATE_INCALL)) {
+ rc = -EPERM;
+ break;
+ }
+ rc = msm_snddev_request_freq(&freq, audio->enc_id,
+ SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+ MM_DBG("sample rate configured %d\n", freq);
+ if (rc < 0) {
+ MM_DBG(" Sample rate can not be set, return code %d\n",
+ rc);
+ msm_snddev_withdraw_freq(audio->enc_id,
+ SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+ MM_DBG("msm_snddev_withdraw_freq\n");
+ break;
+ }
+ /*update aurec session info in audpreproc layer*/
+ audio->session_info.session_id = audio->enc_id;
+ audio->session_info.sampling_freq = audio->samp_rate;
+ audpreproc_update_audrec_info(&audio->session_info);
+ rc = audevrc_in_enable(audio);
+ if (!rc) {
+ rc =
+ wait_event_interruptible_timeout(audio->wait_enable,
+ audio->running != 0, 1*HZ);
+ MM_DBG("state %d rc = %d\n", audio->running, rc);
+
+ if (audio->running == 0)
+ rc = -ENODEV;
+ else
+ rc = 0;
+ }
+ audio->stopped = 0;
+ break;
+ }
+ case AUDIO_STOP: {
+ /*reset the sampling frequency information at audpreproc layer*/
+ audio->session_info.sampling_freq = 0;
+ audpreproc_update_audrec_info(&audio->session_info);
+ rc = audevrc_in_disable(audio);
+ rc = msm_snddev_withdraw_freq(audio->enc_id,
+ SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+ MM_DBG("msm_snddev_withdraw_freq\n");
+ audio->stopped = 1;
+ break;
+ }
+ case AUDIO_FLUSH: {
+ MM_DBG("AUDIO_FLUSH\n");
+ audio->rflush = 1;
+ audio->wflush = 1;
+ audevrc_ioport_reset(audio);
+ if (audio->running) {
+ audevrc_flush_command(audio);
+ rc = wait_event_interruptible(audio->write_wait,
+ !audio->wflush);
+ if (rc < 0) {
+ MM_ERR("AUDIO_FLUSH interrupted\n");
+ rc = -EINTR;
+ }
+ } else {
+ audio->rflush = 0;
+ audio->wflush = 0;
+ }
+ break;
+ }
+ case AUDIO_SET_STREAM_CONFIG: {
+ struct msm_audio_stream_config cfg;
+ if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) {
+ rc = -EFAULT;
+ break;
+ }
+ /* Allow only single frame */
+ if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+ if (cfg.buffer_size != (FRAME_SIZE - 8)) {
+ rc = -EINVAL;
+ break;
+ }
+ } else {
+ if (cfg.buffer_size != (EVRC_FRAME_SIZE + 14)) {
+ rc = -EINVAL;
+ break;
+ }
+ }
+ audio->buffer_size = cfg.buffer_size;
+ break;
+ }
+ case AUDIO_GET_STREAM_CONFIG: {
+ struct msm_audio_stream_config cfg;
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.buffer_size = audio->buffer_size;
+ cfg.buffer_count = FRAME_NUM;
+ if (copy_to_user((void *) arg, &cfg, sizeof(cfg)))
+ rc = -EFAULT;
+ break;
+ }
+ case AUDIO_GET_EVRC_ENC_CONFIG: {
+ if (copy_to_user((void *) arg, &audio->cfg, sizeof(audio->cfg)))
+ rc = -EFAULT;
+ break;
+ }
+ case AUDIO_SET_EVRC_ENC_CONFIG: {
+ struct msm_audio_evrc_enc_config cfg;
+ if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) {
+ rc = -EFAULT;
+ break;
+ }
+ MM_DBG("0X%8x, 0x%8x, 0x%8x\n", cfg.min_bit_rate,
+ cfg.max_bit_rate, cfg.cdma_rate);
+ if (cfg.min_bit_rate > CDMA_RATE_FULL || \
+ cfg.min_bit_rate < CDMA_RATE_EIGHTH) {
+ MM_ERR("invalid min bitrate\n");
+ rc = -EFAULT;
+ break;
+ }
+ if (cfg.max_bit_rate > CDMA_RATE_FULL || \
+ cfg.max_bit_rate < CDMA_RATE_EIGHTH) {
+ MM_ERR("invalid max bitrate\n");
+ rc = -EFAULT;
+ break;
+ }
+ /* Recording Does not support Erase and Blank */
+ if (cfg.cdma_rate > CDMA_RATE_FULL ||
+ cfg.cdma_rate < CDMA_RATE_EIGHTH) {
+ MM_ERR("invalid qcelp cdma rate\n");
+ rc = -EFAULT;
+ break;
+ }
+ memcpy(&audio->cfg, &cfg, sizeof(cfg));
+ break;
+ }
+ case AUDIO_GET_CONFIG: {
+ struct msm_audio_config cfg;
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.buffer_size = OUT_BUFFER_SIZE;
+ cfg.buffer_count = OUT_FRAME_NUM;
+ cfg.sample_rate = audio->samp_rate;
+ cfg.channel_count = audio->channel_mode;
+ if (copy_to_user((void *)arg, &cfg, sizeof(cfg)))
+ rc = -EFAULT;
+ break;
+ }
+ case AUDIO_SET_INCALL: {
+ struct msm_voicerec_mode cfg;
+ unsigned long flags;
+ if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+ if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (cfg.rec_mode != VOC_REC_BOTH &&
+ cfg.rec_mode != VOC_REC_UPLINK &&
+ cfg.rec_mode != VOC_REC_DOWNLINK) {
+ MM_ERR("invalid rec_mode\n");
+ rc = -EINVAL;
+ break;
+ } else {
+ spin_lock_irqsave(&audio->dev_lock, flags);
+ if (cfg.rec_mode == VOC_REC_UPLINK)
+ audio->source = \
+ VOICE_UL_SOURCE_MIX_MASK;
+ else if (cfg.rec_mode == VOC_REC_DOWNLINK)
+ audio->source = \
+ VOICE_DL_SOURCE_MIX_MASK;
+ else
+ audio->source = \
+ VOICE_DL_SOURCE_MIX_MASK |
+ VOICE_UL_SOURCE_MIX_MASK ;
+ audio->in_call = 1;
+ spin_unlock_irqrestore(&audio->dev_lock, flags);
+ }
+ }
+ break;
+ }
+ case AUDIO_GET_SESSION_ID: {
+ if (copy_to_user((void *) arg, &audio->enc_id,
+ sizeof(unsigned short))) {
+ rc = -EFAULT;
+ }
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static ssize_t audevrc_in_read(struct file *file,
+ char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio_in *audio = file->private_data;
+ unsigned long flags;
+ const char __user *start = buf;
+ void *data;
+ uint32_t index;
+ uint32_t size;
+ int rc = 0;
+ struct evrc_encoded_meta_out meta_field;
+ struct audio_frame_nt *nt_frame;
+ MM_DBG("count = %d\n", count);
+ mutex_lock(&audio->read_lock);
+ while (count > 0) {
+ rc = wait_event_interruptible(
+ audio->wait, (audio->in_count > 0) || audio->stopped ||
+ audio->rflush ||
+ ((audio->mode == MSM_AUD_ENC_MODE_TUNNEL) &&
+ audio->in_call && audio->running &&
+ (audio->voice_state == VOICE_STATE_OFFCALL)));
+ if (rc < 0)
+ break;
+
+ if (audio->rflush) {
+ rc = -EBUSY;
+ break;
+ }
+ if (audio->stopped && !audio->in_count) {
+ MM_DBG("Driver in stop state, No more buffer to read");
+ rc = 0;/* End of File */
+ break;
+ } else if ((audio->mode == MSM_AUD_ENC_MODE_TUNNEL) &&
+ audio->in_call && audio->running &&
+ (audio->voice_state \
+ == VOICE_STATE_OFFCALL)) {
+ MM_DBG("Not Permitted Voice Terminated\n");
+ rc = -EPERM; /* Voice Call stopped */
+ break;
+ }
+
+ index = audio->in_tail;
+ data = (uint8_t *) audio->in[index].data;
+ size = audio->in[index].size;
+
+ if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) {
+ nt_frame = (struct audio_frame_nt *)(data -
+ sizeof(struct audio_frame_nt));
+ memcpy((char *)&meta_field.time_stamp_dword_lsw,
+ (char *)&nt_frame->time_stamp_dword_lsw,
+ (sizeof(struct evrc_encoded_meta_out) - \
+ sizeof(uint16_t)));
+ meta_field.metadata_len =
+ sizeof(struct evrc_encoded_meta_out);
+ if (copy_to_user((char *)start, (char *)&meta_field,
+ sizeof(struct evrc_encoded_meta_out))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (nt_frame->nflag_lsw & 0x0001) {
+ MM_ERR("recieved EOS in read call\n");
+ audio->eos_ack = 1;
+ }
+ buf += sizeof(struct evrc_encoded_meta_out);
+ count -= sizeof(struct evrc_encoded_meta_out);
+ }
+ if (count >= size) {
+ if (copy_to_user(buf, data, size)) {
+ rc = -EFAULT;
+ break;
+ }
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (index != audio->in_tail) {
+ /* overrun -- data is
+ * invalid and we need to retry */
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ continue;
+ }
+ audio->in[index].size = 0;
+ audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1);
+ audio->in_count--;
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ count -= size;
+ buf += size;
+ if ((audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL)) {
+ if (!audio->eos_ack) {
+ MM_DBG("sending read ptr command \
+ %d %d\n",
+ audio->dsp_cnt,
+ audio->in_tail);
+ audevrc_dsp_read_buffer(audio,
+ audio->dsp_cnt++);
+ }
+ }
+ } else {
+ MM_ERR("short read\n");
+ break;
+ }
+ break;
+ }
+ mutex_unlock(&audio->read_lock);
+
+ if (buf > start)
+ return buf - start;
+
+ return rc;
+}
+
+static void audpreproc_pcm_send_data(struct audio_in *audio, unsigned needed)
+{
+ struct buffer *frame;
+ unsigned long flags;
+ MM_DBG("\n");
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (!audio->running)
+ goto done;
+
+ if (needed && !audio->wflush) {
+ /* We were called from the callback because the DSP
+ * requested more data. Note that the DSP does want
+ * more data, and if a buffer was in-flight, mark it
+ * as available (since the DSP must now be done with
+ * it).
+ */
+ audio->out_needed = 1;
+ frame = audio->out + audio->out_tail;
+ if (frame->used == 0xffffffff) {
+ MM_DBG("frame %d free\n", audio->out_tail);
+ frame->used = 0;
+ audio->out_tail ^= 1;
+ wake_up(&audio->write_wait);
+ }
+ }
+
+ if (audio->out_needed) {
+ /* If the DSP currently wants data and we have a
+ * buffer available, we will send it and reset
+ * the needed flag. We'll mark the buffer as in-flight
+ * so that it won't be recycled until the next buffer
+ * is requested
+ */
+
+ frame = audio->out + audio->out_tail;
+ if (frame->used) {
+ BUG_ON(frame->used == 0xffffffff);
+ audpreproc_pcm_buffer_ptr_refresh(audio,
+ audio->out_tail,
+ frame->used);
+ frame->used = 0xffffffff;
+ audio->out_needed = 0;
+ }
+ }
+ done:
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+
+static int audevrc_in_fsync(struct file *file, int datasync)
+
+{
+ struct audio_in *audio = file->private_data;
+ int rc = 0;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ if (!audio->running || (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)) {
+ rc = -EINVAL;
+ goto done_nolock;
+ }
+
+ mutex_lock(&audio->write_lock);
+
+ rc = wait_event_interruptible(audio->write_wait,
+ audio->wflush);
+ MM_DBG("waked on by some event audio->wflush = %d\n", audio->wflush);
+
+ if (rc < 0)
+ goto done;
+ else if (audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+done:
+ mutex_unlock(&audio->write_lock);
+done_nolock:
+ return rc;
+
+}
+
+ int audpreproc_evrc_process_eos(struct audio_in *audio,
+ const char __user *buf_start, unsigned short mfield_size)
+{
+ struct buffer *frame;
+ int rc = 0;
+
+ frame = audio->out + audio->out_head;
+
+ rc = wait_event_interruptible(audio->write_wait,
+ (audio->out_needed &&
+ audio->out[0].used == 0 &&
+ audio->out[1].used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+
+ if (rc < 0)
+ goto done;
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+ if (copy_from_user(frame->data, buf_start, mfield_size)) {
+ rc = -EFAULT;
+ goto done;
+ }
+
+ frame->mfield_sz = mfield_size;
+ audio->out_head ^= 1;
+ frame->used = mfield_size;
+ MM_DBG("copying meta_out frame->used = %d\n", frame->used);
+ audpreproc_pcm_send_data(audio, 0);
+done:
+ return rc;
+}
+
+static ssize_t audevrc_in_write(struct file *file,
+ const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio_in *audio = file->private_data;
+ const char __user *start = buf;
+ struct buffer *frame;
+ char *cpy_ptr;
+ int rc = 0, eos_condition = AUDPREPROC_EVRC_EOS_NONE;
+ unsigned short mfield_size = 0;
+ int write_count = 0;
+ MM_DBG("cnt=%d\n", count);
+
+ if (count & 1)
+ return -EINVAL;
+
+ if (audio->mode != MSM_AUD_ENC_MODE_NONTUNNEL)
+ return -EINVAL;
+
+ mutex_lock(&audio->write_lock);
+ frame = audio->out + audio->out_head;
+ /* if supplied count is more than driver buffer size
+ * then only copy driver buffer size
+ */
+ if (count > frame->size)
+ count = frame->size;
+
+ write_count = count;
+ cpy_ptr = frame->data;
+ rc = wait_event_interruptible(audio->write_wait,
+ (frame->used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+ if (rc < 0)
+ goto error;
+
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ goto error;
+ }
+ if (audio->mfield) {
+ if (buf == start) {
+ /* Processing beginning of user buffer */
+ if (__get_user(mfield_size,
+ (unsigned short __user *) buf)) {
+ rc = -EFAULT;
+ goto error;
+ } else if (mfield_size > count) {
+ rc = -EINVAL;
+ goto error;
+ }
+ MM_DBG("mf offset_val %x\n", mfield_size);
+ if (copy_from_user(cpy_ptr, buf, mfield_size)) {
+ rc = -EFAULT;
+ goto error;
+ }
+ /* Check if EOS flag is set and buffer has
+ * contains just meta field
+ */
+ if (cpy_ptr[AUDPREPROC_EVRC_EOS_FLG_OFFSET] &
+ AUDPREPROC_EVRC_EOS_FLG_MASK) {
+ eos_condition = AUDPREPROC_EVRC_EOS_SET;
+ MM_DBG("EOS SET\n");
+ if (mfield_size == count) {
+ buf += mfield_size;
+ eos_condition = 0;
+ goto exit;
+ } else
+ cpy_ptr[AUDPREPROC_EVRC_EOS_FLG_OFFSET] &=
+ ~AUDPREPROC_EVRC_EOS_FLG_MASK;
+ }
+ cpy_ptr += mfield_size;
+ count -= mfield_size;
+ buf += mfield_size;
+ } else {
+ mfield_size = 0;
+ MM_DBG("continuous buffer\n");
+ }
+ frame->mfield_sz = mfield_size;
+ }
+ MM_DBG("copying the stream count = %d\n", count);
+ if (copy_from_user(cpy_ptr, buf, count)) {
+ rc = -EFAULT;
+ goto error;
+ }
+exit:
+ frame->used = count;
+ audio->out_head ^= 1;
+ if (!audio->flush_ack)
+ audpreproc_pcm_send_data(audio, 0);
+ else {
+ audpreproc_pcm_send_data(audio, 1);
+ audio->flush_ack = 0;
+ }
+ if (eos_condition == AUDPREPROC_EVRC_EOS_SET)
+ rc = audpreproc_evrc_process_eos(audio, start, mfield_size);
+ mutex_unlock(&audio->write_lock);
+ return write_count;
+error:
+ mutex_unlock(&audio->write_lock);
+ return rc;
+}
+
+static int audevrc_in_release(struct inode *inode, struct file *file)
+{
+ struct audio_in *audio = file->private_data;
+
+ mutex_lock(&audio->lock);
+ audio->in_call = 0;
+ /* with draw frequency for session
+ incase not stopped the driver */
+ msm_snddev_withdraw_freq(audio->enc_id, SNDDEV_CAP_TX,
+ AUDDEV_CLNT_ENC);
+ auddev_unregister_evt_listner(AUDDEV_CLNT_ENC, audio->enc_id);
+ /*reset the sampling frequency information at audpreproc layer*/
+ audio->session_info.sampling_freq = 0;
+ audpreproc_update_audrec_info(&audio->session_info);
+ audevrc_in_disable(audio);
+ audevrc_in_flush(audio);
+ msm_adsp_put(audio->audrec);
+ audpreproc_aenc_free(audio->enc_id);
+ audio->audrec = NULL;
+ audio->opened = 0;
+ if (audio->data) {
+ iounmap(audio->data);
+ pmem_kfree(audio->phys);
+ audio->data = NULL;
+ }
+ if (audio->out_data) {
+ iounmap(audio->out_data);
+ pmem_kfree(audio->out_phys);
+ audio->out_data = NULL;
+ }
+ mutex_unlock(&audio->lock);
+ return 0;
+}
+
+struct audio_in the_audio_evrc_in;
+static int audevrc_in_open(struct inode *inode, struct file *file)
+{
+ struct audio_in *audio = &the_audio_evrc_in;
+ int rc;
+ int encid;
+
+ mutex_lock(&audio->lock);
+ if (audio->opened) {
+ rc = -EBUSY;
+ goto done;
+ }
+ audio->phys = pmem_kalloc(DMASZ, PMEM_MEMTYPE_EBI1|
+ PMEM_ALIGNMENT_4K);
+ if (!IS_ERR((void *)audio->phys)) {
+ audio->data = ioremap(audio->phys, DMASZ);
+ if (!audio->data) {
+ MM_ERR("could not allocate DMA buffers\n");
+ rc = -ENOMEM;
+ pmem_kfree(audio->phys);
+ goto done;
+ }
+ } else {
+ MM_ERR("could not allocate DMA buffers\n");
+ rc = -ENOMEM;
+ goto done;
+ }
+ MM_DBG("Memory addr = 0x%8x phy addr = 0x%8x\n",\
+ (int) audio->data, (int) audio->phys);
+ if ((file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ audio->mode = MSM_AUD_ENC_MODE_NONTUNNEL;
+ MM_DBG("Opened for non tunnel mode encoding\n");
+ } else if (!(file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ audio->mode = MSM_AUD_ENC_MODE_TUNNEL;
+ MM_DBG("Opened for tunnel mode encoding\n");
+ } else {
+ MM_ERR("Invalid mode\n");
+ rc = -EACCES;
+ goto done;
+ }
+
+ /* Settings will be re-config at AUDIO_SET_CONFIG,
+ * but at least we need to have initial config
+ */
+ if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL)
+ audio->buffer_size = (EVRC_FRAME_SIZE + 14);
+ else
+ audio->buffer_size = (FRAME_SIZE - 8);
+ audio->enc_type = ENC_TYPE_EVRC | audio->mode;
+ audio->samp_rate = 8000;
+ audio->channel_mode = AUDREC_CMD_MODE_MONO;
+ audio->cfg.cdma_rate = CDMA_RATE_FULL;
+ audio->cfg.min_bit_rate = CDMA_RATE_FULL;
+ audio->cfg.max_bit_rate = CDMA_RATE_FULL;
+
+ encid = audpreproc_aenc_alloc(audio->enc_type, &audio->module_name,
+ &audio->queue_ids);
+ if (encid < 0) {
+ MM_ERR("No free encoder available\n");
+ rc = -ENODEV;
+ goto done;
+ }
+ audio->enc_id = encid;
+
+ rc = msm_adsp_get(audio->module_name, &audio->audrec,
+ &audrec_evrc_adsp_ops, audio);
+
+ if (rc) {
+ audpreproc_aenc_free(audio->enc_id);
+ goto done;
+ }
+
+ audio->stopped = 0;
+ audio->source = 0;
+ audio->wflush = 0;
+ audio->rflush = 0;
+ audio->flush_ack = 0;
+
+ audevrc_in_flush(audio);
+ audevrc_out_flush(audio);
+
+ audio->out_phys = pmem_kalloc(BUFFER_SIZE,
+ PMEM_MEMTYPE_EBI1 | PMEM_ALIGNMENT_4K);
+ if (IS_ERR((void *)audio->out_phys)) {
+ MM_ERR("could not allocate write buffers\n");
+ rc = -ENOMEM;
+ goto evt_error;
+ } else {
+ audio->out_data = ioremap(audio->out_phys, BUFFER_SIZE);
+ if (!audio->out_data) {
+ MM_ERR("could map write buffers\n");
+ rc = -ENOMEM;
+ pmem_kfree(audio->out_phys);
+ goto evt_error;
+ }
+ MM_DBG("write buf: phy addr 0x%08x kernel addr 0x%08x\n",
+ audio->out_phys, (int)audio->out_data);
+ }
+
+ /* Initialize buffer */
+ audio->out[0].data = audio->out_data + 0;
+ audio->out[0].addr = audio->out_phys + 0;
+ audio->out[0].size = OUT_BUFFER_SIZE;
+
+ audio->out[1].data = audio->out_data + OUT_BUFFER_SIZE;
+ audio->out[1].addr = audio->out_phys + OUT_BUFFER_SIZE;
+ audio->out[1].size = OUT_BUFFER_SIZE;
+
+ MM_DBG("audio->out[0].data = %d audio->out[1].data = %d",
+ (unsigned int)audio->out[0].data,
+ (unsigned int)audio->out[1].data);
+ audio->device_events = AUDDEV_EVT_DEV_RDY | AUDDEV_EVT_DEV_RLS |
+ AUDDEV_EVT_VOICE_STATE_CHG;
+
+ audio->voice_state = msm_get_voice_state();
+ rc = auddev_register_evt_listner(audio->device_events,
+ AUDDEV_CLNT_ENC, audio->enc_id,
+ evrc_in_listener, (void *) audio);
+ if (rc) {
+ MM_ERR("failed to register device event listener\n");
+ iounmap(audio->out_data);
+ pmem_kfree(audio->out_phys);
+ goto evt_error;
+ }
+ audio->mfield = META_OUT_SIZE;
+ file->private_data = audio;
+ audio->opened = 1;
+ audio->out_frame_cnt++;
+done:
+ mutex_unlock(&audio->lock);
+ return rc;
+evt_error:
+ msm_adsp_put(audio->audrec);
+ audpreproc_aenc_free(audio->enc_id);
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static const struct file_operations audio_in_fops = {
+ .owner = THIS_MODULE,
+ .open = audevrc_in_open,
+ .release = audevrc_in_release,
+ .read = audevrc_in_read,
+ .write = audevrc_in_write,
+ .fsync = audevrc_in_fsync,
+ .unlocked_ioctl = audevrc_in_ioctl,
+};
+
+struct miscdevice audio_evrc_in_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_evrc_in",
+ .fops = &audio_in_fops,
+};
+
+static int __init audevrc_in_init(void)
+{
+ mutex_init(&the_audio_evrc_in.lock);
+ mutex_init(&the_audio_evrc_in.read_lock);
+ spin_lock_init(&the_audio_evrc_in.dsp_lock);
+ spin_lock_init(&the_audio_evrc_in.dev_lock);
+ init_waitqueue_head(&the_audio_evrc_in.wait);
+ init_waitqueue_head(&the_audio_evrc_in.wait_enable);
+ mutex_init(&the_audio_evrc_in.write_lock);
+ init_waitqueue_head(&the_audio_evrc_in.write_wait);
+ return misc_register(&audio_evrc_in_misc);
+}
+
+device_initcall(audevrc_in_init);
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_fm.c b/arch/arm/mach-msm/qdsp5v2/audio_fm.c
new file mode 100644
index 0000000..af65c80
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_fm.c
@@ -0,0 +1,358 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Based on the mp3 native driver in arch/arm/mach-msm/qdsp5v2/audio_mp3.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ *
+ * All source code in this file is licensed under the following license except
+ * where indicated.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/android_pmem.h>
+#include <linux/msm_audio.h>
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+#include <mach/debug_mm.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/qdsp5v2/afe.h>
+#include <mach/qdsp5v2/acdb_commands.h>
+#include <mach/qdsp5v2/audio_acdbi.h>
+#include <mach/qdsp5v2/audio_acdb_def.h>
+
+#define SESSION_ID_FM 6
+#define FM_ENABLE 0xFFFF
+#define FM_DISABLE 0x0
+#define FM_COPP 0x2
+/* Macro specifies maximum FM routing
+ possible */
+#define FM_MAX_RX_ROUTE 0x2
+
+struct fm_rx_calib_gain {
+ uint16_t device_id;
+ struct auddev_evt_devinfo dev_details;
+ struct acdb_calib_gain_rx calib_rx;
+};
+
+struct audio {
+ struct mutex lock;
+
+ int opened;
+ int enabled;
+ int running;
+
+ uint16_t dec_id;
+ uint16_t source;
+ uint16_t fm_source;
+ uint16_t fm_mask;
+ uint32_t device_events;
+ uint16_t volume;
+ struct fm_rx_calib_gain fm_calibration_rx[FM_MAX_RX_ROUTE];
+};
+
+static struct audio fm_audio;
+
+/* must be called with audio->lock held */
+static int audio_enable(struct audio *audio)
+{
+ int rc = 0;
+ if (audio->enabled)
+ return 0;
+
+ MM_DBG("fm mask= %08x fm_source = %08x\n",
+ audio->fm_mask, audio->fm_source);
+ if (audio->fm_mask && audio->fm_source) {
+ rc = afe_config_fm_codec(FM_ENABLE, audio->fm_mask);
+ if (!rc)
+ audio->running = 1;
+ /* Routed to icodec rx path */
+ if ((audio->fm_mask & AFE_HW_PATH_CODEC_RX) ==
+ AFE_HW_PATH_CODEC_RX) {
+ afe_config_fm_calibration_gain(
+ audio->fm_calibration_rx[0].device_id,
+ audio->fm_calibration_rx[0].calib_rx.audppcalgain);
+ }
+ /* Routed to aux codec rx path */
+ if ((audio->fm_mask & AFE_HW_PATH_AUXPCM_RX) ==
+ AFE_HW_PATH_AUXPCM_RX){
+ afe_config_fm_calibration_gain(
+ audio->fm_calibration_rx[1].device_id,
+ audio->fm_calibration_rx[1].calib_rx.audppcalgain);
+ }
+ }
+
+ audio->enabled = 1;
+ return rc;
+}
+
+static void fm_listner(u32 evt_id, union auddev_evt_data *evt_payload,
+ void *private_data)
+{
+ struct audio *audio = (struct audio *) private_data;
+ struct auddev_evt_devinfo *devinfo =
+ (struct auddev_evt_devinfo *)evt_payload;
+ switch (evt_id) {
+ case AUDDEV_EVT_DEV_RDY:
+ MM_DBG(":AUDDEV_EVT_DEV_RDY\n");
+ if (evt_payload->routing_id == FM_COPP)
+ audio->fm_source = 1;
+ else
+ audio->source = (0x1 << evt_payload->routing_id);
+
+ if (audio->source & 0x1)
+ audio->fm_mask = 0x1;
+ else if (audio->source & 0x2)
+ audio->fm_mask = 0x3;
+ else
+ audio->fm_mask = 0x0;
+
+ if (!audio->enabled
+ || !audio->fm_mask
+ || !audio->fm_source)
+ break;
+ else {
+ afe_config_fm_codec(FM_ENABLE, audio->fm_mask);
+ audio->running = 1;
+ }
+ break;
+ case AUDDEV_EVT_DEV_RLS:
+ MM_DBG(":AUDDEV_EVT_DEV_RLS\n");
+ if (evt_payload->routing_id == FM_COPP)
+ audio->fm_source = 0;
+ else
+ audio->source &= ~(0x1 << evt_payload->routing_id);
+
+ if (audio->source & 0x1)
+ audio->fm_mask = 0x1;
+ else if (audio->source & 0x2)
+ audio->fm_mask = 0x3;
+ else
+ audio->fm_mask = 0x0;
+
+ if (audio->running
+ && (!audio->fm_mask || !audio->fm_source)) {
+ afe_config_fm_codec(FM_DISABLE, audio->fm_mask);
+ audio->running = 0;
+ }
+ break;
+ case AUDDEV_EVT_STREAM_VOL_CHG:
+ MM_DBG(":AUDDEV_EVT_STREAM_VOL_CHG, stream vol \n");
+ audio->volume = evt_payload->session_vol;
+ afe_config_fm_volume(audio->volume);
+ break;
+ case AUDDEV_EVT_DEVICE_INFO:{
+ struct acdb_get_block get_block;
+ int rc = 0;
+ MM_DBG(":AUDDEV_EVT_DEVICE_INFO\n");
+ MM_DBG("sample_rate = %d\n", devinfo->sample_rate);
+ MM_DBG("acdb_id = %d\n", devinfo->acdb_id);
+ /* Applucable only for icodec rx and aux codec rx path
+ and fm stream routed to it */
+ if (((devinfo->dev_id == 0x00) || (devinfo->dev_id == 0x01)) &&
+ (devinfo->sessions && (1 << audio->dec_id))) {
+ /* Query ACDB driver for calib gain, only if difference
+ in device */
+ if ((audio->fm_calibration_rx[devinfo->dev_id].
+ dev_details.acdb_id != devinfo->acdb_id) ||
+ (audio->fm_calibration_rx[devinfo->dev_id].
+ dev_details.sample_rate !=
+ devinfo->sample_rate)) {
+ audio->fm_calibration_rx[devinfo->dev_id].
+ dev_details.dev_id = devinfo->dev_id;
+ audio->fm_calibration_rx[devinfo->dev_id].
+ dev_details.sample_rate =
+ devinfo->sample_rate;
+ audio->fm_calibration_rx[devinfo->dev_id].
+ dev_details.dev_type =
+ devinfo->dev_type;
+ audio->fm_calibration_rx[devinfo->dev_id].
+ dev_details.sessions =
+ devinfo->sessions;
+ /* Query ACDB driver for calibration gain */
+ get_block.acdb_id = devinfo->acdb_id;
+ get_block.sample_rate_id = devinfo->sample_rate;
+ get_block.interface_id =
+ IID_AUDIO_CALIBRATION_GAIN_RX;
+ get_block.algorithm_block_id =
+ ABID_AUDIO_CALIBRATION_GAIN_RX;
+ get_block.total_bytes =
+ sizeof(struct acdb_calib_gain_rx);
+ get_block.buf_ptr = (u32 *)
+ &audio->fm_calibration_rx[devinfo->dev_id].
+ calib_rx;
+
+ rc = acdb_get_calibration_data(&get_block);
+ if (rc < 0) {
+ MM_ERR("Unable to get calibration"\
+ "gain\n");
+ /* Set to unity incase of error */
+ audio->\
+ fm_calibration_rx[devinfo->dev_id].
+ calib_rx.audppcalgain = 0x2000;
+ } else
+ MM_DBG("calibration gain = 0x%8x\n",
+ *(get_block.buf_ptr));
+ }
+ if (audio->running) {
+ afe_config_fm_calibration_gain(
+ audio->fm_calibration_rx[devinfo->dev_id].
+ device_id,
+ audio->fm_calibration_rx[devinfo->dev_id].
+ calib_rx.audppcalgain);
+ }
+ }
+ break;
+ }
+ default:
+ MM_DBG(":ERROR:wrong event\n");
+ break;
+ }
+}
+/* must be called with audio->lock held */
+static int audio_disable(struct audio *audio)
+{
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ return afe_config_fm_codec(FM_DISABLE, audio->source);
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct audio *audio = file->private_data;
+ int rc = -EINVAL;
+
+ MM_DBG("cmd = %d\n", cmd);
+
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START:
+ MM_DBG("AUDIO_START\n");
+ rc = audio_enable(audio);
+ break;
+ case AUDIO_STOP:
+ MM_DBG("AUDIO_STOP\n");
+ rc = audio_disable(audio);
+ audio->running = 0;
+ audio->enabled = 0;
+ break;
+ case AUDIO_GET_SESSION_ID:
+ if (copy_to_user((void *) arg, &audio->dec_id,
+ sizeof(unsigned short)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static int audio_release(struct inode *inode, struct file *file)
+{
+ struct audio *audio = file->private_data;
+
+ MM_DBG("audio instance 0x%08x freeing\n", (int)audio);
+ mutex_lock(&audio->lock);
+ auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id);
+ audio_disable(audio);
+ audio->running = 0;
+ audio->enabled = 0;
+ audio->opened = 0;
+ mutex_unlock(&audio->lock);
+ return 0;
+}
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct audio *audio = &fm_audio;
+ int rc = 0;
+
+
+ if (audio->opened)
+ return -EPERM;
+
+ /* Allocate the decoder */
+ audio->dec_id = SESSION_ID_FM;
+
+ audio->running = 0;
+ audio->fm_source = 0;
+ audio->fm_mask = 0;
+
+ /* Initialize the calibration gain structure */
+ audio->fm_calibration_rx[0].device_id = AFE_HW_PATH_CODEC_RX;
+ audio->fm_calibration_rx[1].device_id = AFE_HW_PATH_AUXPCM_RX;
+ audio->fm_calibration_rx[0].calib_rx.audppcalgain = 0x2000;
+ audio->fm_calibration_rx[1].calib_rx.audppcalgain = 0x2000;
+ audio->fm_calibration_rx[0].dev_details.acdb_id = PSEUDO_ACDB_ID;
+ audio->fm_calibration_rx[1].dev_details.acdb_id = PSEUDO_ACDB_ID;
+
+ audio->device_events = AUDDEV_EVT_DEV_RDY
+ |AUDDEV_EVT_DEV_RLS|
+ AUDDEV_EVT_STREAM_VOL_CHG|
+ AUDDEV_EVT_DEVICE_INFO;
+
+ rc = auddev_register_evt_listner(audio->device_events,
+ AUDDEV_CLNT_DEC,
+ audio->dec_id,
+ fm_listner,
+ (void *)audio);
+
+ if (rc) {
+ MM_ERR("%s: failed to register listnet\n", __func__);
+ goto event_err;
+ }
+
+ audio->opened = 1;
+ file->private_data = audio;
+
+event_err:
+ return rc;
+}
+
+static const struct file_operations audio_fm_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_release,
+ .unlocked_ioctl = audio_ioctl,
+};
+
+struct miscdevice audio_fm_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_fm",
+ .fops = &audio_fm_fops,
+};
+
+static int __init audio_init(void)
+{
+ struct audio *audio = &fm_audio;
+
+ mutex_init(&audio->lock);
+ return misc_register(&audio_fm_misc);
+}
+
+device_initcall(audio_init);
+
+MODULE_DESCRIPTION("MSM FM driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_interct.c b/arch/arm/mach-msm/qdsp5v2/audio_interct.c
new file mode 100644
index 0000000..785ed8e
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_interct.c
@@ -0,0 +1,124 @@
+/* Copyright (c) 2009, 2011 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <asm/io.h>
+#include <mach/qdsp5v2/audio_interct.h>
+
+#define AUDIO_INTERCT_ADSPLPA_WBRX_SEL_BMSK 0x4
+#define AUDIO_INTERCT_ADSPLPA_WBRX_SEL_SHFT 0x2
+#define AUDIO_INTERCT_ADSPAV_RPCMI2SRX_SEL_BMSK 0x10
+#define AUDIO_INTERCT_ADSPAV_RPCMI2SRX_SEL_SHFT 0x4
+#define AUDIO_INTERCT_ADSPAV_TPCMI2STX_SEL_BMSK 0x40
+#define AUDIO_INTERCT_ADSPAV_TPCMI2STX_SEL_SHFT 0x6
+#define AUDIO_INTERCT_ADSPAV_AUX_REGSEL_BMSK 0x100
+#define AUDIO_INTERCT_ADSPAV_AUX_REGSEL_SHFT 0x8
+
+/* Should look to protect this register */
+void __iomem *aictl_reg;
+
+void audio_interct_codec(u32 source)
+{
+ u32 reg_val;
+
+ reg_val = readl(aictl_reg);
+ reg_val = (reg_val & ~AUDIO_INTERCT_ADSPLPA_WBRX_SEL_BMSK) |
+ (source << AUDIO_INTERCT_ADSPLPA_WBRX_SEL_SHFT);
+ writel(reg_val, aictl_reg);
+ mb();
+}
+EXPORT_SYMBOL(audio_interct_codec);
+
+void audio_interct_aux_regsel(u32 source)
+{
+ u32 reg_val;
+
+ reg_val = readl(aictl_reg);
+ reg_val = (reg_val & ~AUDIO_INTERCT_ADSPAV_AUX_REGSEL_BMSK) |
+ (source << AUDIO_INTERCT_ADSPAV_AUX_REGSEL_SHFT);
+ writel(reg_val, aictl_reg);
+ mb();
+}
+EXPORT_SYMBOL(audio_interct_aux_regsel);
+
+void audio_interct_tpcm_source(u32 source)
+{
+ u32 reg_val;
+
+ reg_val = readl(aictl_reg);
+ reg_val = (reg_val & ~AUDIO_INTERCT_ADSPAV_TPCMI2STX_SEL_BMSK) |
+ (source << AUDIO_INTERCT_ADSPAV_TPCMI2STX_SEL_SHFT);
+ writel(reg_val, aictl_reg);
+ mb();
+}
+EXPORT_SYMBOL(audio_interct_tpcm_source);
+
+void audio_interct_rpcm_source(u32 source)
+{
+ u32 reg_val;
+
+ reg_val = readl(aictl_reg);
+ reg_val = (reg_val & ~AUDIO_INTERCT_ADSPAV_RPCMI2SRX_SEL_BMSK) |
+ (source << AUDIO_INTERCT_ADSPAV_RPCMI2SRX_SEL_SHFT);
+ writel(reg_val, aictl_reg);
+ mb();
+}
+EXPORT_SYMBOL(audio_interct_rpcm_source);
+
+static int audio_interct_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct resource *aictl_mem;
+
+ aictl_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!aictl_mem) {
+ rc = -ENODEV;
+ goto error;
+ }
+ aictl_reg = ioremap(aictl_mem->start,
+ (aictl_mem->end - aictl_mem->start) + 1);
+error:
+ return rc;
+}
+
+
+static int audio_interct_remove(struct platform_device *pdev)
+{
+ iounmap(aictl_reg);
+ return 0;
+}
+
+static struct platform_driver audio_interct_driver = {
+ .probe = audio_interct_probe,
+ .remove = audio_interct_remove,
+ .driver = {
+ .name = "audio_interct",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init audio_interct_init(void)
+{
+ return platform_driver_register(&audio_interct_driver);
+}
+
+static void __exit audio_interct_exit(void)
+{
+ platform_driver_unregister(&audio_interct_driver);
+}
+
+module_init(audio_interct_init);
+module_exit(audio_interct_exit);
+
+MODULE_DESCRIPTION("MSM Audio Interconnect driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_lpa.c b/arch/arm/mach-msm/qdsp5v2/audio_lpa.c
new file mode 100644
index 0000000..da4ae73
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_lpa.c
@@ -0,0 +1,1700 @@
+/* low power audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/earlysuspend.h>
+#include <linux/list.h>
+#include <linux/android_pmem.h>
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+#include <linux/slab.h>
+#include <linux/msm_audio.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/qdsp5audplaycmdi.h>
+#include <mach/qdsp5v2/qdsp5audplaymsg.h>
+#include <mach/qdsp5v2/audpp.h>
+#include <mach/qdsp5v2/codec_utils.h>
+#include <mach/qdsp5v2/mp3_funcs.h>
+#include <mach/qdsp5v2/pcm_funcs.h>
+#include <mach/debug_mm.h>
+
+#define ADRV_STATUS_AIO_INTF 0x00000001
+#define ADRV_STATUS_OBUF_GIVEN 0x00000002
+#define ADRV_STATUS_IBUF_GIVEN 0x00000004
+#define ADRV_STATUS_FSYNC 0x00000008
+#define ADRV_STATUS_PAUSE 0x00000010
+
+#define DEVICE_SWITCH_STATE_NONE 0
+#define DEVICE_SWITCH_STATE_PENDING 1
+#define DEVICE_SWITCH_STATE_READY 2
+#define DEVICE_SWITCH_STATE_COMPLETE 3
+
+#define AUDDEC_DEC_PCM 0
+#define AUDDEC_DEC_MP3 2
+
+#define PCM_BUFSZ_MIN 4800 /* Hold one stereo MP3 frame */
+
+/* Decoder status received from AUDPPTASK */
+#define AUDPP_DEC_STATUS_SLEEP 0
+#define AUDPP_DEC_STATUS_INIT 1
+#define AUDPP_DEC_STATUS_CFG 2
+#define AUDPP_DEC_STATUS_PLAY 3
+
+#define AUDMP3_METAFIELD_MASK 0xFFFF0000
+#define AUDMP3_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */
+#define AUDMP3_EOS_FLG_MASK 0x01
+#define AUDMP3_EOS_NONE 0x0 /* No EOS detected */
+#define AUDMP3_EOS_SET 0x1 /* EOS set in meta field */
+
+#define AUDLPA_EVENT_NUM 10 /* Default number of pre-allocated event packets */
+
+#define MASK_32BITS 0xFFFFFFFF
+
+#define __CONTAINS(r, v, l) ({ \
+ typeof(r) __r = r; \
+ typeof(v) __v = v; \
+ typeof(v) __e = __v + l; \
+ int res = ((__v >= __r->vaddr) && \
+ (__e <= __r->vaddr + __r->len)); \
+ res; \
+})
+
+#define CONTAINS(r1, r2) ({ \
+ typeof(r2) __r2 = r2; \
+ __CONTAINS(r1, __r2->vaddr, __r2->len); \
+})
+
+#define IN_RANGE(r, v) ({ \
+ typeof(r) __r = r; \
+ typeof(v) __vv = v; \
+ int res = ((__vv >= __r->vaddr) && \
+ (__vv < (__r->vaddr + __r->len))); \
+ res; \
+})
+
+#define OVERLAPS(r1, r2) ({ \
+ typeof(r1) __r1 = r1; \
+ typeof(r2) __r2 = r2; \
+ typeof(__r2->vaddr) __v = __r2->vaddr; \
+ typeof(__v) __e = __v + __r2->len - 1; \
+ int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \
+ res; \
+})
+
+/* payload[7]; -1 indicates error, 0 indicates no error */
+#define CHECK_ERROR(v) (!v[7])
+
+/* calculates avsync_info from payload */
+#define CALCULATE_AVSYNC_FROM_PAYLOAD(v) ((uint64_t)((((uint64_t)v[10]) \
+ << 32) | (v[11] & MASK_32BITS)))
+
+/* calculates avsync_info from avsync_info stored in audio */
+#define CALCULATE_AVSYNC(v) \
+ ((uint64_t)((((uint64_t)v[4]) << 32) | \
+ (v[5] << 16) | (v[6])))
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+struct audlpa_suspend_ctl {
+ struct early_suspend node;
+ struct audio *audio;
+};
+#endif
+
+struct audlpa_event {
+ struct list_head list;
+ int event_type;
+ union msm_audio_event_payload payload;
+};
+
+struct audlpa_pmem_region {
+ struct list_head list;
+ struct file *file;
+ int fd;
+ void *vaddr;
+ unsigned long paddr;
+ unsigned long kvaddr;
+ unsigned long len;
+ unsigned ref_cnt;
+};
+
+struct audlpa_buffer_node {
+ struct list_head list;
+ struct msm_audio_aio_buf buf;
+ unsigned long paddr;
+};
+
+struct audlpa_dec {
+ char *name;
+ int dec_attrb;
+ long (*ioctl)(struct file *, unsigned int, unsigned long);
+ void (*adec_params)(struct audio *);
+};
+
+struct audlpa_dec audlpa_decs[] = {
+ {"msm_mp3_lp", AUDDEC_DEC_MP3, &mp3_ioctl, &audpp_cmd_cfg_mp3_params},
+ {"msm_pcm_lp_dec", AUDDEC_DEC_PCM, &pcm_ioctl,
+ &audpp_cmd_cfg_pcm_params},
+};
+
+static int auddec_dsp_config(struct audio *audio, int enable);
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg);
+static void audlpa_post_event(struct audio *audio, int type,
+ union msm_audio_event_payload payload);
+static unsigned long audlpa_pmem_fixup(struct audio *audio, void *addr,
+ unsigned long len, int ref_up);
+static void audlpa_async_send_data(struct audio *audio, unsigned needed,
+ uint32_t *payload);
+
+static void lpa_listner(u32 evt_id, union auddev_evt_data *evt_payload,
+ void *private_data)
+{
+ struct audio *audio = (struct audio *) private_data;
+ switch (evt_id) {
+ case AUDDEV_EVT_DEV_RDY:
+ MM_DBG(":AUDDEV_EVT_DEV_RDY routing id = %d\n",
+ evt_payload->routing_id);
+ /* Do not select HLB path for icodec, if there is already COPP3
+ * routing exists. DSP can not support concurrency of HLB path
+ * and COPP3 routing as it involves different buffer Path */
+ if (((0x1 << evt_payload->routing_id) == AUDPP_MIXER_ICODEC) &&
+ !(audio->source & AUDPP_MIXER_3)) {
+ audio->source |= AUDPP_MIXER_HLB;
+ MM_DBG("mixer_mask modified for low-power audio\n");
+ } else
+ audio->source |= (0x1 << evt_payload->routing_id);
+
+ MM_DBG("running = %d, enabled = %d, source = 0x%x\n",
+ audio->running, audio->enabled, audio->source);
+ if (audio->running == 1 && audio->enabled == 1) {
+ audpp_route_stream(audio->dec_id, audio->source);
+ if (audio->source & AUDPP_MIXER_HLB)
+ audpp_dsp_set_vol_pan(
+ AUDPP_CMD_CFG_DEV_MIXER_ID_4,
+ &audio->vol_pan,
+ COPP);
+ else if (audio->source & AUDPP_MIXER_NONHLB)
+ audpp_dsp_set_vol_pan(
+ audio->dec_id, &audio->vol_pan, POPP);
+ if (audio->device_switch == DEVICE_SWITCH_STATE_READY) {
+ audio->wflush = 1;
+ audio->device_switch =
+ DEVICE_SWITCH_STATE_COMPLETE;
+ audpp_flush(audio->dec_id);
+ if (wait_event_interruptible(audio->write_wait,
+ !audio->wflush) < 0)
+ MM_DBG("AUDIO_FLUSH interrupted\n");
+
+ if (audio->wflush == 0) {
+ if (audio->drv_status &
+ ADRV_STATUS_PAUSE) {
+ if (audpp_pause(audio->dec_id,
+ 1))
+ MM_DBG("audpp_pause"
+ "failed\n");
+ }
+ }
+ }
+ }
+ break;
+ case AUDDEV_EVT_REL_PENDING:
+ MM_DBG(":AUDDEV_EVT_REL_PENDING\n");
+ /* If route to multiple devices like COPP3, not need to
+ * handle device switch */
+ if ((audio->running == 1) && (audio->enabled == 1) &&
+ !(audio->source & AUDPP_MIXER_3)) {
+ if (audio->device_switch == DEVICE_SWITCH_STATE_NONE) {
+ if (!(audio->drv_status & ADRV_STATUS_PAUSE)) {
+ if (audpp_pause(audio->dec_id, 1))
+ MM_DBG("audpp pause failed\n");
+ }
+ audio->device_switch =
+ DEVICE_SWITCH_STATE_PENDING;
+ audio->avsync_flag = 0;
+ if (audpp_query_avsync(audio->dec_id) < 0)
+ MM_DBG("query avsync failed\n");
+
+ if (wait_event_interruptible_timeout
+ (audio->avsync_wait, audio->avsync_flag,
+ msecs_to_jiffies(AVSYNC_EVENT_TIMEOUT)) < 0)
+ MM_DBG("AV sync timeout failed\n");
+ if (audio->avsync_flag == 1) {
+ if (audio->device_switch ==
+ DEVICE_SWITCH_STATE_PENDING)
+ audio->device_switch =
+ DEVICE_SWITCH_STATE_READY;
+ }
+ }
+ }
+ break;
+ case AUDDEV_EVT_DEV_RLS:
+ /* If there is already COPP3 routing exists. icodec route
+ * was not having HLB path. */
+ MM_DBG(":AUDDEV_EVT_DEV_RLS routing id = %d\n",
+ evt_payload->routing_id);
+ if (((0x1 << evt_payload->routing_id) == AUDPP_MIXER_ICODEC) &&
+ !(audio->source & AUDPP_MIXER_3))
+ audio->source &= ~AUDPP_MIXER_HLB;
+ else
+ audio->source &= ~(0x1 << evt_payload->routing_id);
+ MM_DBG("running = %d, enabled = %d, source = 0x%x\n",
+ audio->running, audio->enabled, audio->source);
+
+ if (audio->running == 1 && audio->enabled == 1)
+ audpp_route_stream(audio->dec_id, audio->source);
+ break;
+ case AUDDEV_EVT_STREAM_VOL_CHG:
+ audio->vol_pan.volume = evt_payload->session_vol;
+ MM_DBG("\n:AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n"
+ "running = %d, enabled = %d, source = 0x%x",
+ audio->vol_pan.volume, audio->running,
+ audio->enabled, audio->source);
+ if (audio->running == 1 && audio->enabled == 1) {
+ if (audio->source & AUDPP_MIXER_HLB)
+ audpp_dsp_set_vol_pan(
+ AUDPP_CMD_CFG_DEV_MIXER_ID_4,
+ &audio->vol_pan, COPP);
+ else if (audio->source & AUDPP_MIXER_NONHLB)
+ audpp_dsp_set_vol_pan(
+ audio->dec_id, &audio->vol_pan, POPP);
+ }
+ break;
+ default:
+ MM_ERR(":ERROR:wrong event\n");
+ break;
+ }
+}
+
+/* must be called with audio->lock held */
+static int audio_enable(struct audio *audio)
+{
+ MM_DBG("\n"); /* Macro prints the file name and function */
+
+ if (audio->enabled)
+ return 0;
+
+ audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+ audio->out_needed = 0;
+
+ if (msm_adsp_enable(audio->audplay)) {
+ MM_ERR("msm_adsp_enable(audplay) failed\n");
+ return -ENODEV;
+ }
+
+ if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) {
+ MM_ERR("audpp_enable() failed\n");
+ msm_adsp_disable(audio->audplay);
+ return -ENODEV;
+ }
+
+ audio->enabled = 1;
+ return 0;
+}
+
+/* must be called with audio->lock held */
+static int audio_disable(struct audio *audio)
+{
+ int rc = 0;
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ if (audio->enabled) {
+ audio->enabled = 0;
+ audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+ auddec_dsp_config(audio, 0);
+ rc = wait_event_interruptible_timeout(audio->wait,
+ audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+ msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE)
+ rc = -EFAULT;
+ else
+ rc = 0;
+ wake_up(&audio->write_wait);
+ msm_adsp_disable(audio->audplay);
+ audpp_disable(audio->dec_id, audio);
+ audio->out_needed = 0;
+ }
+ return rc;
+}
+
+/* ------------------- dsp --------------------- */
+static void audplay_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent) (void *ptr, size_t len))
+{
+ struct audio *audio = data;
+ uint32_t msg[28];
+ getevent(msg, sizeof(msg));
+
+ MM_DBG("msg_id=%x\n", id);
+
+ switch (id) {
+ case AUDPLAY_MSG_DEC_NEEDS_DATA:
+ audlpa_async_send_data(audio, 1, msg);
+ break;
+ case ADSP_MESSAGE_ID:
+ MM_DBG("Received ADSP event: module enable(audplaytask)\n");
+ break;
+ default:
+ MM_ERR("unexpected message from decoder\n");
+ break;
+ }
+}
+
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg)
+{
+ struct audio *audio = private;
+
+ switch (id) {
+ case AUDPP_MSG_STATUS_MSG:{
+ unsigned status = msg[1];
+
+ switch (status) {
+ case AUDPP_DEC_STATUS_SLEEP: {
+ uint16_t reason = msg[2];
+ MM_DBG("decoder status: sleep reason=0x%04x\n",
+ reason);
+ if ((reason == AUDPP_MSG_REASON_MEM)
+ || (reason ==
+ AUDPP_MSG_REASON_NODECODER)) {
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_FAILURE;
+ wake_up(&audio->wait);
+ } else if (reason == AUDPP_MSG_REASON_NONE) {
+ /* decoder is in disable state */
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_CLOSE;
+ wake_up(&audio->wait);
+ }
+ break;
+ }
+ case AUDPP_DEC_STATUS_INIT:
+ MM_DBG("decoder status: init\n");
+ audio->codec_ops.adec_params(audio);
+ break;
+ case AUDPP_DEC_STATUS_CFG:
+ MM_DBG("decoder status: cfg\n");
+ break;
+ case AUDPP_DEC_STATUS_PLAY:
+ MM_DBG("decoder status: play\n");
+ /* send mixer command */
+ audpp_route_stream(audio->dec_id,
+ audio->source);
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_SUCCESS;
+ wake_up(&audio->wait);
+ break;
+ case AUDPP_DEC_STATUS_EOS:
+ MM_DBG("decoder status: EOS\n");
+ audio->teos = 1;
+ wake_up(&audio->write_wait);
+ break;
+ default:
+ MM_ERR("unknown decoder status\n");
+ break;
+ }
+ break;
+ }
+ case AUDPP_MSG_CFG_MSG:
+ if (msg[0] == AUDPP_MSG_ENA_ENA) {
+ MM_DBG("CFG_MSG ENABLE\n");
+ auddec_dsp_config(audio, 1);
+ audio->out_needed = 0;
+ audio->running = 1;
+ MM_DBG("source = 0x%x\n", audio->source);
+ if (audio->source & AUDPP_MIXER_HLB)
+ audpp_dsp_set_vol_pan(
+ AUDPP_CMD_CFG_DEV_MIXER_ID_4,
+ &audio->vol_pan,
+ COPP);
+ else if (audio->source & AUDPP_MIXER_NONHLB)
+ audpp_dsp_set_vol_pan(
+ audio->dec_id, &audio->vol_pan,
+ POPP);
+ audpp_dsp_set_eq(audio->dec_id, audio->eq_enable,
+ &audio->eq, POPP);
+ } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+ MM_DBG("CFG_MSG DISABLE\n");
+ audio->running = 0;
+ } else {
+ MM_DBG("CFG_MSG %d?\n", msg[0]);
+ }
+ break;
+ case AUDPP_MSG_ROUTING_ACK:
+ MM_DBG("ROUTING_ACK mode=%d\n", msg[1]);
+ audio->codec_ops.adec_params(audio);
+ break;
+
+ case AUDPP_MSG_FLUSH_ACK:
+ MM_DBG("FLUSH_ACK\n");
+ audio->wflush = 0;
+ wake_up(&audio->write_wait);
+ break;
+
+ case AUDPP_MSG_PCMDMAMISSED:
+ MM_DBG("PCMDMAMISSED\n");
+ wake_up(&audio->write_wait);
+ break;
+
+ case AUDPP_MSG_AVSYNC_MSG:
+ MM_DBG("AVSYNC_MSG\n");
+ memcpy(&audio->avsync[0], msg, sizeof(audio->avsync));
+ audio->avsync_flag = 1;
+ wake_up(&audio->avsync_wait);
+ break;
+
+ default:
+ MM_ERR("UNKNOWN (%d)\n", id);
+ }
+
+}
+
+struct msm_adsp_ops audplay_adsp_ops_lpa = {
+ .event = audplay_dsp_event,
+};
+
+#define audplay_send_queue0(audio, cmd, len) \
+ msm_adsp_write(audio->audplay, audio->queue_id, \
+ cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+ struct audpp_cmd_cfg_dec_type cfg_dec_cmd;
+
+ memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd));
+
+ cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+ if (enable)
+ cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_ENA_DEC_V |
+ audlpa_decs[audio->minor_no].dec_attrb;
+ else
+ cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_DIS_DEC_V;
+ cfg_dec_cmd.dm_mode = 0x0;
+ cfg_dec_cmd.stream_id = audio->dec_id;
+ return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd));
+}
+
+static void audlpa_async_send_buffer(struct audio *audio)
+{
+ int found = 0;
+ uint64_t temp = 0;
+ struct audplay_cmd_bitstream_data_avail cmd;
+ struct audlpa_buffer_node *next_buf = NULL;
+
+ temp = audio->bytecount_head;
+ if (audio->device_switch == DEVICE_SWITCH_STATE_NONE) {
+ list_for_each_entry(next_buf, &audio->out_queue, list) {
+ if (temp == audio->bytecount_given) {
+ found = 1;
+ break;
+ } else
+ temp += next_buf->buf.data_len;
+ }
+ if (next_buf && found) {
+ cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL;
+ cmd.decoder_id = audio->dec_id;
+ cmd.buf_ptr = (unsigned) next_buf->paddr;
+ cmd.buf_size = next_buf->buf.data_len >> 1;
+ cmd.partition_number = 0;
+ audio->bytecount_given += next_buf->buf.data_len;
+ wmb();
+ audplay_send_queue0(audio, &cmd, sizeof(cmd));
+ audio->out_needed = 0;
+ audio->drv_status |= ADRV_STATUS_OBUF_GIVEN;
+ }
+ } else if (audio->device_switch == DEVICE_SWITCH_STATE_COMPLETE) {
+ audio->device_switch = DEVICE_SWITCH_STATE_NONE;
+ next_buf = list_first_entry(&audio->out_queue,
+ struct audlpa_buffer_node, list);
+ if (next_buf) {
+ cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL;
+ cmd.decoder_id = audio->dec_id;
+ temp = audio->bytecount_head +
+ next_buf->buf.data_len -
+ audio->bytecount_consumed;
+ if (audpp_restore_avsync(audio->dec_id,
+ &audio->avsync[0]))
+ MM_DBG("audpp_restore_avsync failed\n");
+
+ if ((signed)(temp >= 0) &&
+ ((signed)(next_buf->buf.data_len - temp) >= 0)) {
+ cmd.buf_ptr = (unsigned) (next_buf->paddr +
+ (next_buf->buf.data_len -
+ temp));
+ cmd.buf_size = temp >> 1;
+ cmd.partition_number = 0;
+ audio->bytecount_given =
+ audio->bytecount_consumed + temp;
+ wmb();
+ audplay_send_queue0(audio, &cmd, sizeof(cmd));
+ audio->out_needed = 0;
+ audio->drv_status |= ADRV_STATUS_OBUF_GIVEN;
+ }
+ }
+ }
+}
+
+static void audlpa_async_send_data(struct audio *audio, unsigned needed,
+ uint32_t *payload)
+{
+ unsigned long flags;
+ uint64_t temp = 0;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (!audio->running)
+ goto done;
+
+ if (needed && !audio->wflush) {
+ audio->out_needed = 1;
+ if (audio->drv_status & ADRV_STATUS_OBUF_GIVEN) {
+ union msm_audio_event_payload evt_payload;
+ struct audlpa_buffer_node *used_buf = NULL;
+
+ if (CHECK_ERROR(payload))
+ audio->bytecount_consumed =
+ CALCULATE_AVSYNC_FROM_PAYLOAD(payload);
+
+ if ((audio->device_switch ==
+ DEVICE_SWITCH_STATE_COMPLETE) &&
+ (audio->avsync_flag == 1)) {
+ audio->avsync_flag = 0;
+ audio->bytecount_consumed =
+ CALCULATE_AVSYNC(audio->avsync);
+ }
+ BUG_ON(list_empty(&audio->out_queue));
+ temp = audio->bytecount_head;
+ used_buf = list_first_entry(&audio->out_queue,
+ struct audlpa_buffer_node, list);
+ if ((audio->bytecount_head + used_buf->buf.data_len) <
+ audio->bytecount_consumed) {
+ audio->bytecount_head += used_buf->buf.data_len;
+ temp = audio->bytecount_head;
+ list_del(&used_buf->list);
+ evt_payload.aio_buf = used_buf->buf;
+ audlpa_post_event(audio, AUDIO_EVENT_WRITE_DONE,
+ evt_payload);
+ kfree(used_buf);
+ }
+ audio->drv_status &= ~ADRV_STATUS_OBUF_GIVEN;
+ }
+ }
+ if (audio->out_needed) {
+ if (!list_empty(&audio->out_queue))
+ audlpa_async_send_buffer(audio);
+ }
+done:
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+/* ------------------- device --------------------- */
+static void audlpa_async_flush(struct audio *audio)
+{
+ struct audlpa_buffer_node *buf_node;
+ struct list_head *ptr, *next;
+ union msm_audio_event_payload payload;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ list_for_each_safe(ptr, next, &audio->out_queue) {
+ buf_node = list_entry(ptr, struct audlpa_buffer_node, list);
+ list_del(&buf_node->list);
+ payload.aio_buf = buf_node->buf;
+ if ((buf_node->paddr != 0xFFFFFFFF) &&
+ (buf_node->buf.data_len != 0))
+ audlpa_post_event(audio, AUDIO_EVENT_WRITE_DONE,
+ payload);
+ kfree(buf_node);
+ }
+ audio->drv_status &= ~ADRV_STATUS_OBUF_GIVEN;
+ audio->out_needed = 0;
+ audio->bytecount_consumed = 0;
+ audio->bytecount_head = 0;
+ audio->bytecount_given = 0;
+ audio->device_switch = DEVICE_SWITCH_STATE_NONE;
+ atomic_set(&audio->out_bytes, 0);
+}
+
+static void audio_ioport_reset(struct audio *audio)
+{
+ /* If fsync is in progress, make sure
+ * return value of fsync indicates
+ * abort due to flush
+ */
+ if (audio->drv_status & ADRV_STATUS_FSYNC) {
+ MM_DBG("fsync in progress\n");
+ wake_up(&audio->write_wait);
+ mutex_lock(&audio->write_lock);
+ audlpa_async_flush(audio);
+ mutex_unlock(&audio->write_lock);
+ audio->avsync_flag = 1;
+ wake_up(&audio->avsync_wait);
+ } else
+ audlpa_async_flush(audio);
+}
+
+static int audlpa_events_pending(struct audio *audio)
+{
+ unsigned long flags;
+ int empty;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ empty = !list_empty(&audio->event_queue);
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+ return empty || audio->event_abort;
+}
+
+static void audlpa_reset_event_queue(struct audio *audio)
+{
+ unsigned long flags;
+ struct audlpa_event *drv_evt;
+ struct list_head *ptr, *next;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ list_for_each_safe(ptr, next, &audio->event_queue) {
+ drv_evt = list_first_entry(&audio->event_queue,
+ struct audlpa_event, list);
+ list_del(&drv_evt->list);
+ kfree(drv_evt);
+ }
+ list_for_each_safe(ptr, next, &audio->free_event_queue) {
+ drv_evt = list_first_entry(&audio->free_event_queue,
+ struct audlpa_event, list);
+ list_del(&drv_evt->list);
+ kfree(drv_evt);
+ }
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+ return;
+}
+
+static long audlpa_process_event_req(struct audio *audio, void __user *arg)
+{
+ long rc;
+ struct msm_audio_event usr_evt;
+ struct audlpa_event *drv_evt = NULL;
+ int timeout;
+ unsigned long flags;
+
+ if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event)))
+ return -EFAULT;
+
+ timeout = (int) usr_evt.timeout_ms;
+
+ if (timeout > 0) {
+ rc = wait_event_interruptible_timeout(
+ audio->event_wait, audlpa_events_pending(audio),
+ msecs_to_jiffies(timeout));
+ if (rc == 0)
+ return -ETIMEDOUT;
+ } else {
+ rc = wait_event_interruptible(
+ audio->event_wait, audlpa_events_pending(audio));
+ }
+
+ if (rc < 0)
+ return rc;
+
+ if (audio->event_abort) {
+ audio->event_abort = 0;
+ return -ENODEV;
+ }
+
+ rc = 0;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ if (!list_empty(&audio->event_queue)) {
+ drv_evt = list_first_entry(&audio->event_queue,
+ struct audlpa_event, list);
+ list_del(&drv_evt->list);
+ }
+ if (drv_evt) {
+ usr_evt.event_type = drv_evt->event_type;
+ usr_evt.event_payload = drv_evt->payload;
+ list_add_tail(&drv_evt->list, &audio->free_event_queue);
+ } else
+ rc = -1;
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+ if (drv_evt->event_type == AUDIO_EVENT_WRITE_DONE ||
+ drv_evt->event_type == AUDIO_EVENT_READ_DONE) {
+ mutex_lock(&audio->lock);
+ audlpa_pmem_fixup(audio, drv_evt->payload.aio_buf.buf_addr,
+ drv_evt->payload.aio_buf.buf_len, 0);
+ mutex_unlock(&audio->lock);
+ }
+ if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt)))
+ rc = -EFAULT;
+
+ return rc;
+}
+
+static int audlpa_pmem_check(struct audio *audio,
+ void *vaddr, unsigned long len)
+{
+ struct audlpa_pmem_region *region_elt;
+ struct audlpa_pmem_region t = { .vaddr = vaddr, .len = len };
+
+ list_for_each_entry(region_elt, &audio->pmem_region_queue, list) {
+ if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) ||
+ OVERLAPS(region_elt, &t)) {
+ MM_ERR("region (vaddr %p len %ld)"
+ " clashes with registered region"
+ " (vaddr %p paddr %p len %ld)\n",
+ vaddr, len,
+ region_elt->vaddr,
+ (void *)region_elt->paddr,
+ region_elt->len);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int audlpa_pmem_add(struct audio *audio,
+ struct msm_audio_pmem_info *info)
+{
+ unsigned long paddr, kvaddr, len;
+ struct file *file;
+ struct audlpa_pmem_region *region;
+ int rc = -EINVAL;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ region = kmalloc(sizeof(*region), GFP_KERNEL);
+
+ if (!region) {
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ if (get_pmem_file(info->fd, &paddr, &kvaddr, &len, &file)) {
+ kfree(region);
+ goto end;
+ }
+
+ rc = audlpa_pmem_check(audio, info->vaddr, len);
+ if (rc < 0) {
+ put_pmem_file(file);
+ kfree(region);
+ goto end;
+ }
+
+ region->vaddr = info->vaddr;
+ region->fd = info->fd;
+ region->paddr = paddr;
+ region->kvaddr = kvaddr;
+ region->len = len;
+ region->file = file;
+ region->ref_cnt = 0;
+ MM_DBG("add region paddr %lx vaddr %p, len %lu\n", region->paddr,
+ region->vaddr, region->len);
+ list_add_tail(®ion->list, &audio->pmem_region_queue);
+end:
+ return rc;
+}
+
+static int audlpa_pmem_remove(struct audio *audio,
+ struct msm_audio_pmem_info *info)
+{
+ struct audlpa_pmem_region *region;
+ struct list_head *ptr, *next;
+ int rc = -EINVAL;
+
+ MM_DBG("info fd %d vaddr %p\n", info->fd, info->vaddr);
+
+ list_for_each_safe(ptr, next, &audio->pmem_region_queue) {
+ region = list_entry(ptr, struct audlpa_pmem_region, list);
+
+ if ((region->fd == info->fd) &&
+ (region->vaddr == info->vaddr)) {
+ if (region->ref_cnt) {
+ MM_DBG("region %p in use ref_cnt %d\n",
+ region, region->ref_cnt);
+ break;
+ }
+ MM_DBG("remove region fd %d vaddr %p\n",
+ info->fd, info->vaddr);
+ list_del(®ion->list);
+ put_pmem_file(region->file);
+ kfree(region);
+ rc = 0;
+ break;
+ }
+ }
+
+ return rc;
+}
+
+static int audlpa_pmem_lookup_vaddr(struct audio *audio, void *addr,
+ unsigned long len, struct audlpa_pmem_region **region)
+{
+ struct audlpa_pmem_region *region_elt;
+
+ int match_count = 0;
+
+ *region = NULL;
+
+ /* returns physical address or zero */
+ list_for_each_entry(region_elt, &audio->pmem_region_queue,
+ list) {
+ if (addr >= region_elt->vaddr &&
+ addr < region_elt->vaddr + region_elt->len &&
+ addr + len <= region_elt->vaddr + region_elt->len) {
+ /* offset since we could pass vaddr inside a registerd
+ * pmem buffer
+ */
+
+ match_count++;
+ if (!*region)
+ *region = region_elt;
+ }
+ }
+
+ if (match_count > 1) {
+ MM_ERR("multiple hits for vaddr %p, len %ld\n", addr, len);
+ list_for_each_entry(region_elt,
+ &audio->pmem_region_queue, list) {
+ if (addr >= region_elt->vaddr &&
+ addr < region_elt->vaddr + region_elt->len &&
+ addr + len <= region_elt->vaddr + region_elt->len)
+ MM_ERR("\t%p, %ld --> %p\n", region_elt->vaddr,
+ region_elt->len,
+ (void *)region_elt->paddr);
+ }
+ }
+
+ return *region ? 0 : -1;
+}
+
+unsigned long audlpa_pmem_fixup(struct audio *audio, void *addr,
+ unsigned long len, int ref_up)
+{
+ struct audlpa_pmem_region *region;
+ unsigned long paddr;
+ int ret;
+
+ ret = audlpa_pmem_lookup_vaddr(audio, addr, len, ®ion);
+ if (ret) {
+ MM_ERR("lookup (%p, %ld) failed\n", addr, len);
+ return 0;
+ }
+ if (ref_up)
+ region->ref_cnt++;
+ else
+ region->ref_cnt--;
+ MM_DBG("found region %p ref_cnt %d\n", region, region->ref_cnt);
+ paddr = region->paddr + (addr - region->vaddr);
+ return paddr;
+}
+
+/* audio -> lock must be held at this point */
+static int audlpa_aio_buf_add(struct audio *audio, unsigned dir,
+ void __user *arg)
+{
+ unsigned long flags;
+ struct audlpa_buffer_node *buf_node;
+
+ buf_node = kmalloc(sizeof(*buf_node), GFP_KERNEL);
+
+ if (!buf_node)
+ return -ENOMEM;
+
+ if (copy_from_user(&buf_node->buf, arg, sizeof(buf_node->buf))) {
+ kfree(buf_node);
+ return -EFAULT;
+ }
+
+ MM_DBG("node %p dir %x buf_addr %p buf_len %d data_len"
+ "%d\n", buf_node, dir,
+ buf_node->buf.buf_addr, buf_node->buf.buf_len,
+ buf_node->buf.data_len);
+
+ buf_node->paddr = audlpa_pmem_fixup(
+ audio, buf_node->buf.buf_addr,
+ buf_node->buf.buf_len, 1);
+
+ if (dir) {
+ /* write */
+ if (!buf_node->paddr ||
+ (buf_node->paddr & 0x1) ||
+ (buf_node->buf.data_len & 0x1)) {
+ kfree(buf_node);
+ return -EINVAL;
+ }
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ list_add_tail(&buf_node->list, &audio->out_queue);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ audlpa_async_send_data(audio, 0, 0);
+ } else {
+ /* read */
+ }
+
+ MM_DBG("Add buf_node %p paddr %lx\n", buf_node, buf_node->paddr);
+
+ return 0;
+}
+
+static int audio_enable_eq(struct audio *audio, int enable)
+{
+ if (audio->eq_enable == enable && !audio->eq_needs_commit)
+ return 0;
+
+ audio->eq_enable = enable;
+
+ if (audio->running) {
+ audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq, POPP);
+ audio->eq_needs_commit = 0;
+ }
+ return 0;
+}
+
+static int audio_get_avsync_data(struct audio *audio,
+ struct msm_audio_stats *stats)
+{
+ int rc = -EINVAL;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) {
+ /* av_sync sample count */
+ stats->sample_count = (audio->avsync[2] << 16) |
+ (audio->avsync[3]);
+
+ /* av_sync byte_count */
+ stats->byte_count = (audio->avsync[5] << 16) |
+ (audio->avsync[6]);
+
+ audio->avsync_flag = 0;
+ rc = 0;
+ }
+ local_irq_restore(flags);
+ return rc;
+
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct audio *audio = file->private_data;
+ int rc = -EINVAL;
+ unsigned long flags = 0;
+ uint16_t enable_mask;
+ int enable;
+ int prev_state;
+
+ MM_DBG("audio_ioctl() cmd = %d\n", cmd);
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+
+ audio->avsync_flag = 0;
+ memset(&stats, 0, sizeof(stats));
+ if (audpp_query_avsync(audio->dec_id) < 0)
+ return rc;
+
+ rc = wait_event_interruptible_timeout(audio->avsync_wait,
+ (audio->avsync_flag == 1),
+ msecs_to_jiffies(AVSYNC_EVENT_TIMEOUT));
+
+ if (rc < 0)
+ return rc;
+ else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) {
+ if (audio_get_avsync_data(audio, &stats) < 0)
+ return rc;
+
+ if (copy_to_user((void *) arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ } else
+ return -EAGAIN;
+ }
+
+ switch (cmd) {
+ case AUDIO_ENABLE_AUDPP:
+ if (copy_from_user(&enable_mask, (void *) arg,
+ sizeof(enable_mask))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ enable = (enable_mask & EQ_ENABLE) ? 1 : 0;
+ audio_enable_eq(audio, enable);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ rc = 0;
+ break;
+
+ case AUDIO_SET_VOLUME:
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->vol_pan.volume = arg;
+ if (audio->running)
+ audpp_dsp_set_vol_pan(AUDPP_CMD_CFG_DEV_MIXER_ID_4,
+ &audio->vol_pan,
+ COPP);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ rc = 0;
+ break;
+
+ case AUDIO_SET_PAN:
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->vol_pan.pan = arg;
+ if (audio->running)
+ audpp_dsp_set_vol_pan(AUDPP_CMD_CFG_DEV_MIXER_ID_4,
+ &audio->vol_pan,
+ COPP);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ rc = 0;
+ break;
+
+ case AUDIO_SET_EQ:
+ prev_state = audio->eq_enable;
+ audio->eq_enable = 0;
+ if (copy_from_user(&audio->eq.num_bands, (void *) arg,
+ sizeof(audio->eq) -
+ (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) {
+ rc = -EFAULT;
+ break;
+ }
+ audio->eq_enable = prev_state;
+ audio->eq_needs_commit = 1;
+ rc = 0;
+ break;
+ }
+
+ if (-EINVAL != rc)
+ return rc;
+
+ if (cmd == AUDIO_GET_EVENT) {
+ MM_DBG(" AUDIO_GET_EVENT\n");
+ if (mutex_trylock(&audio->get_event_lock)) {
+ rc = audlpa_process_event_req(audio,
+ (void __user *) arg);
+ mutex_unlock(&audio->get_event_lock);
+ } else
+ rc = -EBUSY;
+ return rc;
+ }
+
+ if (cmd == AUDIO_ABORT_GET_EVENT) {
+ audio->event_abort = 1;
+ wake_up(&audio->event_wait);
+ return 0;
+ }
+
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START:
+ MM_DBG("AUDIO_START\n");
+ rc = audio_enable(audio);
+ if (!rc) {
+ rc = wait_event_interruptible_timeout(audio->wait,
+ audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+ msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+ MM_DBG("dec_state %d rc = %d\n", audio->dec_state, rc);
+
+ if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS)
+ rc = -ENODEV;
+ else
+ rc = 0;
+ }
+ break;
+
+ case AUDIO_STOP:
+ MM_DBG("AUDIO_STOP\n");
+ rc = audio_disable(audio);
+ audio->stopped = 1;
+ audio_ioport_reset(audio);
+ audio->stopped = 0;
+ audio->drv_status &= ~ADRV_STATUS_PAUSE;
+ break;
+
+ case AUDIO_FLUSH:
+ MM_DBG("AUDIO_FLUSH\n");
+ audio->wflush = 1;
+ audio_ioport_reset(audio);
+ if (audio->running) {
+ audpp_flush(audio->dec_id);
+ rc = wait_event_interruptible(audio->write_wait,
+ !audio->wflush);
+ if (rc < 0) {
+ MM_ERR("AUDIO_FLUSH interrupted\n");
+ rc = -EINTR;
+ }
+ } else {
+ audio->wflush = 0;
+ }
+ break;
+
+ case AUDIO_SET_CONFIG:{
+ struct msm_audio_config config;
+ MM_INFO("AUDIO_SET_CONFIG\n");
+ if (copy_from_user(&config, (void *) arg, sizeof(config))) {
+ rc = -EFAULT;
+ MM_INFO("ERROR: copy from user\n");
+ break;
+ }
+ if (config.channel_count == 1) {
+ config.channel_count = AUDPP_CMD_PCM_INTF_MONO_V;
+ } else if (config.channel_count == 2) {
+ config.channel_count = AUDPP_CMD_PCM_INTF_STEREO_V;
+ } else {
+ rc = -EINVAL;
+ MM_INFO("ERROR: config.channel_count == %d\n",
+ config.channel_count);
+ break;
+ }
+
+ if (config.bits == 8)
+ config.bits = AUDPP_CMD_WAV_PCM_WIDTH_8;
+ else if (config.bits == 16)
+ config.bits = AUDPP_CMD_WAV_PCM_WIDTH_16;
+ else if (config.bits == 24)
+ config.bits = AUDPP_CMD_WAV_PCM_WIDTH_24;
+ else {
+ rc = -EINVAL;
+ MM_INFO("ERROR: config.bits == %d\n", config.bits);
+ break;
+ }
+ audio->out_sample_rate = config.sample_rate;
+ audio->out_channel_mode = config.channel_count;
+ audio->out_bits = config.bits;
+ MM_DBG("AUDIO_SET_CONFIG: config.bits = %d\n", config.bits);
+ rc = 0;
+ break;
+ }
+
+ case AUDIO_GET_CONFIG:{
+ struct msm_audio_config config;
+ config.buffer_count = 2;
+ config.sample_rate = audio->out_sample_rate;
+ if (audio->out_channel_mode == AUDPP_CMD_PCM_INTF_MONO_V)
+ config.channel_count = 1;
+ else
+ config.channel_count = 2;
+ if (audio->out_bits == AUDPP_CMD_WAV_PCM_WIDTH_8)
+ config.bits = 8;
+ else if (audio->out_bits == AUDPP_CMD_WAV_PCM_WIDTH_24)
+ config.bits = 24;
+ else
+ config.bits = 16;
+ config.meta_field = 0;
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+ config.unused[2] = 0;
+ MM_DBG("AUDIO_GET_CONFIG: config.bits = %d\n", config.bits);
+ if (copy_to_user((void *) arg, &config, sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+
+ case AUDIO_PAUSE:
+ MM_DBG("AUDIO_PAUSE %ld\n", arg);
+ rc = audpp_pause(audio->dec_id, (int) arg);
+ if (arg == 1)
+ audio->drv_status |= ADRV_STATUS_PAUSE;
+ else if (arg == 0)
+ audio->drv_status &= ~ADRV_STATUS_PAUSE;
+ break;
+
+ case AUDIO_REGISTER_PMEM: {
+ struct msm_audio_pmem_info info;
+ MM_DBG("AUDIO_REGISTER_PMEM\n");
+ if (copy_from_user(&info, (void *) arg, sizeof(info)))
+ rc = -EFAULT;
+ else
+ rc = audlpa_pmem_add(audio, &info);
+ break;
+ }
+
+ case AUDIO_DEREGISTER_PMEM: {
+ struct msm_audio_pmem_info info;
+ MM_DBG("AUDIO_DEREGISTER_PMEM\n");
+ if (copy_from_user(&info, (void *) arg, sizeof(info)))
+ rc = -EFAULT;
+ else
+ rc = audlpa_pmem_remove(audio, &info);
+ break;
+ }
+ case AUDIO_ASYNC_WRITE:
+ if (audio->drv_status & ADRV_STATUS_FSYNC)
+ rc = -EBUSY;
+ else
+ rc = audlpa_aio_buf_add(audio, 1, (void __user *) arg);
+ break;
+
+ case AUDIO_GET_SESSION_ID:
+ if (copy_to_user((void *) arg, &audio->dec_id,
+ sizeof(unsigned short)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ default:
+ rc = audio->codec_ops.ioctl(file, cmd, arg);
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+/* Only useful in tunnel-mode */
+int audlpa_async_fsync(struct audio *audio)
+{
+ int rc = 0, empty = 0;
+ struct audlpa_buffer_node *buf_node;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+
+ /* Blocking client sends more data */
+ mutex_lock(&audio->lock);
+ audio->drv_status |= ADRV_STATUS_FSYNC;
+ mutex_unlock(&audio->lock);
+
+ mutex_lock(&audio->write_lock);
+ audio->teos = 0;
+ empty = list_empty(&audio->out_queue);
+ buf_node = kmalloc(sizeof(*buf_node), GFP_KERNEL);
+ if (!buf_node)
+ goto done;
+
+ buf_node->paddr = 0xFFFFFFFF;
+ buf_node->buf.data_len = 0;
+ buf_node->buf.buf_addr = NULL;
+ buf_node->buf.buf_len = 0;
+ buf_node->buf.private_data = NULL;
+ list_add_tail(&buf_node->list, &audio->out_queue);
+ if ((empty != 0) && (audio->out_needed == 1))
+ audlpa_async_send_data(audio, 0, 0);
+
+ rc = wait_event_interruptible(audio->write_wait,
+ audio->teos || audio->wflush ||
+ audio->stopped);
+
+ if (rc < 0)
+ goto done;
+
+ if (audio->teos == 1) {
+ /* Releasing all the pending buffers to user */
+ audio->teos = 0;
+ audlpa_async_flush(audio);
+ }
+
+ if (audio->stopped || audio->wflush)
+ rc = -EBUSY;
+
+done:
+ mutex_unlock(&audio->write_lock);
+ mutex_lock(&audio->lock);
+ audio->drv_status &= ~ADRV_STATUS_FSYNC;
+ mutex_unlock(&audio->lock);
+
+ return rc;
+}
+
+int audlpa_fsync(struct file *file, int datasync)
+{
+ struct audio *audio = file->private_data;
+
+ if (!audio->running)
+ return -EINVAL;
+
+ return audlpa_async_fsync(audio);
+}
+
+static void audlpa_reset_pmem_region(struct audio *audio)
+{
+ struct audlpa_pmem_region *region;
+ struct list_head *ptr, *next;
+
+ list_for_each_safe(ptr, next, &audio->pmem_region_queue) {
+ region = list_entry(ptr, struct audlpa_pmem_region, list);
+ list_del(®ion->list);
+ put_pmem_file(region->file);
+ kfree(region);
+ }
+
+ return;
+}
+
+static int audio_release(struct inode *inode, struct file *file)
+{
+ struct audio *audio = file->private_data;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+
+ MM_INFO("audio instance 0x%08x freeing\n", (int)audio);
+ mutex_lock(&audio->lock);
+ auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id);
+ audio_disable(audio);
+ audlpa_async_flush(audio);
+ audlpa_reset_pmem_region(audio);
+
+ msm_adsp_put(audio->audplay);
+ audpp_adec_free(audio->dec_id);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&audio->suspend_ctl.node);
+#endif
+ audio->opened = 0;
+ audio->event_abort = 1;
+ wake_up(&audio->event_wait);
+ audlpa_reset_event_queue(audio);
+ iounmap(audio->data);
+ pmem_kfree(audio->phys);
+ mutex_unlock(&audio->lock);
+#ifdef CONFIG_DEBUG_FS
+ if (audio->dentry)
+ debugfs_remove(audio->dentry);
+#endif
+ kfree(audio);
+ return 0;
+}
+
+static void audlpa_post_event(struct audio *audio, int type,
+ union msm_audio_event_payload payload)
+{
+ struct audlpa_event *e_node = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+
+ if (!list_empty(&audio->free_event_queue)) {
+ e_node = list_first_entry(&audio->free_event_queue,
+ struct audlpa_event, list);
+ list_del(&e_node->list);
+ } else {
+ e_node = kmalloc(sizeof(struct audlpa_event), GFP_ATOMIC);
+ if (!e_node) {
+ MM_ERR("No mem to post event %d\n", type);
+ return;
+ }
+ }
+
+ e_node->event_type = type;
+ e_node->payload = payload;
+
+ list_add_tail(&e_node->list, &audio->event_queue);
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+ wake_up(&audio->event_wait);
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void audlpa_suspend(struct early_suspend *h)
+{
+ struct audlpa_suspend_ctl *ctl =
+ container_of(h, struct audlpa_suspend_ctl, node);
+ union msm_audio_event_payload payload;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ audlpa_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload);
+}
+
+static void audlpa_resume(struct early_suspend *h)
+{
+ struct audlpa_suspend_ctl *ctl =
+ container_of(h, struct audlpa_suspend_ctl, node);
+ union msm_audio_event_payload payload;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ audlpa_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload);
+}
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t audlpa_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t audlpa_debug_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ const int debug_bufmax = 4096;
+ static char buffer[4096];
+ int n = 0;
+ struct audio *audio = file->private_data;
+
+ mutex_lock(&audio->lock);
+ n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "enabled %d\n", audio->enabled);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "stopped %d\n", audio->stopped);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "volume %x\n", audio->vol_pan.volume);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "sample rate %d\n",
+ audio->out_sample_rate);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "channel mode %d\n",
+ audio->out_channel_mode);
+ mutex_unlock(&audio->lock);
+ /* Following variables are only useful for debugging when
+ * when playback halts unexpectedly. Thus, no mutual exclusion
+ * enforced
+ */
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "wflush %d\n", audio->wflush);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "running %d\n", audio->running);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "dec state %d\n", audio->dec_state);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_needed %d\n", audio->out_needed);
+ buffer[n] = 0;
+ return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static const struct file_operations audlpa_debug_fops = {
+ .read = audlpa_debug_read,
+ .open = audlpa_debug_open,
+};
+#endif
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct audio *audio = NULL;
+ int rc, i, dec_attrb = 0, decid;
+ struct audlpa_event *e_node = NULL;
+#ifdef CONFIG_DEBUG_FS
+ /* 4 bytes represents decoder number, 1 byte for terminate string */
+ char name[sizeof "msm_lpa_" + 5];
+#endif
+
+ /* Allocate audio instance, set to zero */
+ audio = kzalloc(sizeof(struct audio), GFP_KERNEL);
+ if (!audio) {
+ MM_ERR("no memory to allocate audio instance\n");
+ rc = -ENOMEM;
+ goto done;
+ }
+ MM_INFO("audio instance 0x%08x created\n", (int)audio);
+
+ if ((file->f_mode & FMODE_WRITE) && !(file->f_mode & FMODE_READ)) {
+ dec_attrb |= MSM_AUD_MODE_TUNNEL;
+ } else {
+ kfree(audio);
+ rc = -EACCES;
+ goto done;
+ }
+
+ /* Allocate the decoder based on inode minor number*/
+ audio->minor_no = iminor(inode);
+ dec_attrb |= audlpa_decs[audio->minor_no].dec_attrb;
+ audio->codec_ops.ioctl = audlpa_decs[audio->minor_no].ioctl;
+ audio->codec_ops.adec_params = audlpa_decs[audio->minor_no].adec_params;
+
+ dec_attrb |= MSM_AUD_MODE_LP;
+
+ decid = audpp_adec_alloc(dec_attrb, &audio->module_name,
+ &audio->queue_id);
+ if (decid < 0) {
+ MM_ERR("No free decoder available\n");
+ rc = -ENODEV;
+ MM_INFO("audio instance 0x%08x freeing\n", (int)audio);
+ kfree(audio);
+ goto done;
+ }
+ audio->dec_id = decid & MSM_AUD_DECODER_MASK;
+
+ MM_DBG("set to aio interface\n");
+ audio->drv_status |= ADRV_STATUS_AIO_INTF;
+
+ rc = msm_adsp_get(audio->module_name, &audio->audplay,
+ &audplay_adsp_ops_lpa, audio);
+
+ if (rc) {
+ MM_ERR("failed to get %s module\n", audio->module_name);
+ goto err;
+ }
+
+ /* Initialize all locks of audio instance */
+ mutex_init(&audio->lock);
+ mutex_init(&audio->write_lock);
+ mutex_init(&audio->get_event_lock);
+ spin_lock_init(&audio->dsp_lock);
+ init_waitqueue_head(&audio->write_wait);
+ INIT_LIST_HEAD(&audio->out_queue);
+ INIT_LIST_HEAD(&audio->pmem_region_queue);
+ INIT_LIST_HEAD(&audio->free_event_queue);
+ INIT_LIST_HEAD(&audio->event_queue);
+ init_waitqueue_head(&audio->wait);
+ init_waitqueue_head(&audio->event_wait);
+ spin_lock_init(&audio->event_queue_lock);
+ init_waitqueue_head(&audio->avsync_wait);
+
+ audio->out_sample_rate = 44100;
+ audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V;
+ audio->out_bits = AUDPP_CMD_WAV_PCM_WIDTH_16;
+ audio->vol_pan.volume = 0x2000;
+
+ audlpa_async_flush(audio);
+
+ file->private_data = audio;
+ audio->opened = 1;
+
+ audio->device_events = AUDDEV_EVT_DEV_RDY
+ |AUDDEV_EVT_DEV_RLS | AUDDEV_EVT_REL_PENDING
+ |AUDDEV_EVT_STREAM_VOL_CHG;
+ audio->device_switch = DEVICE_SWITCH_STATE_NONE;
+ audio->drv_status &= ~ADRV_STATUS_PAUSE;
+ audio->bytecount_consumed = 0;
+ audio->bytecount_head = 0;
+ audio->bytecount_given = 0;
+
+ rc = auddev_register_evt_listner(audio->device_events,
+ AUDDEV_CLNT_DEC,
+ audio->dec_id,
+ lpa_listner,
+ (void *)audio);
+ if (rc) {
+ MM_ERR("%s: failed to register listnet\n", __func__);
+ goto event_err;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ snprintf(name, sizeof name, "msm_lpa_%04x", audio->dec_id);
+ audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *) audio, &audlpa_debug_fops);
+
+ if (IS_ERR(audio->dentry))
+ MM_DBG("debugfs_create_file failed\n");
+#endif
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+ audio->suspend_ctl.node.resume = audlpa_resume;
+ audio->suspend_ctl.node.suspend = audlpa_suspend;
+ audio->suspend_ctl.audio = audio;
+ register_early_suspend(&audio->suspend_ctl.node);
+#endif
+ for (i = 0; i < AUDLPA_EVENT_NUM; i++) {
+ e_node = kmalloc(sizeof(struct audlpa_event), GFP_KERNEL);
+ if (e_node)
+ list_add_tail(&e_node->list, &audio->free_event_queue);
+ else {
+ MM_ERR("event pkt alloc failed\n");
+ break;
+ }
+ }
+done:
+ return rc;
+event_err:
+ msm_adsp_put(audio->audplay);
+err:
+ iounmap(audio->data);
+ pmem_kfree(audio->phys);
+ audpp_adec_free(audio->dec_id);
+ MM_INFO("audio instance 0x%08x freeing\n", (int)audio);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_lpa_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_release,
+ .unlocked_ioctl = audio_ioctl,
+ .fsync = audlpa_fsync,
+};
+
+static dev_t audlpa_devno;
+static struct class *audlpa_class;
+struct audlpa_device {
+ const char *name;
+ struct device *device;
+ struct cdev cdev;
+};
+
+static struct audlpa_device *audlpa_devices;
+
+static void audlpa_create(struct audlpa_device *adev, const char *name,
+ struct device *parent, dev_t devt)
+{
+ struct device *dev;
+ int rc;
+
+ dev = device_create(audlpa_class, parent, devt, "%s", name);
+ if (IS_ERR(dev))
+ return;
+
+ cdev_init(&adev->cdev, &audio_lpa_fops);
+ adev->cdev.owner = THIS_MODULE;
+
+ rc = cdev_add(&adev->cdev, devt, 1);
+ if (rc < 0) {
+ device_destroy(audlpa_class, devt);
+ } else {
+ adev->device = dev;
+ adev->name = name;
+ }
+}
+
+static int __init audio_init(void)
+{
+ int rc;
+ int n = ARRAY_SIZE(audlpa_decs);
+
+ audlpa_devices = kzalloc(sizeof(struct audlpa_device) * n, GFP_KERNEL);
+ if (!audlpa_devices)
+ return -ENOMEM;
+
+ audlpa_class = class_create(THIS_MODULE, "audlpa");
+ if (IS_ERR(audlpa_class))
+ goto fail_create_class;
+
+ rc = alloc_chrdev_region(&audlpa_devno, 0, n, "msm_audio_lpa");
+ if (rc < 0)
+ goto fail_alloc_region;
+
+ for (n = 0; n < ARRAY_SIZE(audlpa_decs); n++) {
+ audlpa_create(audlpa_devices + n,
+ audlpa_decs[n].name, NULL,
+ MKDEV(MAJOR(audlpa_devno), n));
+ }
+
+ return 0;
+
+fail_alloc_region:
+ class_unregister(audlpa_class);
+ return rc;
+fail_create_class:
+ kfree(audlpa_devices);
+ return -ENOMEM;
+}
+
+static void __exit audio_exit(void)
+{
+ class_unregister(audlpa_class);
+ kfree(audlpa_devices);
+}
+
+module_init(audio_init);
+module_exit(audio_exit);
+
+MODULE_DESCRIPTION("MSM LPA driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_mp3.c b/arch/arm/mach-msm/qdsp5v2/audio_mp3.c
new file mode 100644
index 0000000..7c0cde8
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_mp3.c
@@ -0,0 +1,2505 @@
+/* mp3 audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/earlysuspend.h>
+#include <linux/list.h>
+#include <linux/android_pmem.h>
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+
+#include <linux/msm_audio.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/qdsp5audplaycmdi.h>
+#include <mach/qdsp5v2/qdsp5audplaymsg.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/qdsp5v2/audpp.h>
+#include <mach/debug_mm.h>
+#include <linux/slab.h>
+#define ADRV_STATUS_AIO_INTF 0x00000001
+#define ADRV_STATUS_OBUF_GIVEN 0x00000002
+#define ADRV_STATUS_IBUF_GIVEN 0x00000004
+#define ADRV_STATUS_FSYNC 0x00000008
+
+/* Size must be power of 2 */
+#define BUFSZ_MAX 32768
+#define BUFSZ_MIN 4096
+#define DMASZ_MAX (BUFSZ_MAX * 2)
+#define DMASZ_MIN (BUFSZ_MIN * 2)
+
+#define AUDPLAY_INVALID_READ_PTR_OFFSET 0xFFFF
+#define AUDDEC_DEC_MP3 2
+
+#define PCM_BUFSZ_MIN 4800 /* Hold one stereo MP3 frame */
+#define PCM_BUF_MAX_COUNT 5 /* DSP only accepts 5 buffers at most
+ but support 2 buffers currently */
+#define ROUTING_MODE_FTRT 1
+#define ROUTING_MODE_RT 2
+/* Decoder status received from AUDPPTASK */
+#define AUDPP_DEC_STATUS_SLEEP 0
+#define AUDPP_DEC_STATUS_INIT 1
+#define AUDPP_DEC_STATUS_CFG 2
+#define AUDPP_DEC_STATUS_PLAY 3
+
+#define AUDMP3_METAFIELD_MASK 0xFFFF0000
+#define AUDMP3_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */
+#define AUDMP3_EOS_FLG_MASK 0x01
+#define AUDMP3_EOS_NONE 0x0 /* No EOS detected */
+#define AUDMP3_EOS_SET 0x1 /* EOS set in meta field */
+
+#define AUDMP3_EVENT_NUM 10 /* Default number of pre-allocated event packets */
+
+#define BITSTREAM_ERROR_THRESHOLD_VALUE 0x1 /* DEFAULT THRESHOLD VALUE */
+
+#define __CONTAINS(r, v, l) ({ \
+ typeof(r) __r = r; \
+ typeof(v) __v = v; \
+ typeof(v) __e = __v + l; \
+ int res = ((__v >= __r->vaddr) && \
+ (__e <= __r->vaddr + __r->len)); \
+ res; \
+})
+
+#define CONTAINS(r1, r2) ({ \
+ typeof(r2) __r2 = r2; \
+ __CONTAINS(r1, __r2->vaddr, __r2->len); \
+})
+
+#define IN_RANGE(r, v) ({ \
+ typeof(r) __r = r; \
+ typeof(v) __vv = v; \
+ int res = ((__vv >= __r->vaddr) && \
+ (__vv < (__r->vaddr + __r->len))); \
+ res; \
+})
+
+#define OVERLAPS(r1, r2) ({ \
+ typeof(r1) __r1 = r1; \
+ typeof(r2) __r2 = r2; \
+ typeof(__r2->vaddr) __v = __r2->vaddr; \
+ typeof(__v) __e = __v + __r2->len - 1; \
+ int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \
+ res; \
+})
+
+struct buffer {
+ void *data;
+ unsigned size;
+ unsigned used; /* Input usage actual DSP produced PCM size */
+ unsigned addr;
+ unsigned short mfield_sz; /*only useful for data has meta field */
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+struct audmp3_suspend_ctl {
+ struct early_suspend node;
+ struct audio *audio;
+};
+#endif
+
+struct audmp3_event {
+ struct list_head list;
+ int event_type;
+ union msm_audio_event_payload payload;
+};
+
+struct audmp3_pmem_region {
+ struct list_head list;
+ struct file *file;
+ int fd;
+ void *vaddr;
+ unsigned long paddr;
+ unsigned long kvaddr;
+ unsigned long len;
+ unsigned ref_cnt;
+};
+
+struct audmp3_buffer_node {
+ struct list_head list;
+ struct msm_audio_aio_buf buf;
+ unsigned long paddr;
+};
+
+struct audmp3_drv_operations {
+ void (*pcm_buf_update)(struct audio *, uint32_t *);
+ void (*buffer_refresh)(struct audio *);
+ void (*send_data)(struct audio *, unsigned);
+ void (*out_flush)(struct audio *);
+ void (*in_flush)(struct audio *);
+ int (*fsync)(struct audio *);
+};
+
+struct audio {
+ struct buffer out[2];
+
+ spinlock_t dsp_lock;
+
+ uint8_t out_head;
+ uint8_t out_tail;
+ uint8_t out_needed; /* number of buffers the dsp is waiting for */
+ unsigned out_dma_sz;
+ struct list_head out_queue; /* queue to retain output buffers */
+ atomic_t out_bytes;
+
+ struct mutex lock;
+ struct mutex write_lock;
+ wait_queue_head_t write_wait;
+
+ /* Host PCM section */
+ struct buffer in[PCM_BUF_MAX_COUNT];
+ struct mutex read_lock;
+ wait_queue_head_t read_wait; /* Wait queue for read */
+ char *read_data; /* pointer to reader buffer */
+ int32_t read_phys; /* physical address of reader buffer */
+ uint8_t read_next; /* index to input buffers to be read next */
+ uint8_t fill_next; /* index to buffer that DSP should be filling */
+ uint8_t pcm_buf_count; /* number of pcm buffer allocated */
+ struct list_head in_queue; /* queue to retain input buffers */
+ /* ---- End of Host PCM section */
+
+ struct msm_adsp_module *audplay;
+
+ /* configuration to use on next enable */
+ uint32_t out_sample_rate;
+ uint32_t out_channel_mode;
+
+ /* data allocated for various buffers */
+ char *data;
+ int32_t phys; /* physical address of write buffer */
+
+ uint32_t drv_status;
+ int mfield; /* meta field embedded in data */
+ int rflush; /* Read flush */
+ int wflush; /* Write flush */
+ int opened;
+ int enabled;
+ int running;
+ int stopped; /* set when stopped, cleared on flush */
+ int pcm_feedback;
+ int buf_refresh;
+ int teos; /* valid only if tunnel mode & no data left for decoder */
+ enum msm_aud_decoder_state dec_state; /* Represents decoder state */
+ int reserved; /* A byte is being reserved */
+ char rsv_byte; /* Handle odd length user data */
+
+ const char *module_name;
+ unsigned queue_id;
+ uint16_t dec_id;
+ uint32_t read_ptr_offset;
+ int16_t source;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct audmp3_suspend_ctl suspend_ctl;
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dentry;
+#endif
+
+ wait_queue_head_t wait;
+ struct list_head free_event_queue;
+ struct list_head event_queue;
+ wait_queue_head_t event_wait;
+ spinlock_t event_queue_lock;
+ struct mutex get_event_lock;
+ int event_abort;
+ /* AV sync Info */
+ int avsync_flag; /* Flag to indicate feedback from DSP */
+ wait_queue_head_t avsync_wait;/* Wait queue for AV Sync Message */
+ /* flags, 48 bits sample/bytes counter per channel */
+ uint16_t avsync[AUDPP_AVSYNC_CH_COUNT * AUDPP_AVSYNC_NUM_WORDS + 1];
+
+ uint32_t device_events;
+
+ struct list_head pmem_region_queue; /* protected by lock */
+ struct audmp3_drv_operations drv_ops;
+
+ struct msm_audio_bitstream_info stream_info;
+ struct msm_audio_bitstream_error_info bitstream_error_info;
+ uint32_t bitstream_error_threshold_value;
+
+ int eq_enable;
+ int eq_needs_commit;
+ struct audpp_cmd_cfg_object_params_eqalizer eq;
+ struct audpp_cmd_cfg_object_params_volume vol_pan;
+};
+
+static int auddec_dsp_config(struct audio *audio, int enable);
+static void audpp_cmd_cfg_adec_params(struct audio *audio);
+static void audpp_cmd_cfg_routing_mode(struct audio *audio);
+static void audplay_send_data(struct audio *audio, unsigned needed);
+static void audplay_error_threshold_config(struct audio *audio);
+static void audplay_config_hostpcm(struct audio *audio);
+static void audplay_buffer_refresh(struct audio *audio);
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg);
+static void audmp3_post_event(struct audio *audio, int type,
+ union msm_audio_event_payload payload);
+static unsigned long audmp3_pmem_fixup(struct audio *audio, void *addr,
+ unsigned long len, int ref_up);
+
+static void mp3_listner(u32 evt_id, union auddev_evt_data *evt_payload,
+ void *private_data)
+{
+ struct audio *audio = (struct audio *) private_data;
+ switch (evt_id) {
+ case AUDDEV_EVT_DEV_RDY:
+ MM_DBG(":AUDDEV_EVT_DEV_RDY\n");
+ audio->source |= (0x1 << evt_payload->routing_id);
+ if (audio->running == 1 && audio->enabled == 1)
+ audpp_route_stream(audio->dec_id, audio->source);
+
+ break;
+ case AUDDEV_EVT_DEV_RLS:
+ MM_DBG(":AUDDEV_EVT_DEV_RLS\n");
+ audio->source &= ~(0x1 << evt_payload->routing_id);
+ if (audio->running == 1 && audio->enabled == 1)
+ audpp_route_stream(audio->dec_id, audio->source);
+ break;
+ case AUDDEV_EVT_STREAM_VOL_CHG:
+ audio->vol_pan.volume = evt_payload->session_vol;
+ MM_DBG(":AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n",
+ audio->vol_pan.volume);
+ if (audio->running)
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ break;
+ default:
+ MM_ERR(":ERROR:wrong event\n");
+ break;
+ }
+}
+/* must be called with audio->lock held */
+static int audio_enable(struct audio *audio)
+{
+ MM_DBG("\n"); /* Macro prints the file name and function */
+
+ if (audio->enabled)
+ return 0;
+
+ audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+ audio->out_tail = 0;
+ audio->out_needed = 0;
+
+ if (msm_adsp_enable(audio->audplay)) {
+ MM_ERR("msm_adsp_enable(audplay) failed\n");
+ return -ENODEV;
+ }
+
+ if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) {
+ MM_ERR("audpp_enable() failed\n");
+ msm_adsp_disable(audio->audplay);
+ return -ENODEV;
+ }
+
+ audio->enabled = 1;
+ return 0;
+}
+
+/* must be called with audio->lock held */
+static int audio_disable(struct audio *audio)
+{
+ int rc = 0;
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ if (audio->enabled) {
+ audio->enabled = 0;
+ audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+ auddec_dsp_config(audio, 0);
+ rc = wait_event_interruptible_timeout(audio->wait,
+ audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+ msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE)
+ rc = -EFAULT;
+ else
+ rc = 0;
+ wake_up(&audio->write_wait);
+ wake_up(&audio->read_wait);
+ msm_adsp_disable(audio->audplay);
+ audpp_disable(audio->dec_id, audio);
+ audio->out_needed = 0;
+ }
+ return rc;
+}
+
+/* ------------------- dsp --------------------- */
+static void audmp3_async_pcm_buf_update(struct audio *audio, uint32_t *payload)
+{
+ unsigned long flags;
+ union msm_audio_event_payload event_payload;
+ struct audmp3_buffer_node *filled_buf;
+ uint8_t index;
+
+ if (audio->rflush)
+ return;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ for (index = 0; index < payload[1]; index++) {
+ BUG_ON(list_empty(&audio->in_queue));
+ filled_buf = list_first_entry(&audio->in_queue,
+ struct audmp3_buffer_node, list);
+ if (filled_buf->paddr == payload[2 + index * 2]) {
+ list_del(&filled_buf->list);
+ event_payload.aio_buf = filled_buf->buf;
+ event_payload.aio_buf.data_len =
+ payload[3 + index * 2];
+ MM_DBG("pcm buf %p data_len %d\n", filled_buf,
+ event_payload.aio_buf.data_len);
+ audmp3_post_event(audio, AUDIO_EVENT_READ_DONE,
+ event_payload);
+ kfree(filled_buf);
+ } else {
+ MM_ERR("expected=%lx ret=%x\n", filled_buf->paddr,
+ payload[2 + index * 2]);
+ break;
+ }
+ }
+
+ audio->drv_status &= ~ADRV_STATUS_IBUF_GIVEN;
+ audio->drv_ops.buffer_refresh(audio);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+
+}
+
+static void audio_update_pcm_buf_entry(struct audio *audio, uint32_t *payload)
+{
+ uint8_t index;
+ unsigned long flags;
+
+ if (audio->rflush)
+ return;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ for (index = 0; index < payload[1]; index++) {
+ if (audio->in[audio->fill_next].addr ==
+ payload[2 + index * 2]) {
+ MM_DBG("in[%d] ready\n", audio->fill_next);
+ audio->in[audio->fill_next].used =
+ payload[3 + index * 2];
+ if ((++audio->fill_next) == audio->pcm_buf_count)
+ audio->fill_next = 0;
+
+ } else {
+ MM_ERR("expected=%x ret=%x\n",
+ audio->in[audio->fill_next].addr,
+ payload[2 + index * 2]);
+ break;
+ }
+ }
+ if (audio->in[audio->fill_next].used == 0) {
+ audio->drv_ops.buffer_refresh(audio);
+ } else {
+ MM_DBG("read cannot keep up\n");
+ audio->buf_refresh = 1;
+ }
+ wake_up(&audio->read_wait);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+
+}
+
+static void audmp3_bitstream_error_info(struct audio *audio, uint32_t *payload)
+{
+ unsigned long flags;
+ union msm_audio_event_payload e_payload;
+
+ if (payload[0] != AUDDEC_DEC_MP3) {
+ MM_ERR("Unexpected bitstream error info from DSP:\
+ Invalid decoder\n");
+ return;
+ }
+
+ /* get stream info from DSP msg */
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+
+ audio->bitstream_error_info.dec_id = payload[0];
+ audio->bitstream_error_info.err_msg_indicator = payload[1];
+ audio->bitstream_error_info.err_type = payload[2];
+
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ MM_ERR("bit_stream_error_type=%d error_count=%d\n",
+ audio->bitstream_error_info.err_type, (0x0000FFFF &
+ audio->bitstream_error_info.err_msg_indicator));
+
+ /* send event to ARM to notify error info coming */
+ e_payload.error_info = audio->bitstream_error_info;
+ audmp3_post_event(audio, AUDIO_EVENT_BITSTREAM_ERROR_INFO, e_payload);
+}
+
+static void audmp3_update_stream_info(struct audio *audio, uint32_t *payload)
+{
+ unsigned long flags;
+ union msm_audio_event_payload e_payload;
+
+ /* get stream info from DSP msg */
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+
+ audio->stream_info.codec_type = AUDIO_CODEC_TYPE_MP3;
+ audio->stream_info.chan_info = (0x0000FFFF & payload[1]);
+ audio->stream_info.sample_rate = (0x0000FFFF & payload[2]);
+ audio->stream_info.bit_stream_info = (0x0000FFFF & payload[3]);
+ audio->stream_info.bit_rate = payload[4];
+
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ MM_DBG("chan_info=%d, sample_rate=%d, bit_stream_info=%d\n",
+ audio->stream_info.chan_info,
+ audio->stream_info.sample_rate,
+ audio->stream_info.bit_stream_info);
+
+ /* send event to ARM to notify steam info coming */
+ e_payload.stream_info = audio->stream_info;
+ audmp3_post_event(audio, AUDIO_EVENT_STREAM_INFO, e_payload);
+}
+
+static void audplay_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent) (void *ptr, size_t len))
+{
+ struct audio *audio = data;
+ uint32_t msg[28];
+ getevent(msg, sizeof(msg));
+
+ MM_DBG("msg_id=%x\n", id);
+
+ switch (id) {
+ case AUDPLAY_MSG_DEC_NEEDS_DATA:
+ audio->drv_ops.send_data(audio, 1);
+ break;
+
+ case AUDPLAY_MSG_BUFFER_UPDATE:
+ audio->drv_ops.pcm_buf_update(audio, msg);
+ break;
+
+ case AUDPLAY_UP_STREAM_INFO:
+ if ((msg[1] & AUDPLAY_STREAM_INFO_MSG_MASK) ==
+ AUDPLAY_STREAM_INFO_MSG_MASK) {
+ audmp3_bitstream_error_info(audio, msg);
+ } else {
+ audmp3_update_stream_info(audio, msg);
+ }
+ break;
+
+ case AUDPLAY_UP_OUTPORT_FLUSH_ACK:
+ MM_DBG("OUTPORT_FLUSH_ACK\n");
+ audio->rflush = 0;
+ wake_up(&audio->read_wait);
+ if (audio->pcm_feedback)
+ audio->drv_ops.buffer_refresh(audio);
+ break;
+
+ case ADSP_MESSAGE_ID:
+ MM_DBG("Received ADSP event: module enable(audplaytask)\n");
+ break;
+
+ default:
+ MM_ERR("unexpected message from decoder \n");
+ break;
+ }
+}
+
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg)
+{
+ struct audio *audio = private;
+
+ switch (id) {
+ case AUDPP_MSG_STATUS_MSG:{
+ unsigned status = msg[1];
+
+ switch (status) {
+ case AUDPP_DEC_STATUS_SLEEP: {
+ uint16_t reason = msg[2];
+ MM_DBG("decoder status: sleep reason=0x%04x\n",
+ reason);
+ if ((reason == AUDPP_MSG_REASON_MEM)
+ || (reason ==
+ AUDPP_MSG_REASON_NODECODER)) {
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_FAILURE;
+ wake_up(&audio->wait);
+ } else if (reason == AUDPP_MSG_REASON_NONE) {
+ /* decoder is in disable state */
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_CLOSE;
+ wake_up(&audio->wait);
+ }
+ break;
+ }
+ case AUDPP_DEC_STATUS_INIT:
+ MM_DBG("decoder status: init \n");
+ if (audio->pcm_feedback)
+ audpp_cmd_cfg_routing_mode(audio);
+ else
+ audpp_cmd_cfg_adec_params(audio);
+ break;
+
+ case AUDPP_DEC_STATUS_CFG:
+ MM_DBG("decoder status: cfg \n");
+ break;
+ case AUDPP_DEC_STATUS_PLAY:
+ MM_DBG("decoder status: play \n");
+ /* send mixer command */
+ audpp_route_stream(audio->dec_id,
+ audio->source);
+ if (audio->pcm_feedback) {
+ audplay_error_threshold_config(audio);
+ audplay_config_hostpcm(audio);
+ audio->drv_ops.buffer_refresh(audio);
+ }
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_SUCCESS;
+ wake_up(&audio->wait);
+ break;
+ default:
+ MM_ERR("unknown decoder status \n");
+ break;
+ }
+ break;
+ }
+ case AUDPP_MSG_CFG_MSG:
+ if (msg[0] == AUDPP_MSG_ENA_ENA) {
+ MM_DBG("CFG_MSG ENABLE\n");
+ auddec_dsp_config(audio, 1);
+ audio->out_needed = 0;
+ audio->running = 1;
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ audpp_dsp_set_eq(audio->dec_id, audio->eq_enable,
+ &audio->eq, POPP);
+ } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+ MM_DBG("CFG_MSG DISABLE\n");
+ audio->running = 0;
+ } else {
+ MM_DBG("CFG_MSG %d?\n", msg[0]);
+ }
+ break;
+ case AUDPP_MSG_ROUTING_ACK:
+ MM_DBG("ROUTING_ACK mode=%d\n", msg[1]);
+ audpp_cmd_cfg_adec_params(audio);
+ break;
+
+ case AUDPP_MSG_FLUSH_ACK:
+ MM_DBG("FLUSH_ACK\n");
+ audio->wflush = 0;
+ audio->rflush = 0;
+ wake_up(&audio->write_wait);
+ if (audio->pcm_feedback)
+ audio->drv_ops.buffer_refresh(audio);
+ break;
+
+ case AUDPP_MSG_PCMDMAMISSED:
+ MM_DBG("PCMDMAMISSED\n");
+ audio->teos = 1;
+ wake_up(&audio->write_wait);
+ break;
+
+ case AUDPP_MSG_AVSYNC_MSG:
+ MM_DBG("AUDPP_MSG_AVSYNC_MSG\n");
+ memcpy(&audio->avsync[0], msg, sizeof(audio->avsync));
+ audio->avsync_flag = 1;
+ wake_up(&audio->avsync_wait);
+ break;
+
+ default:
+ MM_ERR("UNKNOWN (%d)\n", id);
+ }
+
+}
+
+
+struct msm_adsp_ops audplay_adsp_ops = {
+ .event = audplay_dsp_event,
+};
+
+
+#define audplay_send_queue0(audio, cmd, len) \
+ msm_adsp_write(audio->audplay, audio->queue_id, \
+ cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+ struct audpp_cmd_cfg_dec_type cfg_dec_cmd;
+
+ memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd));
+
+ cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+ if (enable)
+ cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_MP3;
+ else
+ cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_DIS_DEC_V;
+ cfg_dec_cmd.dm_mode = 0x0;
+ cfg_dec_cmd.stream_id = audio->dec_id;
+ return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd));
+}
+
+static void audpp_cmd_cfg_adec_params(struct audio *audio)
+{
+ struct audpp_cmd_cfg_adec_params_mp3 cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+ cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_MP3_LEN;
+ cmd.common.dec_id = audio->dec_id;
+ cmd.common.input_sampling_frequency = audio->out_sample_rate;
+
+ audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_routing_mode(struct audio *audio)
+{
+ struct audpp_cmd_routing_mode cmd;
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_ROUTING_MODE;
+ cmd.object_number = audio->dec_id;
+ if (audio->pcm_feedback)
+ cmd.routing_mode = ROUTING_MODE_FTRT;
+ else
+ cmd.routing_mode = ROUTING_MODE_RT;
+
+ audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static int audplay_dsp_send_data_avail(struct audio *audio,
+ unsigned idx, unsigned len)
+{
+ struct audplay_cmd_bitstream_data_avail_nt2 cmd;
+
+ cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2;
+ if (audio->mfield)
+ cmd.decoder_id = AUDMP3_METAFIELD_MASK |
+ (audio->out[idx].mfield_sz >> 1);
+ else
+ cmd.decoder_id = audio->dec_id;
+ cmd.buf_ptr = audio->out[idx].addr;
+ cmd.buf_size = len/2;
+ cmd.partition_number = 0;
+ return audplay_send_queue0(audio, &cmd, sizeof(cmd));
+}
+/* Caller holds irq_lock */
+static void audmp3_async_buffer_refresh(struct audio *audio)
+{
+ struct audplay_cmd_buffer_refresh refresh_cmd;
+ struct audmp3_buffer_node *next_buf;
+
+ if (!audio->running ||
+ audio->drv_status & ADRV_STATUS_IBUF_GIVEN)
+ return;
+
+ if (!list_empty(&audio->in_queue)) {
+ next_buf = list_first_entry(&audio->in_queue,
+ struct audmp3_buffer_node, list);
+ if (!next_buf)
+ return;
+ MM_DBG("next buf %p phy %lx len %d\n", next_buf,
+ next_buf->paddr, next_buf->buf.buf_len);
+ refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH;
+ refresh_cmd.num_buffers = 1;
+ refresh_cmd.buf0_address = next_buf->paddr;
+ refresh_cmd.buf0_length = next_buf->buf.buf_len -
+ (next_buf->buf.buf_len % 576) +
+ (audio->mfield ? 24 : 0); /* Mp3 frame size */
+ refresh_cmd.buf_read_count = 0;
+ audio->drv_status |= ADRV_STATUS_IBUF_GIVEN;
+ (void) audplay_send_queue0(audio, &refresh_cmd,
+ sizeof(refresh_cmd));
+ }
+
+}
+
+static void audplay_buffer_refresh(struct audio *audio)
+{
+ struct audplay_cmd_buffer_refresh refresh_cmd;
+
+ refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH;
+ refresh_cmd.num_buffers = 1;
+ refresh_cmd.buf0_address = audio->in[audio->fill_next].addr;
+ refresh_cmd.buf0_length = audio->in[audio->fill_next].size -
+ (audio->in[audio->fill_next].size % 576) +
+ (audio->mfield ? 24 : 0); /* Mp3 frame size */
+ refresh_cmd.buf_read_count = 0;
+ MM_DBG("buf0_addr=%x buf0_len=%d\n", refresh_cmd.buf0_address,
+ refresh_cmd.buf0_length);
+ (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd));
+}
+
+static void audplay_error_threshold_config(struct audio *audio)
+{
+ union audplay_cmd_channel_info ch_cfg_cmd;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ ch_cfg_cmd.thr_update.cmd_id = AUDPLAY_CMD_CHANNEL_INFO;
+ ch_cfg_cmd.thr_update.threshold_update = AUDPLAY_ERROR_THRESHOLD_ENABLE;
+ ch_cfg_cmd.thr_update.threshold_value =
+ audio->bitstream_error_threshold_value;
+ (void)audplay_send_queue0(audio, &ch_cfg_cmd, sizeof(ch_cfg_cmd));
+}
+
+static void audplay_config_hostpcm(struct audio *audio)
+{
+ struct audplay_cmd_hpcm_buf_cfg cfg_cmd;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG;
+ cfg_cmd.max_buffers = 1;
+ cfg_cmd.byte_swap = 0;
+ cfg_cmd.hostpcm_config = (0x8000) | (0x4000);
+ cfg_cmd.feedback_frequency = 1;
+ cfg_cmd.partition_number = 0;
+ (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd));
+
+}
+
+static void audplay_outport_flush(struct audio *audio)
+{
+ struct audplay_cmd_outport_flush op_flush_cmd;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ op_flush_cmd.cmd_id = AUDPLAY_CMD_OUTPORT_FLUSH;
+ (void)audplay_send_queue0(audio, &op_flush_cmd, sizeof(op_flush_cmd));
+}
+
+static void audmp3_async_send_data(struct audio *audio, unsigned needed)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (!audio->running)
+ goto done;
+
+ if (needed && !audio->wflush) {
+ audio->out_needed = 1;
+ if (audio->drv_status & ADRV_STATUS_OBUF_GIVEN) {
+ /* pop one node out of queue */
+ union msm_audio_event_payload payload;
+ struct audmp3_buffer_node *used_buf;
+
+ MM_DBG("consumed\n");
+ BUG_ON(list_empty(&audio->out_queue));
+ used_buf = list_first_entry(&audio->out_queue,
+ struct audmp3_buffer_node, list);
+ list_del(&used_buf->list);
+ payload.aio_buf = used_buf->buf;
+ audmp3_post_event(audio, AUDIO_EVENT_WRITE_DONE,
+ payload);
+ kfree(used_buf);
+ audio->drv_status &= ~ADRV_STATUS_OBUF_GIVEN;
+ }
+
+ }
+
+ if (audio->out_needed) {
+ struct audmp3_buffer_node *next_buf;
+ struct audplay_cmd_bitstream_data_avail_nt2 cmd;
+ if (!list_empty(&audio->out_queue)) {
+ next_buf = list_first_entry(&audio->out_queue,
+ struct audmp3_buffer_node, list);
+ MM_DBG("next_buf %p\n", next_buf);
+ if (next_buf) {
+ MM_DBG("next buf phy %lx len %d\n",
+ next_buf->paddr,
+ next_buf->buf.data_len);
+
+ cmd.cmd_id =
+ AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2;
+ if (audio->mfield)
+ cmd.decoder_id = AUDMP3_METAFIELD_MASK |
+ (next_buf->buf.mfield_sz >> 1);
+ else
+ cmd.decoder_id = audio->dec_id;
+ cmd.buf_ptr = (unsigned) next_buf->paddr;
+ cmd.buf_size = next_buf->buf.data_len >> 1;
+ cmd.partition_number = 0;
+ audplay_send_queue0(audio, &cmd, sizeof(cmd));
+ audio->out_needed = 0;
+ audio->drv_status |= ADRV_STATUS_OBUF_GIVEN;
+ }
+ }
+ }
+
+done:
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+static void audplay_send_data(struct audio *audio, unsigned needed)
+{
+ struct buffer *frame;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (!audio->running)
+ goto done;
+
+ if (needed && !audio->wflush) {
+ /* We were called from the callback because the DSP
+ * requested more data. Note that the DSP does want
+ * more data, and if a buffer was in-flight, mark it
+ * as available (since the DSP must now be done with
+ * it).
+ */
+ audio->out_needed = 1;
+ frame = audio->out + audio->out_tail;
+ if (frame->used == 0xffffffff) {
+ MM_DBG("frame %d free\n", audio->out_tail);
+ frame->used = 0;
+ audio->out_tail ^= 1;
+ wake_up(&audio->write_wait);
+ }
+ }
+
+ if (audio->out_needed) {
+ /* If the DSP currently wants data and we have a
+ * buffer available, we will send it and reset
+ * the needed flag. We'll mark the buffer as in-flight
+ * so that it won't be recycled until the next buffer
+ * is requested
+ */
+
+ frame = audio->out + audio->out_tail;
+ if (frame->used) {
+ BUG_ON(frame->used == 0xffffffff);
+ MM_DBG("frame %d busy\n", audio->out_tail);
+ audplay_dsp_send_data_avail(audio, audio->out_tail,
+ frame->used);
+ frame->used = 0xffffffff;
+ audio->out_needed = 0;
+ }
+ }
+done:
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+/* ------------------- device --------------------- */
+static void audmp3_async_flush(struct audio *audio)
+{
+ struct audmp3_buffer_node *buf_node;
+ struct list_head *ptr, *next;
+ union msm_audio_event_payload payload;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ list_for_each_safe(ptr, next, &audio->out_queue) {
+ buf_node = list_entry(ptr, struct audmp3_buffer_node, list);
+ list_del(&buf_node->list);
+ payload.aio_buf = buf_node->buf;
+ audmp3_post_event(audio, AUDIO_EVENT_WRITE_DONE,
+ payload);
+ kfree(buf_node);
+ }
+ audio->drv_status &= ~ADRV_STATUS_OBUF_GIVEN;
+ audio->out_needed = 0;
+ atomic_set(&audio->out_bytes, 0);
+}
+
+static void audio_flush(struct audio *audio)
+{
+ audio->out[0].used = 0;
+ audio->out[1].used = 0;
+ audio->out_head = 0;
+ audio->out_tail = 0;
+ audio->reserved = 0;
+ audio->out_needed = 0;
+ atomic_set(&audio->out_bytes, 0);
+}
+
+static void audmp3_async_flush_pcm_buf(struct audio *audio)
+{
+ struct audmp3_buffer_node *buf_node;
+ struct list_head *ptr, *next;
+ union msm_audio_event_payload payload;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ list_for_each_safe(ptr, next, &audio->in_queue) {
+ buf_node = list_entry(ptr, struct audmp3_buffer_node, list);
+ list_del(&buf_node->list);
+ payload.aio_buf = buf_node->buf;
+ payload.aio_buf.data_len = 0;
+ audmp3_post_event(audio, AUDIO_EVENT_READ_DONE,
+ payload);
+ kfree(buf_node);
+ }
+ audio->drv_status &= ~ADRV_STATUS_IBUF_GIVEN;
+
+}
+
+static void audio_flush_pcm_buf(struct audio *audio)
+{
+ uint8_t index;
+
+ for (index = 0; index < PCM_BUF_MAX_COUNT; index++)
+ audio->in[index].used = 0;
+
+ audio->buf_refresh = 0;
+ audio->read_next = 0;
+ audio->fill_next = 0;
+}
+
+static void audio_ioport_reset(struct audio *audio)
+{
+ if (audio->drv_status & ADRV_STATUS_AIO_INTF) {
+ /* If fsync is in progress, make sure
+ * return value of fsync indicates
+ * abort due to flush
+ */
+ if (audio->drv_status & ADRV_STATUS_FSYNC) {
+ MM_DBG("fsync in progress\n");
+ wake_up(&audio->write_wait);
+ mutex_lock(&audio->write_lock);
+ audio->drv_ops.out_flush(audio);
+ mutex_unlock(&audio->write_lock);
+ } else
+ audio->drv_ops.out_flush(audio);
+ audio->drv_ops.in_flush(audio);
+ } else {
+ /* Make sure read/write thread are free from
+ * sleep and knowing that system is not able
+ * to process io request at the moment
+ */
+ wake_up(&audio->write_wait);
+ mutex_lock(&audio->write_lock);
+ audio->drv_ops.out_flush(audio);
+ mutex_unlock(&audio->write_lock);
+ wake_up(&audio->read_wait);
+ mutex_lock(&audio->read_lock);
+ audio->drv_ops.in_flush(audio);
+ mutex_unlock(&audio->read_lock);
+ }
+ audio->avsync_flag = 1;
+ wake_up(&audio->avsync_wait);
+}
+
+static int audmp3_events_pending(struct audio *audio)
+{
+ unsigned long flags;
+ int empty;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ empty = !list_empty(&audio->event_queue);
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+ return empty || audio->event_abort;
+}
+
+static void audmp3_reset_event_queue(struct audio *audio)
+{
+ unsigned long flags;
+ struct audmp3_event *drv_evt;
+ struct list_head *ptr, *next;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ list_for_each_safe(ptr, next, &audio->event_queue) {
+ drv_evt = list_first_entry(&audio->event_queue,
+ struct audmp3_event, list);
+ list_del(&drv_evt->list);
+ kfree(drv_evt);
+ }
+ list_for_each_safe(ptr, next, &audio->free_event_queue) {
+ drv_evt = list_first_entry(&audio->free_event_queue,
+ struct audmp3_event, list);
+ list_del(&drv_evt->list);
+ kfree(drv_evt);
+ }
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+ return;
+}
+
+static long audmp3_process_event_req(struct audio *audio, void __user *arg)
+{
+ long rc;
+ struct msm_audio_event usr_evt;
+ struct audmp3_event *drv_evt = NULL;
+ int timeout;
+ unsigned long flags;
+
+ if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event)))
+ return -EFAULT;
+
+ timeout = (int) usr_evt.timeout_ms;
+
+ if (timeout > 0) {
+ rc = wait_event_interruptible_timeout(
+ audio->event_wait, audmp3_events_pending(audio),
+ msecs_to_jiffies(timeout));
+ if (rc == 0)
+ return -ETIMEDOUT;
+ } else {
+ rc = wait_event_interruptible(
+ audio->event_wait, audmp3_events_pending(audio));
+ }
+
+ if (rc < 0)
+ return rc;
+
+ if (audio->event_abort) {
+ audio->event_abort = 0;
+ return -ENODEV;
+ }
+
+ rc = 0;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ if (!list_empty(&audio->event_queue)) {
+ drv_evt = list_first_entry(&audio->event_queue,
+ struct audmp3_event, list);
+ list_del(&drv_evt->list);
+ }
+ if (drv_evt) {
+ usr_evt.event_type = drv_evt->event_type;
+ usr_evt.event_payload = drv_evt->payload;
+ list_add_tail(&drv_evt->list, &audio->free_event_queue);
+ } else
+ rc = -1;
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+ if (drv_evt->event_type == AUDIO_EVENT_WRITE_DONE ||
+ drv_evt->event_type == AUDIO_EVENT_READ_DONE) {
+ mutex_lock(&audio->lock);
+ audmp3_pmem_fixup(audio, drv_evt->payload.aio_buf.buf_addr,
+ drv_evt->payload.aio_buf.buf_len, 0);
+ mutex_unlock(&audio->lock);
+ }
+ if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt)))
+ rc = -EFAULT;
+
+ return rc;
+}
+
+static int audmp3_pmem_check(struct audio *audio,
+ void *vaddr, unsigned long len)
+{
+ struct audmp3_pmem_region *region_elt;
+ struct audmp3_pmem_region t = { .vaddr = vaddr, .len = len };
+
+ list_for_each_entry(region_elt, &audio->pmem_region_queue, list) {
+ if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) ||
+ OVERLAPS(region_elt, &t)) {
+ MM_ERR("region (vaddr %p len %ld)"
+ " clashes with registered region"
+ " (vaddr %p paddr %p len %ld)\n",
+ vaddr, len,
+ region_elt->vaddr,
+ (void *)region_elt->paddr,
+ region_elt->len);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int audmp3_pmem_add(struct audio *audio,
+ struct msm_audio_pmem_info *info)
+{
+ unsigned long paddr, kvaddr, len;
+ struct file *file;
+ struct audmp3_pmem_region *region;
+ int rc = -EINVAL;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ region = kmalloc(sizeof(*region), GFP_KERNEL);
+
+ if (!region) {
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ if (get_pmem_file(info->fd, &paddr, &kvaddr, &len, &file)) {
+ kfree(region);
+ goto end;
+ }
+
+ rc = audmp3_pmem_check(audio, info->vaddr, len);
+ if (rc < 0) {
+ put_pmem_file(file);
+ kfree(region);
+ goto end;
+ }
+
+ region->vaddr = info->vaddr;
+ region->fd = info->fd;
+ region->paddr = paddr;
+ region->kvaddr = kvaddr;
+ region->len = len;
+ region->file = file;
+ region->ref_cnt = 0;
+ MM_DBG("add region paddr %lx vaddr %p, len %lu\n", region->paddr,
+ region->vaddr, region->len);
+ list_add_tail(®ion->list, &audio->pmem_region_queue);
+end:
+ return rc;
+}
+
+static int audmp3_pmem_remove(struct audio *audio,
+ struct msm_audio_pmem_info *info)
+{
+ struct audmp3_pmem_region *region;
+ struct list_head *ptr, *next;
+ int rc = -EINVAL;
+
+ MM_DBG("info fd %d vaddr %p\n", info->fd, info->vaddr);
+
+ list_for_each_safe(ptr, next, &audio->pmem_region_queue) {
+ region = list_entry(ptr, struct audmp3_pmem_region, list);
+
+ if ((region->fd == info->fd) &&
+ (region->vaddr == info->vaddr)) {
+ if (region->ref_cnt) {
+ MM_DBG("region %p in use ref_cnt %d\n",
+ region, region->ref_cnt);
+ break;
+ }
+ MM_DBG("remove region fd %d vaddr %p \n",
+ info->fd, info->vaddr);
+ list_del(®ion->list);
+ put_pmem_file(region->file);
+ kfree(region);
+ rc = 0;
+ break;
+ }
+ }
+
+ return rc;
+}
+
+static int audmp3_pmem_lookup_vaddr(struct audio *audio, void *addr,
+ unsigned long len, struct audmp3_pmem_region **region)
+{
+ struct audmp3_pmem_region *region_elt;
+
+ int match_count = 0;
+
+ *region = NULL;
+
+ /* returns physical address or zero */
+ list_for_each_entry(region_elt, &audio->pmem_region_queue,
+ list) {
+ if (addr >= region_elt->vaddr &&
+ addr < region_elt->vaddr + region_elt->len &&
+ addr + len <= region_elt->vaddr + region_elt->len) {
+ /* offset since we could pass vaddr inside a registerd
+ * pmem buffer
+ */
+
+ match_count++;
+ if (!*region)
+ *region = region_elt;
+ }
+ }
+
+ if (match_count > 1) {
+ MM_ERR("multiple hits for vaddr %p, len %ld\n", addr, len);
+ list_for_each_entry(region_elt,
+ &audio->pmem_region_queue, list) {
+ if (addr >= region_elt->vaddr &&
+ addr < region_elt->vaddr + region_elt->len &&
+ addr + len <= region_elt->vaddr + region_elt->len)
+ MM_ERR("\t%p, %ld --> %p\n", region_elt->vaddr,
+ region_elt->len,
+ (void *)region_elt->paddr);
+ }
+ }
+
+ return *region ? 0 : -1;
+}
+
+unsigned long audmp3_pmem_fixup(struct audio *audio, void *addr,
+ unsigned long len, int ref_up)
+{
+ struct audmp3_pmem_region *region;
+ unsigned long paddr;
+ int ret;
+
+ ret = audmp3_pmem_lookup_vaddr(audio, addr, len, ®ion);
+ if (ret) {
+ MM_ERR("lookup (%p, %ld) failed\n", addr, len);
+ return 0;
+ }
+ if (ref_up)
+ region->ref_cnt++;
+ else
+ region->ref_cnt--;
+ MM_DBG("found region %p ref_cnt %d\n", region, region->ref_cnt);
+ paddr = region->paddr + (addr - region->vaddr);
+ return paddr;
+}
+
+/* audio -> lock must be held at this point */
+static int audmp3_aio_buf_add(struct audio *audio, unsigned dir,
+ void __user *arg)
+{
+ unsigned long flags;
+ struct audmp3_buffer_node *buf_node;
+
+ buf_node = kmalloc(sizeof(*buf_node), GFP_KERNEL);
+
+ if (!buf_node)
+ return -ENOMEM;
+
+ if (copy_from_user(&buf_node->buf, arg, sizeof(buf_node->buf))) {
+ kfree(buf_node);
+ return -EFAULT;
+ }
+
+ MM_DBG("node %p dir %x buf_addr %p buf_len %d data_len \
+ %d\n", buf_node, dir,
+ buf_node->buf.buf_addr, buf_node->buf.buf_len,
+ buf_node->buf.data_len);
+
+ buf_node->paddr = audmp3_pmem_fixup(
+ audio, buf_node->buf.buf_addr,
+ buf_node->buf.buf_len, 1);
+
+ if (dir) {
+ /* write */
+ if (!buf_node->paddr ||
+ (buf_node->paddr & 0x1) ||
+ (buf_node->buf.data_len & 0x1) ||
+ (!audio->pcm_feedback &&
+ !buf_node->buf.data_len)) {
+ kfree(buf_node);
+ return -EINVAL;
+ }
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ list_add_tail(&buf_node->list, &audio->out_queue);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ audio->drv_ops.send_data(audio, 0);
+ } else {
+ /* read */
+ if (!buf_node->paddr ||
+ (buf_node->paddr & 0x1) ||
+ (buf_node->buf.buf_len < PCM_BUFSZ_MIN)) {
+ kfree(buf_node);
+ return -EINVAL;
+ }
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ list_add_tail(&buf_node->list, &audio->in_queue);
+ audio->drv_ops.buffer_refresh(audio);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ }
+
+ MM_DBG("Add buf_node %p paddr %lx\n", buf_node, buf_node->paddr);
+
+ return 0;
+}
+
+static int audio_enable_eq(struct audio *audio, int enable)
+{
+ if (audio->eq_enable == enable && !audio->eq_needs_commit)
+ return 0;
+
+ audio->eq_enable = enable;
+
+ if (audio->running) {
+ audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq, POPP);
+ audio->eq_needs_commit = 0;
+ }
+ return 0;
+}
+
+static int audio_get_avsync_data(struct audio *audio,
+ struct msm_audio_stats *stats)
+{
+ int rc = -EINVAL;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) {
+ /* av_sync sample count */
+ stats->sample_count = (audio->avsync[2] << 16) |
+ (audio->avsync[3]);
+
+ /* av_sync byte_count */
+ stats->byte_count = (audio->avsync[5] << 16) |
+ (audio->avsync[6]);
+
+ audio->avsync_flag = 0;
+ rc = 0;
+ }
+ local_irq_restore(flags);
+ return rc;
+
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct audio *audio = file->private_data;
+ int rc = -EINVAL;
+ unsigned long flags = 0;
+ uint16_t enable_mask;
+ int enable;
+ int prev_state;
+
+ MM_DBG("cmd = %d\n", cmd);
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+
+ audio->avsync_flag = 0;
+ memset(&stats, 0, sizeof(stats));
+ if (audpp_query_avsync(audio->dec_id) < 0)
+ return rc;
+
+ rc = wait_event_interruptible_timeout(audio->avsync_wait,
+ (audio->avsync_flag == 1),
+ msecs_to_jiffies(AUDPP_AVSYNC_EVENT_TIMEOUT));
+
+ if (rc < 0)
+ return rc;
+ else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) {
+ if (audio_get_avsync_data(audio, &stats) < 0)
+ return rc;
+
+ if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ } else
+ return -EAGAIN;
+ }
+
+ switch (cmd) {
+ case AUDIO_ENABLE_AUDPP:
+ if (copy_from_user(&enable_mask, (void *) arg,
+ sizeof(enable_mask))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ enable = (enable_mask & EQ_ENABLE) ? 1 : 0;
+ audio_enable_eq(audio, enable);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ rc = 0;
+ break;
+ case AUDIO_SET_VOLUME:
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->vol_pan.volume = arg;
+ if (audio->running)
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ rc = 0;
+ break;
+
+ case AUDIO_SET_PAN:
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->vol_pan.pan = arg;
+ if (audio->running)
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ rc = 0;
+ break;
+
+ case AUDIO_SET_EQ:
+ prev_state = audio->eq_enable;
+ audio->eq_enable = 0;
+ if (copy_from_user(&audio->eq.num_bands, (void *) arg,
+ sizeof(audio->eq) -
+ (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) {
+ rc = -EFAULT;
+ break;
+ }
+ audio->eq_enable = prev_state;
+ audio->eq_needs_commit = 1;
+ rc = 0;
+ break;
+ }
+
+ if (-EINVAL != rc)
+ return rc;
+
+ if (cmd == AUDIO_GET_EVENT) {
+ MM_DBG(" AUDIO_GET_EVENT\n");
+ if (mutex_trylock(&audio->get_event_lock)) {
+ rc = audmp3_process_event_req(audio,
+ (void __user *) arg);
+ mutex_unlock(&audio->get_event_lock);
+ } else
+ rc = -EBUSY;
+ return rc;
+ }
+
+ if (cmd == AUDIO_ABORT_GET_EVENT) {
+ audio->event_abort = 1;
+ wake_up(&audio->event_wait);
+ return 0;
+ }
+
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START:
+ MM_DBG("AUDIO_START\n");
+ rc = audio_enable(audio);
+ if (!rc) {
+ rc = wait_event_interruptible_timeout(audio->wait,
+ audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+ msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+ MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc);
+
+ if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS)
+ rc = -ENODEV;
+ else
+ rc = 0;
+ }
+ break;
+ case AUDIO_STOP:
+ MM_DBG("AUDIO_STOP\n");
+ rc = audio_disable(audio);
+ audio->stopped = 1;
+ audio_ioport_reset(audio);
+ audio->stopped = 0;
+ break;
+ case AUDIO_FLUSH:
+ MM_DBG("AUDIO_FLUSH\n");
+ audio->rflush = 1;
+ audio->wflush = 1;
+ audio_ioport_reset(audio);
+ if (audio->running) {
+ audpp_flush(audio->dec_id);
+ rc = wait_event_interruptible(audio->write_wait,
+ !audio->wflush);
+ if (rc < 0) {
+ MM_ERR("AUDIO_FLUSH interrupted\n");
+ rc = -EINTR;
+ }
+ } else {
+ audio->rflush = 0;
+ audio->wflush = 0;
+ }
+ break;
+ case AUDIO_OUTPORT_FLUSH:
+ MM_DBG("AUDIO_OUTPORT_FLUSH\n");
+ audio->rflush = 1;
+ if (audio->drv_status & ADRV_STATUS_AIO_INTF) {
+ audio->drv_ops.in_flush(audio);
+ } else {
+ wake_up(&audio->read_wait);
+ mutex_lock(&audio->read_lock);
+ audio->drv_ops.in_flush(audio);
+ mutex_unlock(&audio->read_lock);
+ }
+ audplay_outport_flush(audio);
+ rc = wait_event_interruptible(audio->read_wait,
+ !audio->rflush);
+ if (rc < 0) {
+ MM_ERR("AUDPLAY_OUTPORT_FLUSH interrupted\n");
+ rc = -EINTR;
+ }
+ break;
+ case AUDIO_SET_CONFIG: {
+ struct msm_audio_config config;
+ if (copy_from_user(&config, (void *) arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (config.channel_count == 1) {
+ config.channel_count = AUDPP_CMD_PCM_INTF_MONO_V;
+ } else if (config.channel_count == 2) {
+ config.channel_count = AUDPP_CMD_PCM_INTF_STEREO_V;
+ } else {
+ rc = -EINVAL;
+ break;
+ }
+ audio->mfield = config.meta_field;
+ audio->out_sample_rate = config.sample_rate;
+ audio->out_channel_mode = config.channel_count;
+ rc = 0;
+ break;
+ }
+ case AUDIO_GET_CONFIG: {
+ struct msm_audio_config config;
+ config.buffer_size = (audio->out_dma_sz >> 1);
+ config.buffer_count = 2;
+ config.sample_rate = audio->out_sample_rate;
+ if (audio->out_channel_mode == AUDPP_CMD_PCM_INTF_MONO_V)
+ config.channel_count = 1;
+ else
+ config.channel_count = 2;
+ config.meta_field = 0;
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+ config.unused[2] = 0;
+ if (copy_to_user((void *) arg, &config, sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ case AUDIO_GET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+ config.pcm_feedback = audio->pcm_feedback;
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+ config.buffer_size = PCM_BUFSZ_MIN;
+ if (copy_to_user((void *)arg, &config,
+ sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ case AUDIO_SET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+ if (copy_from_user
+ (&config, (void *)arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ if (config.pcm_feedback != audio->pcm_feedback) {
+ MM_ERR("Not sufficient permission to"
+ "change the playback mode\n");
+ rc = -EACCES;
+ break;
+ }
+ if (audio->drv_status & ADRV_STATUS_AIO_INTF) {
+ rc = 0;
+ break;
+ }
+
+ if ((config.buffer_count > PCM_BUF_MAX_COUNT) ||
+ (config.buffer_count == 1))
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+
+ if (config.buffer_size < PCM_BUFSZ_MIN)
+ config.buffer_size = PCM_BUFSZ_MIN;
+
+ /* Check if pcm feedback is required */
+ if ((config.pcm_feedback) && (!audio->read_data)) {
+ MM_DBG("allocate PCM buffer %d\n",
+ config.buffer_count *
+ config.buffer_size);
+ audio->read_phys = pmem_kalloc(
+ config.buffer_size *
+ config.buffer_count,
+ PMEM_MEMTYPE_EBI1|
+ PMEM_ALIGNMENT_4K);
+ if (IS_ERR((void *)audio->read_phys)) {
+ rc = -ENOMEM;
+ break;
+ }
+ audio->read_data = ioremap(audio->read_phys,
+ config.buffer_size *
+ config.buffer_count);
+ if (!audio->read_data) {
+ MM_ERR("malloc read buf failed\n");
+ rc = -ENOMEM;
+ pmem_kfree(audio->read_phys);
+ } else {
+ uint8_t index;
+ uint32_t offset = 0;
+ audio->buf_refresh = 0;
+ audio->pcm_buf_count =
+ config.buffer_count;
+ audio->read_next = 0;
+ audio->fill_next = 0;
+
+ for (index = 0;
+ index < config.buffer_count;
+ index++) {
+ audio->in[index].data =
+ audio->read_data + offset;
+ audio->in[index].addr =
+ audio->read_phys + offset;
+ audio->in[index].size =
+ config.buffer_size;
+ audio->in[index].used = 0;
+ offset += config.buffer_size;
+ }
+ rc = 0;
+ MM_DBG("read buf: phy addr \
+ 0x%08x kernel addr 0x%08x\n",
+ audio->read_phys,
+ (int)audio->read_data);
+ }
+ } else {
+ rc = 0;
+ }
+ break;
+ }
+ case AUDIO_PAUSE:
+ MM_DBG("AUDIO_PAUSE %ld\n", arg);
+ rc = audpp_pause(audio->dec_id, (int) arg);
+ break;
+
+ case AUDIO_GET_STREAM_INFO:{
+ if (audio->stream_info.sample_rate == 0) {
+ /* haven't received DSP stream event,
+ the stream info is not updated */
+ rc = -EPERM;
+ break;
+ }
+ if (copy_to_user((void *)arg, &audio->stream_info,
+ sizeof(struct msm_audio_bitstream_info)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ case AUDIO_GET_BITSTREAM_ERROR_INFO:{
+ if ((audio->bitstream_error_info.err_msg_indicator &
+ AUDPLAY_STREAM_INFO_MSG_MASK) ==
+ AUDPLAY_STREAM_INFO_MSG_MASK) {
+ /* haven't received bitstream error info event,
+ the bitstream error info is not updated */
+ rc = -EPERM;
+ break;
+ }
+ if (copy_to_user((void *)arg, &audio->bitstream_error_info,
+ sizeof(struct msm_audio_bitstream_error_info)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+
+ case AUDIO_REGISTER_PMEM: {
+ struct msm_audio_pmem_info info;
+ MM_DBG("AUDIO_REGISTER_PMEM\n");
+ if (copy_from_user(&info, (void *) arg, sizeof(info)))
+ rc = -EFAULT;
+ else
+ rc = audmp3_pmem_add(audio, &info);
+ break;
+ }
+
+ case AUDIO_DEREGISTER_PMEM: {
+ struct msm_audio_pmem_info info;
+ MM_DBG("AUDIO_DEREGISTER_PMEM\n");
+ if (copy_from_user(&info, (void *) arg, sizeof(info)))
+ rc = -EFAULT;
+ else
+ rc = audmp3_pmem_remove(audio, &info);
+ break;
+ }
+ case AUDIO_ASYNC_WRITE:
+ if (audio->drv_status & ADRV_STATUS_FSYNC)
+ rc = -EBUSY;
+ else
+ rc = audmp3_aio_buf_add(audio, 1, (void __user *) arg);
+ break;
+
+ case AUDIO_ASYNC_READ:
+ if (audio->pcm_feedback)
+ rc = audmp3_aio_buf_add(audio, 0, (void __user *) arg);
+ else
+ rc = -EPERM;
+ break;
+ case AUDIO_GET_SESSION_ID:
+ if (copy_to_user((void *) arg, &audio->dec_id,
+ sizeof(unsigned short)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ case AUDIO_SET_ERR_THRESHOLD_VALUE:
+ if (copy_from_user(&audio->bitstream_error_threshold_value,
+ (void *)arg, sizeof(uint32_t)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+/* Only useful in tunnel-mode */
+int audmp3_async_fsync(struct audio *audio)
+{
+ int rc = 0;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+
+ /* Blocking client sends more data */
+ mutex_lock(&audio->lock);
+ audio->drv_status |= ADRV_STATUS_FSYNC;
+ mutex_unlock(&audio->lock);
+
+ mutex_lock(&audio->write_lock);
+ /* pcm dmamiss message is sent continously
+ * when decoder is starved so no race
+ * condition concern
+ */
+ audio->teos = 0;
+
+ rc = wait_event_interruptible(audio->write_wait,
+ (audio->teos && audio->out_needed &&
+ list_empty(&audio->out_queue))
+ || audio->wflush || audio->stopped);
+
+ if (audio->stopped || audio->wflush)
+ rc = -EBUSY;
+
+ mutex_unlock(&audio->write_lock);
+ mutex_lock(&audio->lock);
+ audio->drv_status &= ~ADRV_STATUS_FSYNC;
+ mutex_unlock(&audio->lock);
+
+ return rc;
+}
+
+int audmp3_sync_fsync(struct audio *audio)
+{
+ struct buffer *frame;
+ int rc = 0;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+
+ mutex_lock(&audio->write_lock);
+
+ rc = wait_event_interruptible(audio->write_wait,
+ (!audio->out[0].used &&
+ !audio->out[1].used &&
+ audio->out_needed) || audio->wflush);
+
+ if (rc < 0)
+ goto done;
+ else if (audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+
+ if (audio->reserved) {
+ MM_DBG("send reserved byte\n");
+ frame = audio->out + audio->out_tail;
+ ((char *) frame->data)[0] = audio->rsv_byte;
+ ((char *) frame->data)[1] = 0;
+ frame->used = 2;
+ audio->drv_ops.send_data(audio, 0);
+
+ rc = wait_event_interruptible(audio->write_wait,
+ (!audio->out[0].used &&
+ !audio->out[1].used &&
+ audio->out_needed) || audio->wflush);
+
+ if (rc < 0)
+ goto done;
+ else if (audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+ }
+
+ /* pcm dmamiss message is sent continously
+ * when decoder is starved so no race
+ * condition concern
+ */
+ audio->teos = 0;
+
+ rc = wait_event_interruptible(audio->write_wait,
+ audio->teos || audio->wflush);
+
+ if (audio->wflush)
+ rc = -EBUSY;
+
+done:
+ mutex_unlock(&audio->write_lock);
+ return rc;
+}
+
+int audmp3_fsync(struct file *file, int datasync)
+{
+ struct audio *audio = file->private_data;
+
+ if (!audio->running || audio->pcm_feedback)
+ return -EINVAL;
+
+ return audio->drv_ops.fsync(audio);
+}
+
+static ssize_t audio_read(struct file *file, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ int rc = 0;
+
+ if (audio->drv_status & ADRV_STATUS_AIO_INTF)
+ return -EPERM;
+ else if (!audio->pcm_feedback)
+ return 0; /* PCM feedback disabled. Nothing to read */
+
+ mutex_lock(&audio->read_lock);
+ MM_DBG("%d \n", count);
+ while (count > 0) {
+ rc = wait_event_interruptible_timeout(
+ audio->read_wait,
+ (audio->in[audio->read_next].
+ used > 0) || (audio->stopped)
+ || (audio->rflush),
+ msecs_to_jiffies(MSM_AUD_BUFFER_UPDATE_WAIT_MS));
+
+ if (rc == 0) {
+ rc = -ETIMEDOUT;
+ break;
+ } else if (rc < 0)
+ break;
+
+ if (audio->stopped || audio->rflush) {
+ rc = -EBUSY;
+ break;
+ }
+
+ if (count < audio->in[audio->read_next].used) {
+ /* Read must happen in frame boundary. Since
+ * driver does not know frame size, read count
+ * must be greater or equal
+ * to size of PCM samples
+ */
+ MM_DBG("no partial frame done reading\n");
+ break;
+ } else {
+ MM_DBG("read from in[%d]\n", audio->read_next);
+
+ if (copy_to_user
+ (buf, audio->in[audio->read_next].data,
+ audio->in[audio->read_next].used)) {
+ MM_ERR("invalid addr %x \n", (unsigned int)buf);
+ rc = -EFAULT;
+ break;
+ }
+ count -= audio->in[audio->read_next].used;
+ buf += audio->in[audio->read_next].used;
+ audio->in[audio->read_next].used = 0;
+ if ((++audio->read_next) == audio->pcm_buf_count)
+ audio->read_next = 0;
+ break; /* Force to exit while loop
+ * to prevent output thread
+ * sleep too long if data is
+ * not ready at this moment.
+ */
+ }
+ }
+
+ /* don't feed output buffer to HW decoder during flushing
+ * buffer refresh command will be sent once flush completes
+ * send buf refresh command here can confuse HW decoder
+ */
+ if (audio->buf_refresh && !audio->rflush) {
+ audio->buf_refresh = 0;
+ MM_DBG("kick start pcm feedback again\n");
+ audio->drv_ops.buffer_refresh(audio);
+ }
+
+ mutex_unlock(&audio->read_lock);
+
+ if (buf > start)
+ rc = buf - start;
+
+ MM_DBG("read %d bytes\n", rc);
+ return rc;
+}
+
+static int audmp3_process_eos(struct audio *audio,
+ const char __user *buf_start, unsigned short mfield_size)
+{
+ int rc = 0;
+ struct buffer *frame;
+ char *buf_ptr;
+
+ if (audio->reserved) {
+ MM_DBG("flush reserve byte\n");
+ frame = audio->out + audio->out_head;
+ buf_ptr = frame->data;
+ rc = wait_event_interruptible(audio->write_wait,
+ (frame->used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+ if (rc < 0)
+ goto done;
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+
+ buf_ptr[0] = audio->rsv_byte;
+ buf_ptr[1] = 0;
+ audio->out_head ^= 1;
+ frame->mfield_sz = 0;
+ frame->used = 2;
+ audio->reserved = 0;
+ audio->drv_ops.send_data(audio, 0);
+ }
+
+ frame = audio->out + audio->out_head;
+
+ rc = wait_event_interruptible(audio->write_wait,
+ (audio->out_needed &&
+ audio->out[0].used == 0 &&
+ audio->out[1].used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+
+ if (rc < 0)
+ goto done;
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+
+ if (copy_from_user(frame->data, buf_start, mfield_size)) {
+ rc = -EFAULT;
+ goto done;
+ }
+
+ frame->mfield_sz = mfield_size;
+ audio->out_head ^= 1;
+ frame->used = mfield_size;
+ audio->drv_ops.send_data(audio, 0);
+done:
+ return rc;
+}
+
+static ssize_t audio_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ struct buffer *frame;
+ size_t xfer;
+ char *cpy_ptr;
+ int rc = 0, eos_condition = AUDMP3_EOS_NONE;
+ unsigned dsize;
+ unsigned short mfield_size = 0;
+
+ if (audio->drv_status & ADRV_STATUS_AIO_INTF)
+ return -EPERM;
+
+ MM_DBG("cnt=%d\n", count);
+
+ mutex_lock(&audio->write_lock);
+ while (count > 0) {
+ frame = audio->out + audio->out_head;
+ cpy_ptr = frame->data;
+ dsize = 0;
+ rc = wait_event_interruptible(audio->write_wait,
+ (frame->used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+ if (rc < 0)
+ break;
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ break;
+ }
+ if (audio->mfield) {
+ if (buf == start) {
+ /* Processing beginning of user buffer */
+ if (__get_user(mfield_size,
+ (unsigned short __user *) buf)) {
+ rc = -EFAULT;
+ break;
+ } else if (mfield_size > count) {
+ rc = -EINVAL;
+ break;
+ }
+ MM_DBG("mf offset_val %x\n", mfield_size);
+ if (copy_from_user(cpy_ptr, buf, mfield_size)) {
+ rc = -EFAULT;
+ break;
+ }
+ /* Check if EOS flag is set and buffer has
+ * contains just meta field
+ */
+ if (cpy_ptr[AUDMP3_EOS_FLG_OFFSET] &
+ AUDMP3_EOS_FLG_MASK) {
+ MM_DBG("EOS SET\n");
+ eos_condition = AUDMP3_EOS_SET;
+ if (mfield_size == count) {
+ buf += mfield_size;
+ break;
+ } else
+ cpy_ptr[AUDMP3_EOS_FLG_OFFSET]
+ &= ~AUDMP3_EOS_FLG_MASK;
+ }
+ cpy_ptr += mfield_size;
+ count -= mfield_size;
+ dsize += mfield_size;
+ buf += mfield_size;
+ } else {
+ mfield_size = 0;
+ MM_DBG("continuous buffer\n");
+ }
+ frame->mfield_sz = mfield_size;
+ }
+
+ if (audio->reserved) {
+ MM_DBG("append reserved byte %x\n", audio->rsv_byte);
+ *cpy_ptr = audio->rsv_byte;
+ xfer = (count > ((frame->size - mfield_size) - 1)) ?
+ (frame->size - mfield_size) - 1 : count;
+ cpy_ptr++;
+ dsize += 1;
+ audio->reserved = 0;
+ } else
+ xfer = (count > (frame->size - mfield_size)) ?
+ (frame->size - mfield_size) : count;
+
+ if (copy_from_user(cpy_ptr, buf, xfer)) {
+ rc = -EFAULT;
+ break;
+ }
+
+ dsize += xfer;
+ if (dsize & 1) {
+ audio->rsv_byte = ((char *) frame->data)[dsize - 1];
+ MM_DBG("odd length buf reserve last byte %x\n",
+ audio->rsv_byte);
+ audio->reserved = 1;
+ dsize--;
+ }
+ count -= xfer;
+ buf += xfer;
+
+ if (dsize > 0) {
+ audio->out_head ^= 1;
+ frame->used = dsize;
+ audio->drv_ops.send_data(audio, 0);
+ }
+ }
+ if (eos_condition == AUDMP3_EOS_SET)
+ rc = audmp3_process_eos(audio, start, mfield_size);
+ mutex_unlock(&audio->write_lock);
+ if (!rc) {
+ if (buf > start)
+ return buf - start;
+ }
+ return rc;
+}
+
+static void audmp3_reset_pmem_region(struct audio *audio)
+{
+ struct audmp3_pmem_region *region;
+ struct list_head *ptr, *next;
+
+ list_for_each_safe(ptr, next, &audio->pmem_region_queue) {
+ region = list_entry(ptr, struct audmp3_pmem_region, list);
+ list_del(®ion->list);
+ put_pmem_file(region->file);
+ kfree(region);
+ }
+
+ return;
+}
+
+static int audio_release(struct inode *inode, struct file *file)
+{
+ struct audio *audio = file->private_data;
+
+ MM_INFO("audio instance 0x%08x freeing\n", (int)audio);
+ mutex_lock(&audio->lock);
+ auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id);
+ audio_disable(audio);
+ audio->drv_ops.out_flush(audio);
+ audio->drv_ops.in_flush(audio);
+ audmp3_reset_pmem_region(audio);
+
+ msm_adsp_put(audio->audplay);
+ audpp_adec_free(audio->dec_id);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&audio->suspend_ctl.node);
+#endif
+ audio->opened = 0;
+ audio->event_abort = 1;
+ wake_up(&audio->event_wait);
+ audmp3_reset_event_queue(audio);
+ if (audio->data) {
+ iounmap(audio->data);
+ pmem_kfree(audio->phys);
+ }
+ if (audio->read_data) {
+ iounmap(audio->read_data);
+ pmem_kfree(audio->read_phys);
+ }
+ mutex_unlock(&audio->lock);
+#ifdef CONFIG_DEBUG_FS
+ if (audio->dentry)
+ debugfs_remove(audio->dentry);
+#endif
+ kfree(audio);
+ return 0;
+}
+
+static void audmp3_post_event(struct audio *audio, int type,
+ union msm_audio_event_payload payload)
+{
+ struct audmp3_event *e_node = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+
+ if (!list_empty(&audio->free_event_queue)) {
+ e_node = list_first_entry(&audio->free_event_queue,
+ struct audmp3_event, list);
+ list_del(&e_node->list);
+ } else {
+ e_node = kmalloc(sizeof(struct audmp3_event), GFP_ATOMIC);
+ if (!e_node) {
+ MM_ERR("No mem to post event %d\n", type);
+ return;
+ }
+ }
+
+ e_node->event_type = type;
+ e_node->payload = payload;
+
+ list_add_tail(&e_node->list, &audio->event_queue);
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+ wake_up(&audio->event_wait);
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void audmp3_suspend(struct early_suspend *h)
+{
+ struct audmp3_suspend_ctl *ctl =
+ container_of(h, struct audmp3_suspend_ctl, node);
+ union msm_audio_event_payload payload;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ audmp3_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload);
+}
+
+static void audmp3_resume(struct early_suspend *h)
+{
+ struct audmp3_suspend_ctl *ctl =
+ container_of(h, struct audmp3_suspend_ctl, node);
+ union msm_audio_event_payload payload;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ audmp3_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload);
+}
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t audmp3_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t audmp3_debug_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ const int debug_bufmax = 4096;
+ static char buffer[4096];
+ int n = 0, i;
+ struct audio *audio = file->private_data;
+
+ mutex_lock(&audio->lock);
+ n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "enabled %d\n", audio->enabled);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "stopped %d\n", audio->stopped);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "pcm_feedback %d\n", audio->pcm_feedback);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_buf_sz %d\n", audio->out[0].size);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "pcm_buf_count %d \n", audio->pcm_buf_count);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "pcm_buf_sz %d \n", audio->in[0].size);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "volume %x \n", audio->vol_pan.volume);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "sample rate %d \n", audio->out_sample_rate);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "channel mode %d \n", audio->out_channel_mode);
+ mutex_unlock(&audio->lock);
+ /* Following variables are only useful for debugging when
+ * when playback halts unexpectedly. Thus, no mutual exclusion
+ * enforced
+ */
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "wflush %d\n", audio->wflush);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "rflush %d\n", audio->rflush);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "running %d \n", audio->running);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "dec state %d \n", audio->dec_state);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_needed %d \n", audio->out_needed);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_head %d \n", audio->out_head);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_tail %d \n", audio->out_tail);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out[0].used %d \n", audio->out[0].used);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out[1].used %d \n", audio->out[1].used);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "buffer_refresh %d \n", audio->buf_refresh);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "read_next %d \n", audio->read_next);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "fill_next %d \n", audio->fill_next);
+ for (i = 0; i < audio->pcm_buf_count; i++)
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "in[%d].size %d \n", i, audio->in[i].used);
+ buffer[n] = 0;
+ return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static const struct file_operations audmp3_debug_fops = {
+ .read = audmp3_debug_read,
+ .open = audmp3_debug_open,
+};
+#endif
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+
+ struct audio *audio = NULL;
+ int rc, i, dec_attrb, decid;
+ struct audmp3_event *e_node = NULL;
+ unsigned pmem_sz = DMASZ_MAX;
+#ifdef CONFIG_DEBUG_FS
+ /* 4 bytes represents decoder number, 1 byte for terminate string */
+ char name[sizeof "msm_mp3_" + 5];
+#endif
+
+ /* Allocate audio instance, set to zero */
+ audio = kzalloc(sizeof(struct audio), GFP_KERNEL);
+ if (!audio) {
+ MM_ERR("no memory to allocate audio instance \n");
+ rc = -ENOMEM;
+ goto done;
+ }
+ MM_INFO("audio instance 0x%08x created\n", (int)audio);
+
+ /* Allocate the decoder */
+ dec_attrb = AUDDEC_DEC_MP3;
+ if ((file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ dec_attrb |= MSM_AUD_MODE_NONTUNNEL;
+ audio->pcm_feedback = NON_TUNNEL_MODE_PLAYBACK;
+ } else if ((file->f_mode & FMODE_WRITE) &&
+ !(file->f_mode & FMODE_READ)) {
+ dec_attrb |= MSM_AUD_MODE_TUNNEL;
+ audio->pcm_feedback = TUNNEL_MODE_PLAYBACK;
+ } else {
+ kfree(audio);
+ rc = -EACCES;
+ goto done;
+ }
+
+ decid = audpp_adec_alloc(dec_attrb, &audio->module_name,
+ &audio->queue_id);
+ if (decid < 0) {
+ MM_ERR("No free decoder available, freeing instance 0x%08x\n",
+ (int)audio);
+ rc = -ENODEV;
+ kfree(audio);
+ goto done;
+ }
+ audio->dec_id = decid & MSM_AUD_DECODER_MASK;
+
+ /* AIO interface */
+ if (file->f_flags & O_NONBLOCK) {
+ MM_DBG("set to aio interface \n");
+ audio->drv_status |= ADRV_STATUS_AIO_INTF;
+ audio->drv_ops.pcm_buf_update = audmp3_async_pcm_buf_update;
+ audio->drv_ops.buffer_refresh = audmp3_async_buffer_refresh;
+ audio->drv_ops.send_data = audmp3_async_send_data;
+ audio->drv_ops.out_flush = audmp3_async_flush;
+ audio->drv_ops.in_flush = audmp3_async_flush_pcm_buf;
+ audio->drv_ops.fsync = audmp3_async_fsync;
+ } else {
+ MM_DBG("set to std io interface \n");
+ while (pmem_sz >= DMASZ_MIN) {
+ MM_DBG("pmemsz = %d \n", pmem_sz);
+ audio->phys = pmem_kalloc(pmem_sz, PMEM_MEMTYPE_EBI1|
+ PMEM_ALIGNMENT_4K);
+ if (!IS_ERR((void *)audio->phys)) {
+ audio->data = ioremap(audio->phys, pmem_sz);
+ if (!audio->data) {
+ MM_ERR("could not allocate write \
+ buffers, freeing instance \
+ 0x%08x\n", (int)audio);
+ rc = -ENOMEM;
+ pmem_kfree(audio->phys);
+ audpp_adec_free(audio->dec_id);
+ kfree(audio);
+ goto done;
+ }
+ MM_DBG("write buf: phy addr 0x%08x kernel addr\
+ 0x%08x\n", audio->phys,\
+ (int)audio->data);
+ break;
+ } else if (pmem_sz == DMASZ_MIN) {
+ MM_ERR("could not allocate write buffers, \
+ freeing instance 0x%08x\n",
+ (int)audio);
+ rc = -ENOMEM;
+ audpp_adec_free(audio->dec_id);
+ kfree(audio);
+ goto done;
+ } else
+ pmem_sz >>= 1;
+ }
+ audio->out_dma_sz = pmem_sz;
+ audio->drv_ops.pcm_buf_update = audio_update_pcm_buf_entry;
+ audio->drv_ops.buffer_refresh = audplay_buffer_refresh;
+ audio->drv_ops.send_data = audplay_send_data;
+ audio->drv_ops.out_flush = audio_flush;
+ audio->drv_ops.in_flush = audio_flush_pcm_buf;
+ audio->drv_ops.fsync = audmp3_sync_fsync;
+ audio->out[0].data = audio->data + 0;
+ audio->out[0].addr = audio->phys + 0;
+ audio->out[0].size = (audio->out_dma_sz >> 1);
+
+ audio->out[1].data = audio->data + audio->out[0].size;
+ audio->out[1].addr = audio->phys + audio->out[0].size;
+ audio->out[1].size = audio->out[0].size;
+ }
+
+ rc = msm_adsp_get(audio->module_name, &audio->audplay,
+ &audplay_adsp_ops, audio);
+
+ if (rc) {
+ MM_ERR("failed to get %s module freeing instance 0x%08x\n",
+ audio->module_name, (int)audio);
+ goto err;
+ }
+
+ /* Initialize all locks of audio instance */
+ mutex_init(&audio->lock);
+ mutex_init(&audio->write_lock);
+ mutex_init(&audio->read_lock);
+ mutex_init(&audio->get_event_lock);
+ spin_lock_init(&audio->dsp_lock);
+ init_waitqueue_head(&audio->write_wait);
+ init_waitqueue_head(&audio->read_wait);
+ INIT_LIST_HEAD(&audio->out_queue);
+ INIT_LIST_HEAD(&audio->in_queue);
+ INIT_LIST_HEAD(&audio->pmem_region_queue);
+ INIT_LIST_HEAD(&audio->free_event_queue);
+ INIT_LIST_HEAD(&audio->event_queue);
+ init_waitqueue_head(&audio->wait);
+ init_waitqueue_head(&audio->event_wait);
+ spin_lock_init(&audio->event_queue_lock);
+ init_waitqueue_head(&audio->avsync_wait);
+
+ audio->out_sample_rate = 44100;
+ audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V;
+ audio->vol_pan.volume = 0x2000;
+ audio->bitstream_error_threshold_value =
+ BITSTREAM_ERROR_THRESHOLD_VALUE;
+
+ audio->drv_ops.out_flush(audio);
+
+ file->private_data = audio;
+ audio->opened = 1;
+
+ audio->device_events = AUDDEV_EVT_DEV_RDY
+ |AUDDEV_EVT_DEV_RLS |
+ AUDDEV_EVT_STREAM_VOL_CHG;
+
+ rc = auddev_register_evt_listner(audio->device_events,
+ AUDDEV_CLNT_DEC,
+ audio->dec_id,
+ mp3_listner,
+ (void *)audio);
+ if (rc) {
+ MM_ERR("%s: failed to register listner\n", __func__);
+ goto event_err;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ snprintf(name, sizeof name, "msm_mp3_%04x", audio->dec_id);
+ audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *) audio, &audmp3_debug_fops);
+
+ if (IS_ERR(audio->dentry))
+ MM_DBG("debugfs_create_file failed\n");
+#endif
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+ audio->suspend_ctl.node.resume = audmp3_resume;
+ audio->suspend_ctl.node.suspend = audmp3_suspend;
+ audio->suspend_ctl.audio = audio;
+ register_early_suspend(&audio->suspend_ctl.node);
+#endif
+ for (i = 0; i < AUDMP3_EVENT_NUM; i++) {
+ e_node = kmalloc(sizeof(struct audmp3_event), GFP_KERNEL);
+ if (e_node)
+ list_add_tail(&e_node->list, &audio->free_event_queue);
+ else {
+ MM_ERR("event pkt alloc failed\n");
+ break;
+ }
+ }
+ memset(&audio->stream_info, 0, sizeof(struct msm_audio_bitstream_info));
+ memset(&audio->bitstream_error_info, 0,
+ sizeof(struct msm_audio_bitstream_info));
+done:
+ return rc;
+event_err:
+ msm_adsp_put(audio->audplay);
+err:
+ if (audio->data) {
+ iounmap(audio->data);
+ pmem_kfree(audio->phys);
+ }
+ audpp_adec_free(audio->dec_id);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_mp3_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_release,
+ .read = audio_read,
+ .write = audio_write,
+ .unlocked_ioctl = audio_ioctl,
+ .fsync = audmp3_fsync,
+};
+
+struct miscdevice audio_mp3_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_mp3",
+ .fops = &audio_mp3_fops,
+};
+
+static int __init audio_init(void)
+{
+ return misc_register(&audio_mp3_misc);
+}
+
+static void __exit audio_exit(void)
+{
+ misc_deregister(&audio_mp3_misc);
+}
+
+module_init(audio_init);
+module_exit(audio_exit);
+
+MODULE_DESCRIPTION("MSM MP3 driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_mvs.c b/arch/arm/mach-msm/qdsp5v2/audio_mvs.c
new file mode 100644
index 0000000..dc41bf4
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_mvs.c
@@ -0,0 +1,1748 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/kthread.h>
+#include <linux/list.h>
+#include <linux/uaccess.h>
+#include <linux/mutex.h>
+#include <linux/wakelock.h>
+#include <linux/msm_audio_mvs.h>
+#include <linux/slab.h>
+#include <mach/msm_rpcrouter.h>
+
+#define MVS_PROG 0x30000014
+#define MVS_VERS 0x00030001
+#define MVS_VERS_COMP_VER4 0x00040001
+#define MVS_VERS_COMP_VER5 0x00050001
+
+#define MVS_CLIENT_ID_VOIP 0x00000003
+
+#define MVS_ACQUIRE_PROC 4
+#define MVS_ENABLE_PROC 5
+#define MVS_RELEASE_PROC 6
+#define MVS_AMR_SET_AMR_MODE_PROC 7
+#define MVS_AMR_SET_AWB_MODE_PROC 8
+#define MVS_VOC_SET_FRAME_RATE_PROC 10
+#define MVS_GSM_SET_DTX_MODE_PROC 11
+#define MVS_G729A_SET_MODE_PROC 12
+#define MVS_G711_GET_MODE_PROC 14
+#define MVS_G711_SET_MODE_PROC 15
+#define MVS_G711A_GET_MODE_PROC 16
+#define MVS_G711A_SET_MODE_PROC 17
+#define MVS_G722_SET_MODE_PROC 20
+#define MVS_G722_GET_MODE_PROC 21
+#define MVS_SET_DTX_MODE_PROC 22
+
+#define MVS_EVENT_CB_TYPE_PROC 1
+#define MVS_PACKET_UL_FN_TYPE_PROC 2
+#define MVS_PACKET_DL_FN_TYPE_PROC 3
+
+#define MVS_CB_FUNC_ID 0xAAAABBBB
+#define MVS_UL_CB_FUNC_ID 0xBBBBCCCC
+#define MVS_DL_CB_FUNC_ID 0xCCCCDDDD
+
+#define MVS_FRAME_MODE_VOC_TX 1
+#define MVS_FRAME_MODE_VOC_RX 2
+#define MVS_FRAME_MODE_AMR_UL 3
+#define MVS_FRAME_MODE_AMR_DL 4
+#define MVS_FRAME_MODE_GSM_UL 5
+#define MVS_FRAME_MODE_GSM_DL 6
+#define MVS_FRAME_MODE_HR_UL 7
+#define MVS_FRAME_MODE_HR_DL 8
+#define MVS_FRAME_MODE_G711_UL 9
+#define MVS_FRAME_MODE_G711_DL 10
+#define MVS_FRAME_MODE_PCM_UL 13
+#define MVS_FRAME_MODE_PCM_DL 14
+#define MVS_FRAME_MODE_G729A_UL 17
+#define MVS_FRAME_MODE_G729A_DL 18
+#define MVS_FRAME_MODE_G711A_UL 19
+#define MVS_FRAME_MODE_G711A_DL 20
+#define MVS_FRAME_MODE_G722_UL 21
+#define MVS_FRAME_MODE_G722_DL 22
+
+
+
+#define MVS_PKT_CONTEXT_ISR 0x00000001
+
+#define RPC_TYPE_REQUEST 0
+#define RPC_TYPE_REPLY 1
+
+#define RPC_STATUS_FAILURE 0
+#define RPC_STATUS_SUCCESS 1
+#define RPC_STATUS_REJECT 1
+
+#define RPC_COMMON_HDR_SZ (sizeof(uint32_t) * 2)
+#define RPC_REQUEST_HDR_SZ (sizeof(struct rpc_request_hdr))
+#define RPC_REPLY_HDR_SZ (sizeof(uint32_t) * 3)
+
+enum audio_mvs_state_type {
+ AUDIO_MVS_CLOSED,
+ AUDIO_MVS_OPENED,
+ AUDIO_MVS_STARTED,
+ AUDIO_MVS_STOPPED
+};
+
+enum audio_mvs_event_type {
+ AUDIO_MVS_COMMAND,
+ AUDIO_MVS_MODE,
+ AUDIO_MVS_NOTIFY
+};
+
+enum audio_mvs_cmd_status_type {
+ AUDIO_MVS_CMD_FAILURE,
+ AUDIO_MVS_CMD_BUSY,
+ AUDIO_MVS_CMD_SUCCESS
+};
+
+enum audio_mvs_mode_status_type {
+ AUDIO_MVS_MODE_NOT_AVAIL,
+ AUDIO_MVS_MODE_INIT,
+ AUDIO_MVS_MODE_READY
+};
+
+enum audio_mvs_pkt_status_type {
+ AUDIO_MVS_PKT_NORMAL,
+ AUDIO_MVS_PKT_FAST,
+ AUDIO_MVS_PKT_SLOW
+};
+
+/* Parameters required for MVS acquire. */
+struct rpc_audio_mvs_acquire_args {
+ uint32_t client_id;
+ uint32_t cb_func_id;
+};
+
+struct audio_mvs_acquire_msg {
+ struct rpc_request_hdr rpc_hdr;
+ struct rpc_audio_mvs_acquire_args acquire_args;
+};
+
+/* Parameters required for MVS enable. */
+struct rpc_audio_mvs_enable_args {
+ uint32_t client_id;
+ uint32_t mode;
+ uint32_t ul_cb_func_id;
+ uint32_t dl_cb_func_id;
+ uint32_t context;
+};
+
+struct audio_mvs_enable_msg {
+ struct rpc_request_hdr rpc_hdr;
+ struct rpc_audio_mvs_enable_args enable_args;
+};
+
+/* Parameters required for MVS release. */
+struct audio_mvs_release_msg {
+ struct rpc_request_hdr rpc_hdr;
+ uint32_t client_id;
+};
+
+/* Parameters required for setting AMR mode. */
+struct audio_mvs_set_amr_mode_msg {
+ struct rpc_request_hdr rpc_hdr;
+ uint32_t amr_mode;
+};
+
+/* Parameters required for setting DTX. */
+struct audio_mvs_set_dtx_mode_msg {
+ struct rpc_request_hdr rpc_hdr;
+ uint32_t dtx_mode;
+};
+
+/* Parameters required for setting EVRC mode. */
+struct audio_mvs_set_voc_mode_msg {
+ struct rpc_request_hdr rpc_hdr;
+ uint32_t max_rate;
+ uint32_t min_rate;
+};
+
+/* Parameters for G711 mode */
+struct audio_mvs_set_g711_mode_msg {
+ struct rpc_request_hdr rpc_hdr;
+ uint32_t g711_mode;
+};
+
+/* Parameters for G729 mode */
+struct audio_mvs_set_g729_mode_msg {
+ struct rpc_request_hdr rpc_hdr;
+ uint32_t g729_mode;
+};
+
+/* Parameters for G722 mode */
+struct audio_mvs_set_g722_mode_msg {
+ struct rpc_request_hdr rpc_hdr;
+ uint32_t g722_mode;
+};
+
+
+/* Parameters for G711A mode */
+struct audio_mvs_set_g711A_mode_msg {
+ struct rpc_request_hdr rpc_hdr;
+ uint32_t g711A_mode;
+};
+
+/* Parameters for EFR FR and HR mode */
+struct audio_mvs_set_efr_mode_msg {
+ struct rpc_request_hdr rpc_hdr;
+ uint32_t efr_mode;
+};
+
+union audio_mvs_event_data {
+ struct mvs_ev_command_type {
+ uint32_t event;
+ uint32_t client_id;
+ uint32_t cmd_status;
+ } mvs_ev_command_type;
+
+ struct mvs_ev_mode_type {
+ uint32_t event;
+ uint32_t client_id;
+ uint32_t mode_status;
+ uint32_t mode;
+ } mvs_ev_mode_type;
+
+ struct mvs_ev_notify_type {
+ uint32_t event;
+ uint32_t client_id;
+ uint32_t buf_dir;
+ uint32_t max_frames;
+ } mvs_ev_notify_type;
+};
+
+struct audio_mvs_cb_func_args {
+ uint32_t cb_func_id;
+ uint32_t valid_ptr;
+ uint32_t event;
+ union audio_mvs_event_data event_data;
+};
+
+struct audio_mvs_frame_info_hdr {
+ uint32_t frame_mode;
+ uint32_t mvs_mode;
+ uint16_t buf_free_cnt;
+};
+
+struct audio_mvs_ul_reply {
+ struct rpc_reply_hdr reply_hdr;
+ uint32_t valid_pkt_status_ptr;
+ uint32_t pkt_status;
+};
+
+struct audio_mvs_dl_cb_func_args {
+ uint32_t cb_func_id;
+
+ uint32_t valid_ptr;
+ uint32_t frame_mode;
+ uint32_t frame_mode_ignore;
+
+ struct audio_mvs_frame_info_hdr frame_info_hdr;
+
+ uint32_t amr_frame;
+ uint32_t amr_mode;
+};
+/*general codec parameters includes AMR, G711A, PCM
+G729, VOC and HR vocoders
+*/
+struct gnr_cdc_param {
+ uint32_t param1;
+ uint32_t param2;
+ uint32_t valid_pkt_status_ptr;
+ uint32_t pkt_status;
+};
+/*G711 codec parameter*/
+struct g711_param {
+ uint32_t param1;
+ uint32_t valid_pkt_status_ptr;
+ uint32_t pkt_status;
+};
+
+union codec_param {
+ struct gnr_cdc_param gnr_arg;
+ struct g711_param g711_arg;
+};
+
+struct audio_mvs_dl_reply {
+ struct rpc_reply_hdr reply_hdr;
+
+ uint32_t voc_pkt[MVS_MAX_VOC_PKT_SIZE/4];
+
+ uint32_t valid_frame_info_ptr;
+ uint32_t frame_mode;
+ uint32_t frame_mode_again;
+
+ struct audio_mvs_frame_info_hdr frame_info_hdr;
+ union codec_param cdc_param;
+};
+
+struct audio_mvs_buf_node {
+ struct list_head list;
+ struct msm_audio_mvs_frame frame;
+};
+
+/* Each buffer is 20 ms, queue holds 200 ms of data. */
+#define MVS_MAX_Q_LEN 10
+
+struct audio_mvs_info_type {
+ enum audio_mvs_state_type state;
+ uint32_t frame_mode;
+ uint32_t mvs_mode;
+ uint32_t buf_free_cnt;
+ uint32_t rate_type;
+ uint32_t dtx_mode;
+
+ struct msm_rpc_endpoint *rpc_endpt;
+ uint32_t rpc_prog;
+ uint32_t rpc_ver;
+ uint32_t rpc_status;
+
+ uint8_t *mem_chunk;
+
+ struct list_head in_queue;
+ struct list_head free_in_queue;
+
+ struct list_head out_queue;
+ struct list_head free_out_queue;
+
+ struct task_struct *task;
+
+ wait_queue_head_t wait;
+ wait_queue_head_t mode_wait;
+ wait_queue_head_t out_wait;
+
+ struct mutex lock;
+ struct mutex in_lock;
+ struct mutex out_lock;
+
+ struct wake_lock suspend_lock;
+ struct wake_lock idle_lock;
+};
+
+static struct audio_mvs_info_type audio_mvs_info;
+
+static int audio_mvs_setup_mode(struct audio_mvs_info_type *audio)
+{
+ int rc = 0;
+
+ pr_debug("%s:\n", __func__);
+
+ switch (audio->mvs_mode) {
+ case MVS_MODE_AMR:
+ case MVS_MODE_AMR_WB: {
+ struct audio_mvs_set_amr_mode_msg set_amr_mode_msg;
+ struct audio_mvs_set_dtx_mode_msg set_dtx_mode_msg;
+
+ /* Set AMR mode. */
+ memset(&set_amr_mode_msg, 0, sizeof(set_amr_mode_msg));
+ set_amr_mode_msg.amr_mode = cpu_to_be32(audio->rate_type);
+
+ if (audio->mvs_mode == MVS_MODE_AMR) {
+ msm_rpc_setup_req(&set_amr_mode_msg.rpc_hdr,
+ audio->rpc_prog,
+ audio->rpc_ver,
+ MVS_AMR_SET_AMR_MODE_PROC);
+ } else {
+ msm_rpc_setup_req(&set_amr_mode_msg.rpc_hdr,
+ audio->rpc_prog,
+ audio->rpc_ver,
+ MVS_AMR_SET_AWB_MODE_PROC);
+ }
+
+ audio->rpc_status = RPC_STATUS_FAILURE;
+ rc = msm_rpc_write(audio->rpc_endpt,
+ &set_amr_mode_msg,
+ sizeof(set_amr_mode_msg));
+
+ if (rc >= 0) {
+ pr_debug("%s: RPC write for set amr mode done\n",
+ __func__);
+
+ /* Save the MVS configuration information. */
+ audio->frame_mode = MVS_FRAME_MODE_AMR_DL;
+
+ /* Disable DTX. */
+ memset(&set_dtx_mode_msg, 0, sizeof(set_dtx_mode_msg));
+ set_dtx_mode_msg.dtx_mode = cpu_to_be32(0);
+
+ msm_rpc_setup_req(&set_dtx_mode_msg.rpc_hdr,
+ audio->rpc_prog,
+ audio->rpc_ver,
+ MVS_SET_DTX_MODE_PROC);
+
+ audio->rpc_status = RPC_STATUS_FAILURE;
+ rc = msm_rpc_write(audio->rpc_endpt,
+ &set_dtx_mode_msg,
+ sizeof(set_dtx_mode_msg));
+
+ if (rc >= 0) {
+ pr_debug("%s: RPC write for set dtx done\n",
+ __func__);
+
+ rc = 0;
+ }
+ } else {
+ pr_err("%s: RPC write for set amr mode failed %d\n",
+ __func__, rc);
+ }
+ break;
+ }
+ case MVS_MODE_PCM:
+ case MVS_MODE_LINEAR_PCM: {
+ /* PCM does not have any params to be set.
+ Save the MVS configuration information. */
+ audio->rate_type = MVS_AMR_MODE_UNDEF;
+ audio->frame_mode = MVS_FRAME_MODE_PCM_DL;
+ break;
+ }
+ case MVS_MODE_IS127:
+ case MVS_MODE_IS733:
+ case MVS_MODE_4GV_NB:
+ case MVS_MODE_4GV_WB: {
+ struct audio_mvs_set_voc_mode_msg set_voc_mode_msg;
+
+ /* Set EVRC mode. */
+ memset(&set_voc_mode_msg, 0, sizeof(set_voc_mode_msg));
+ set_voc_mode_msg.min_rate = cpu_to_be32(audio->rate_type);
+ set_voc_mode_msg.max_rate = cpu_to_be32(audio->rate_type);
+
+ msm_rpc_setup_req(&set_voc_mode_msg.rpc_hdr,
+ audio->rpc_prog,
+ audio->rpc_ver,
+ MVS_VOC_SET_FRAME_RATE_PROC);
+
+ audio->rpc_status = RPC_STATUS_FAILURE;
+ rc = msm_rpc_write(audio->rpc_endpt,
+ &set_voc_mode_msg,
+ sizeof(set_voc_mode_msg));
+
+ if (rc >= 0) {
+ pr_debug("%s: RPC write for set voc mode done\n",
+ __func__);
+
+ /* Save the MVS configuration information. */
+ audio->frame_mode = MVS_FRAME_MODE_VOC_RX;
+
+ rc = 0;
+ } else {
+ pr_err("%s: RPC write for set voc mode failed %d\n",
+ __func__, rc);
+ }
+ break;
+ }
+ case MVS_MODE_G711: {
+ struct audio_mvs_set_g711_mode_msg set_g711_mode_msg;
+
+ /* Set G711 mode. */
+ memset(&set_g711_mode_msg, 0, sizeof(set_g711_mode_msg));
+ set_g711_mode_msg.g711_mode = cpu_to_be32(audio->rate_type);
+
+ pr_debug("%s: mode of g711:%d\n",
+ __func__, set_g711_mode_msg.g711_mode);
+
+ msm_rpc_setup_req(&set_g711_mode_msg.rpc_hdr,
+ audio->rpc_prog,
+ audio->rpc_ver,
+ MVS_G711_SET_MODE_PROC);
+
+ audio->rpc_status = RPC_STATUS_FAILURE;
+ rc = msm_rpc_write(audio->rpc_endpt,
+ &set_g711_mode_msg,
+ sizeof(set_g711_mode_msg));
+
+ if (rc >= 0) {
+ pr_debug("%s: RPC write for set g711 mode done\n",
+ __func__);
+ /* Save the MVS configuration information. */
+ audio->frame_mode = MVS_FRAME_MODE_G711_DL;
+
+ rc = 0;
+ } else {
+ pr_err("%s: RPC write for set g711 mode failed %d\n",
+ __func__, rc);
+ }
+ break;
+ }
+ case MVS_MODE_G729A: {
+ struct audio_mvs_set_g729_mode_msg set_g729_mode_msg;
+
+ /* Set G729 mode. */
+ memset(&set_g729_mode_msg, 0, sizeof(set_g729_mode_msg));
+ set_g729_mode_msg.g729_mode = cpu_to_be32(audio->dtx_mode);
+
+ pr_debug("%s: mode of g729:%d\n",
+ __func__, set_g729_mode_msg.g729_mode);
+
+ msm_rpc_setup_req(&set_g729_mode_msg.rpc_hdr,
+ audio->rpc_prog,
+ audio->rpc_ver,
+ MVS_G729A_SET_MODE_PROC);
+
+ audio->rpc_status = RPC_STATUS_FAILURE;
+ rc = msm_rpc_write(audio->rpc_endpt,
+ &set_g729_mode_msg,
+ sizeof(set_g729_mode_msg));
+
+ if (rc >= 0) {
+ pr_debug("%s: RPC write for set g729 mode done\n",
+ __func__);
+
+ /* Save the MVS configuration information. */
+ audio->frame_mode = MVS_FRAME_MODE_G729A_DL;
+
+ rc = 0;
+ } else {
+ pr_err("%s: RPC write for set g729 mode failed %d\n",
+ __func__, rc);
+ }
+ break;
+ }
+ case MVS_MODE_G722: {
+ struct audio_mvs_set_g722_mode_msg set_g722_mode_msg;
+
+ /* Set G722 mode. */
+ memset(&set_g722_mode_msg, 0, sizeof(set_g722_mode_msg));
+ set_g722_mode_msg.g722_mode = cpu_to_be32(audio->rate_type);
+
+ pr_debug("%s: mode of g722:%d\n",
+ __func__, set_g722_mode_msg.g722_mode);
+
+ msm_rpc_setup_req(&set_g722_mode_msg.rpc_hdr,
+ audio->rpc_prog,
+ audio->rpc_ver,
+ MVS_G722_SET_MODE_PROC);
+
+ audio->rpc_status = RPC_STATUS_FAILURE;
+ rc = msm_rpc_write(audio->rpc_endpt,
+ &set_g722_mode_msg,
+ sizeof(set_g722_mode_msg));
+
+ if (rc >= 0) {
+ pr_debug("%s: RPC write for set g722 mode done\n",
+ __func__);
+
+ /* Save the MVS configuration information. */
+ audio->frame_mode = MVS_FRAME_MODE_G722_DL;
+
+ rc = 0;
+ }
+ break;
+ }
+ case MVS_MODE_G711A: {
+ struct audio_mvs_set_g711A_mode_msg set_g711A_mode_msg;
+ struct audio_mvs_set_dtx_mode_msg set_dtx_mode_msg;
+
+ /* Set G711A mode. */
+ memset(&set_g711A_mode_msg, 0, sizeof(set_g711A_mode_msg));
+ set_g711A_mode_msg.g711A_mode = cpu_to_be32(audio->rate_type);
+
+ pr_debug("%s: mode of g711A:%d\n",
+ __func__, set_g711A_mode_msg.g711A_mode);
+
+ msm_rpc_setup_req(&set_g711A_mode_msg.rpc_hdr,
+ audio->rpc_prog,
+ audio->rpc_ver,
+ MVS_G711A_SET_MODE_PROC);
+
+ audio->rpc_status = RPC_STATUS_FAILURE;
+ rc = msm_rpc_write(audio->rpc_endpt,
+ &set_g711A_mode_msg,
+ sizeof(set_g711A_mode_msg));
+
+ if (rc >= 0) {
+ pr_debug("%s: RPC write for set g711A mode done\n",
+ __func__);
+
+ /* Save the MVS configuration information. */
+ audio->frame_mode = MVS_FRAME_MODE_G711A_DL;
+ /* Set DTX MODE. */
+ memset(&set_dtx_mode_msg, 0, sizeof(set_dtx_mode_msg));
+ set_dtx_mode_msg.dtx_mode =
+ cpu_to_be32((audio->dtx_mode));
+
+ msm_rpc_setup_req(&set_dtx_mode_msg.rpc_hdr,
+ audio->rpc_prog,
+ audio->rpc_ver,
+ MVS_SET_DTX_MODE_PROC);
+
+ audio->rpc_status = RPC_STATUS_FAILURE;
+ rc = msm_rpc_write(audio->rpc_endpt,
+ &set_dtx_mode_msg,
+ sizeof(set_dtx_mode_msg));
+
+ if (rc >= 0) {
+ pr_debug("%s: RPC write for set dtx done\n",
+ __func__);
+
+ rc = 0;
+ }
+ rc = 0;
+ } else {
+ pr_err("%s: RPC write for set g711A mode failed %d\n",
+ __func__, rc);
+ }
+ break;
+ }
+ case MVS_MODE_EFR:
+ case MVS_MODE_FR:
+ case MVS_MODE_HR: {
+ struct audio_mvs_set_efr_mode_msg set_efr_mode_msg;
+
+ /* Set G729 mode. */
+ memset(&set_efr_mode_msg, 0, sizeof(set_efr_mode_msg));
+ set_efr_mode_msg.efr_mode = cpu_to_be32(audio->dtx_mode);
+
+ pr_debug("%s: mode of EFR, FR and HR:%d\n",
+ __func__, set_efr_mode_msg.efr_mode);
+
+ msm_rpc_setup_req(&set_efr_mode_msg.rpc_hdr,
+ audio->rpc_prog,
+ audio->rpc_ver,
+ MVS_GSM_SET_DTX_MODE_PROC);
+
+ audio->rpc_status = RPC_STATUS_FAILURE;
+ rc = msm_rpc_write(audio->rpc_endpt,
+ &set_efr_mode_msg,
+ sizeof(set_efr_mode_msg));
+
+ if (rc >= 0) {
+ pr_debug("%s: RPC write for set EFR, FR and HR mode done\n",
+ __func__);
+
+ /* Save the MVS configuration information. */
+ if ((audio->mvs_mode == MVS_MODE_EFR) ||
+ (audio->mvs_mode == MVS_MODE_FR))
+ audio->frame_mode = MVS_FRAME_MODE_GSM_DL;
+ if (audio->mvs_mode == MVS_MODE_HR)
+ audio->frame_mode = MVS_FRAME_MODE_HR_DL;
+
+ rc = 0;
+ } else {
+ pr_err("%s: RPC write for set EFR, FR and HR mode failed %d\n",
+ __func__, rc);
+ }
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ pr_err("Default case\n");
+ }
+ return rc;
+}
+
+static int audio_mvs_setup(struct audio_mvs_info_type *audio)
+{
+ int rc = 0;
+ struct audio_mvs_enable_msg enable_msg;
+
+ pr_debug("%s:\n", __func__);
+
+ /* Enable MVS. */
+ memset(&enable_msg, 0, sizeof(enable_msg));
+ enable_msg.enable_args.client_id = cpu_to_be32(MVS_CLIENT_ID_VOIP);
+ enable_msg.enable_args.mode = cpu_to_be32(audio->mvs_mode);
+ enable_msg.enable_args.ul_cb_func_id = cpu_to_be32(MVS_UL_CB_FUNC_ID);
+ enable_msg.enable_args.dl_cb_func_id = cpu_to_be32(MVS_DL_CB_FUNC_ID);
+ enable_msg.enable_args.context = cpu_to_be32(MVS_PKT_CONTEXT_ISR);
+
+ msm_rpc_setup_req(&enable_msg.rpc_hdr,
+ audio->rpc_prog,
+ audio->rpc_ver,
+ MVS_ENABLE_PROC);
+
+ audio->rpc_status = RPC_STATUS_FAILURE;
+ rc = msm_rpc_write(audio->rpc_endpt, &enable_msg, sizeof(enable_msg));
+
+ if (rc >= 0) {
+ pr_debug("%s: RPC write for enable done\n", __func__);
+
+ rc = wait_event_timeout(audio->mode_wait,
+ (audio->rpc_status != RPC_STATUS_FAILURE),
+ 10 * HZ);
+
+ if (rc > 0) {
+ pr_debug("%s: Wait event for enable succeeded\n",
+ __func__);
+ rc = audio_mvs_setup_mode(audio);
+ if (rc < 0) {
+ pr_err("%s: Unknown MVS mode %d\n",
+ __func__, audio->mvs_mode);
+ }
+ pr_err("rc value after mode setup: %d\n", rc);
+ } else {
+ pr_err("%s: Wait event for enable failed %d\n",
+ __func__, rc);
+ }
+ } else {
+ pr_err("%s: RPC write for enable failed %d\n", __func__, rc);
+ }
+
+ return rc;
+}
+
+static int audio_mvs_start(struct audio_mvs_info_type *audio)
+{
+ int rc = 0;
+ struct audio_mvs_acquire_msg acquire_msg;
+
+ pr_info("%s:\n", __func__);
+
+ /* Prevent sleep. */
+ wake_lock(&audio->suspend_lock);
+ wake_lock(&audio->idle_lock);
+
+ /* Acquire MVS. */
+ memset(&acquire_msg, 0, sizeof(acquire_msg));
+ acquire_msg.acquire_args.client_id = cpu_to_be32(MVS_CLIENT_ID_VOIP);
+ acquire_msg.acquire_args.cb_func_id = cpu_to_be32(MVS_CB_FUNC_ID);
+
+ msm_rpc_setup_req(&acquire_msg.rpc_hdr,
+ audio->rpc_prog,
+ audio->rpc_ver,
+ MVS_ACQUIRE_PROC);
+
+ audio->rpc_status = RPC_STATUS_FAILURE;
+ rc = msm_rpc_write(audio->rpc_endpt,
+ &acquire_msg,
+ sizeof(acquire_msg));
+
+ if (rc >= 0) {
+ pr_debug("%s: RPC write for acquire done\n", __func__);
+
+ rc = wait_event_timeout(audio->wait,
+ (audio->rpc_status != RPC_STATUS_FAILURE),
+ 1 * HZ);
+
+ if (rc > 0) {
+
+ rc = audio_mvs_setup(audio);
+
+ if (rc == 0)
+ audio->state = AUDIO_MVS_STARTED;
+
+ } else {
+ pr_err("%s: Wait event for acquire failed %d\n",
+ __func__, rc);
+
+ rc = -EBUSY;
+ }
+ } else {
+ pr_err("%s: RPC write for acquire failed %d\n", __func__, rc);
+
+ rc = -EBUSY;
+ }
+
+ return rc;
+}
+
+static int audio_mvs_stop(struct audio_mvs_info_type *audio)
+{
+ int rc = 0;
+ struct audio_mvs_release_msg release_msg;
+
+ pr_info("%s:\n", __func__);
+
+ /* Release MVS. */
+ memset(&release_msg, 0, sizeof(release_msg));
+ release_msg.client_id = cpu_to_be32(MVS_CLIENT_ID_VOIP);
+
+ msm_rpc_setup_req(&release_msg.rpc_hdr,
+ audio->rpc_prog,
+ audio->rpc_ver,
+ MVS_RELEASE_PROC);
+
+ audio->rpc_status = RPC_STATUS_FAILURE;
+ rc = msm_rpc_write(audio->rpc_endpt, &release_msg, sizeof(release_msg));
+
+ if (rc >= 0) {
+ pr_debug("%s: RPC write for release done\n", __func__);
+
+ rc = wait_event_timeout(audio->mode_wait,
+ (audio->rpc_status != RPC_STATUS_FAILURE),
+ 1 * HZ);
+
+ if (rc > 0) {
+ pr_debug("%s: Wait event for release succeeded\n",
+ __func__);
+
+ audio->state = AUDIO_MVS_STOPPED;
+
+ /* Un-block read in case it is waiting for data. */
+ wake_up(&audio->out_wait);
+ rc = 0;
+ } else {
+ pr_err("%s: Wait event for release failed %d\n",
+ __func__, rc);
+ }
+ } else {
+ pr_err("%s: RPC write for release failed %d\n", __func__, rc);
+ }
+
+ /* Allow sleep. */
+ wake_unlock(&audio->suspend_lock);
+ wake_unlock(&audio->idle_lock);
+
+ return rc;
+}
+
+static void audio_mvs_process_rpc_request(uint32_t procedure,
+ uint32_t xid,
+ void *data,
+ uint32_t length,
+ struct audio_mvs_info_type *audio)
+{
+ int rc = 0;
+
+ pr_debug("%s:\n", __func__);
+
+ switch (procedure) {
+ case MVS_EVENT_CB_TYPE_PROC: {
+ struct audio_mvs_cb_func_args *args = data;
+ struct rpc_reply_hdr reply_hdr;
+
+ pr_debug("%s: MVS CB CB_FUNC_ID 0x%x\n",
+ __func__, be32_to_cpu(args->cb_func_id));
+
+ if (be32_to_cpu(args->valid_ptr)) {
+ uint32_t event_type = be32_to_cpu(args->event);
+
+ pr_debug("%s: MVS CB event type %d\n",
+ __func__, be32_to_cpu(args->event));
+
+ if (event_type == AUDIO_MVS_COMMAND) {
+ uint32_t cmd_status = be32_to_cpu(
+ args->event_data.mvs_ev_command_type.cmd_status);
+
+ pr_debug("%s: MVS CB command status %d\n",
+ __func__, cmd_status);
+
+ if (cmd_status == AUDIO_MVS_CMD_SUCCESS) {
+ audio->rpc_status = RPC_STATUS_SUCCESS;
+ wake_up(&audio->wait);
+ }
+
+ } else if (event_type == AUDIO_MVS_MODE) {
+ uint32_t mode_status = be32_to_cpu(
+ args->event_data.mvs_ev_mode_type.mode_status);
+
+ pr_debug("%s: MVS CB mode status %d\n",
+ __func__, mode_status);
+
+ if (mode_status == AUDIO_MVS_MODE_READY) {
+ audio->rpc_status = RPC_STATUS_SUCCESS;
+ wake_up(&audio->mode_wait);
+ }
+ } else {
+ pr_err("%s: MVS CB unknown event type %d\n",
+ __func__, event_type);
+ }
+ } else {
+ pr_err("%s: MVS CB event pointer not valid\n",
+ __func__);
+ }
+
+ /* Send ack to modem. */
+ memset(&reply_hdr, 0, sizeof(reply_hdr));
+ reply_hdr.xid = cpu_to_be32(xid);
+ reply_hdr.type = cpu_to_be32(RPC_TYPE_REPLY);
+ reply_hdr.reply_stat = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED);
+
+ reply_hdr.data.acc_hdr.accept_stat = cpu_to_be32(
+ RPC_ACCEPTSTAT_SUCCESS);
+ reply_hdr.data.acc_hdr.verf_flavor = 0;
+ reply_hdr.data.acc_hdr.verf_length = 0;
+
+ rc = msm_rpc_write(audio->rpc_endpt,
+ &reply_hdr,
+ sizeof(reply_hdr));
+
+ if (rc < 0)
+ pr_err("%s: RPC write for response failed %d\n",
+ __func__, rc);
+
+ break;
+ }
+
+ case MVS_PACKET_UL_FN_TYPE_PROC: {
+ uint32_t *args = data;
+ uint32_t pkt_len;
+ uint32_t frame_mode;
+ struct audio_mvs_ul_reply ul_reply;
+ struct audio_mvs_buf_node *buf_node = NULL;
+
+ pr_debug("%s: MVS UL CB_FUNC_ID 0x%x\n",
+ __func__, be32_to_cpu(*args));
+ args++;
+
+ pkt_len = be32_to_cpu(*args);
+ pr_debug("%s: UL pkt_len %d\n", __func__, pkt_len);
+ args++;
+
+ /* Copy the vocoder packets. */
+ mutex_lock(&audio->out_lock);
+
+ if (!list_empty(&audio->free_out_queue)) {
+ buf_node = list_first_entry(&audio->free_out_queue,
+ struct audio_mvs_buf_node,
+ list);
+ list_del(&buf_node->list);
+
+ memcpy(&buf_node->frame.voc_pkt[0], args, pkt_len);
+ buf_node->frame.len = pkt_len;
+ pkt_len = ALIGN(pkt_len, 4);
+ args = args + pkt_len/4;
+
+ pr_debug("%s: UL valid_ptr 0x%x\n",
+ __func__, be32_to_cpu(*args));
+ args++;
+
+ frame_mode = be32_to_cpu(*args);
+ pr_debug("%s: UL frame_mode %d\n",
+ __func__, frame_mode);
+ args++;
+
+ pr_debug("%s: UL frame_mode %d\n",
+ __func__, be32_to_cpu(*args));
+ args++;
+
+ pr_debug("%s: UL frame_mode %d\n",
+ __func__, be32_to_cpu(*args));
+ args++;
+
+ pr_debug("%s: UL mvs_mode %d\n",
+ __func__, be32_to_cpu(*args));
+ args++;
+
+ pr_debug("%s: UL buf_free_cnt %d\n",
+ __func__, be32_to_cpu(*args));
+ args++;
+
+ if (frame_mode == MVS_FRAME_MODE_AMR_UL) {
+ /* Extract AMR frame type. */
+ buf_node->frame.frame_type = be32_to_cpu(*args);
+
+ pr_debug("%s: UL AMR frame_type %d\n",
+ __func__, be32_to_cpu(*args));
+ } else if ((frame_mode == MVS_FRAME_MODE_PCM_UL) ||
+ (frame_mode == MVS_FRAME_MODE_VOC_TX)) {
+ /* PCM and EVRC don't have frame_type */
+ buf_node->frame.frame_type = 0;
+ } else if (frame_mode == MVS_FRAME_MODE_G711_UL) {
+ /* Extract G711 frame type. */
+ buf_node->frame.frame_type = be32_to_cpu(*args);
+
+ pr_debug("%s: UL G711 frame_type %d\n",
+ __func__, be32_to_cpu(*args));
+ } else if (frame_mode == MVS_FRAME_MODE_G729A_UL) {
+ /* Extract G729 frame type. */
+ buf_node->frame.frame_type = be32_to_cpu(*args);
+
+ pr_debug("%s: UL G729 frame_type %d\n",
+ __func__, be32_to_cpu(*args));
+ } else if (frame_mode == MVS_FRAME_MODE_G722_UL) {
+ /* Extract G722 frame type. */
+ buf_node->frame.frame_type = be32_to_cpu(*args);
+
+ pr_debug("%s: UL G722 frame_type %d\n",
+ __func__, be32_to_cpu(*args));
+ } else if (frame_mode == MVS_FRAME_MODE_G711A_UL) {
+ /* Extract G711A frame type. */
+ buf_node->frame.frame_type = be32_to_cpu(*args);
+
+ pr_debug("%s: UL G711A frame_type %d\n",
+ __func__, be32_to_cpu(*args));
+ } else if ((frame_mode == MVS_FRAME_MODE_GSM_UL) ||
+ (frame_mode == MVS_FRAME_MODE_HR_UL)) {
+ /* Extract EFR, FR and HR frame type. */
+ buf_node->frame.frame_type = be32_to_cpu(*args);
+
+ pr_debug("%s: UL EFR,FR,HR frame_type %d\n",
+ __func__, be32_to_cpu(*args));
+ } else {
+ pr_debug("%s: UL Unknown frame mode %d\n",
+ __func__, frame_mode);
+ }
+
+ list_add_tail(&buf_node->list, &audio->out_queue);
+ } else {
+ pr_err("%s: UL data dropped, read is slow\n", __func__);
+ }
+
+ mutex_unlock(&audio->out_lock);
+
+ wake_up(&audio->out_wait);
+
+ /* Send UL message accept to modem. */
+ memset(&ul_reply, 0, sizeof(ul_reply));
+ ul_reply.reply_hdr.xid = cpu_to_be32(xid);
+ ul_reply.reply_hdr.type = cpu_to_be32(RPC_TYPE_REPLY);
+ ul_reply.reply_hdr.reply_stat = cpu_to_be32(
+ RPCMSG_REPLYSTAT_ACCEPTED);
+
+ ul_reply.reply_hdr.data.acc_hdr.accept_stat = cpu_to_be32(
+ RPC_ACCEPTSTAT_SUCCESS);
+ ul_reply.reply_hdr.data.acc_hdr.verf_flavor = 0;
+ ul_reply.reply_hdr.data.acc_hdr.verf_length = 0;
+
+ ul_reply.valid_pkt_status_ptr = cpu_to_be32(0x00000001);
+ ul_reply.pkt_status = cpu_to_be32(0x00000000);
+
+ rc = msm_rpc_write(audio->rpc_endpt,
+ &ul_reply,
+ sizeof(ul_reply));
+
+ if (rc < 0)
+ pr_err("%s: RPC write for UL response failed %d\n",
+ __func__, rc);
+
+ break;
+ }
+
+ case MVS_PACKET_DL_FN_TYPE_PROC: {
+ struct audio_mvs_dl_cb_func_args *args = data;
+ struct audio_mvs_dl_reply dl_reply;
+ uint32_t frame_mode;
+ struct audio_mvs_buf_node *buf_node = NULL;
+
+ pr_debug("%s: MVS DL CB CB_FUNC_ID 0x%x\n",
+ __func__, be32_to_cpu(args->cb_func_id));
+
+ frame_mode = be32_to_cpu(args->frame_mode);
+ pr_debug("%s: DL frame_mode %d\n", __func__, frame_mode);
+
+ /* Prepare and send the DL packets to modem. */
+ memset(&dl_reply, 0, sizeof(dl_reply));
+ dl_reply.reply_hdr.xid = cpu_to_be32(xid);
+ dl_reply.reply_hdr.type = cpu_to_be32(RPC_TYPE_REPLY);
+ dl_reply.reply_hdr.reply_stat = cpu_to_be32(
+ RPCMSG_REPLYSTAT_ACCEPTED);
+
+ dl_reply.reply_hdr.data.acc_hdr.accept_stat = cpu_to_be32(
+ RPC_ACCEPTSTAT_SUCCESS);
+ dl_reply.reply_hdr.data.acc_hdr.verf_flavor = 0;
+ dl_reply.reply_hdr.data.acc_hdr.verf_length = 0;
+
+ mutex_lock(&audio->in_lock);
+
+ if (!list_empty(&audio->in_queue)) {
+ buf_node = list_first_entry(&audio->in_queue,
+ struct audio_mvs_buf_node,
+ list);
+ list_del(&buf_node->list);
+
+ memcpy(&dl_reply.voc_pkt,
+ &buf_node->frame.voc_pkt[0],
+ buf_node->frame.len);
+
+ pr_debug("%s:frame mode %d\n", __func__, frame_mode);
+ if (frame_mode == MVS_FRAME_MODE_AMR_DL) {
+ dl_reply.cdc_param.gnr_arg.param1 = cpu_to_be32(
+ buf_node->frame.frame_type);
+ dl_reply.cdc_param.gnr_arg.param2 =
+ cpu_to_be32(audio->rate_type);
+ dl_reply.cdc_param.\
+ gnr_arg.valid_pkt_status_ptr =
+ cpu_to_be32(0x00000001);
+ dl_reply.cdc_param.gnr_arg.pkt_status =
+ cpu_to_be32(AUDIO_MVS_PKT_NORMAL);
+ } else if (frame_mode == MVS_FRAME_MODE_PCM_DL) {
+ dl_reply.cdc_param.gnr_arg.param1 = 0;
+ dl_reply.cdc_param.gnr_arg.param2 = 0;
+ dl_reply.cdc_param.\
+ gnr_arg.valid_pkt_status_ptr =
+ cpu_to_be32(0x00000001);
+ dl_reply.cdc_param.gnr_arg.pkt_status =
+ cpu_to_be32(AUDIO_MVS_PKT_NORMAL);
+ } else if (frame_mode == MVS_FRAME_MODE_VOC_RX) {
+ dl_reply.cdc_param.gnr_arg.param1 =
+ cpu_to_be32(audio->rate_type);
+ dl_reply.cdc_param.gnr_arg.param2 = 0;
+ dl_reply.cdc_param.\
+ gnr_arg.valid_pkt_status_ptr =
+ cpu_to_be32(0x00000001);
+ dl_reply.cdc_param.gnr_arg.pkt_status =
+ cpu_to_be32(AUDIO_MVS_PKT_NORMAL);
+ } else if (frame_mode == MVS_FRAME_MODE_G711_DL) {
+ dl_reply.cdc_param.g711_arg.param1 =
+ cpu_to_be32(buf_node->frame.frame_type);
+ dl_reply.cdc_param.\
+ g711_arg.valid_pkt_status_ptr =
+ cpu_to_be32(0x00000001);
+ dl_reply.cdc_param.g711_arg.pkt_status =
+ cpu_to_be32(AUDIO_MVS_PKT_NORMAL);
+ } else if (frame_mode == MVS_FRAME_MODE_G729A_DL) {
+ dl_reply.cdc_param.gnr_arg.param1 = cpu_to_be32(
+ buf_node->frame.frame_type);
+ dl_reply.cdc_param.gnr_arg.param2 =
+ cpu_to_be32(audio->rate_type);
+ dl_reply.cdc_param.\
+ gnr_arg.valid_pkt_status_ptr =
+ cpu_to_be32(0x00000001);
+ dl_reply.cdc_param.gnr_arg.pkt_status =
+ cpu_to_be32(AUDIO_MVS_PKT_NORMAL);
+ } else if (frame_mode == MVS_FRAME_MODE_G722_DL) {
+ dl_reply.cdc_param.gnr_arg.param1 = cpu_to_be32(
+ buf_node->frame.frame_type);
+ dl_reply.cdc_param.gnr_arg.param2 =
+ cpu_to_be32(audio->rate_type);
+ dl_reply.cdc_param.\
+ gnr_arg.valid_pkt_status_ptr =
+ cpu_to_be32(0x00000001);
+ dl_reply.cdc_param.gnr_arg.pkt_status =
+ cpu_to_be32(AUDIO_MVS_PKT_NORMAL);
+ } else if (frame_mode == MVS_FRAME_MODE_G711A_DL) {
+ dl_reply.cdc_param.gnr_arg.param1 = cpu_to_be32(
+ buf_node->frame.frame_type);
+ dl_reply.cdc_param.gnr_arg.param2 =
+ cpu_to_be32(audio->rate_type);
+ dl_reply.cdc_param.\
+ gnr_arg.valid_pkt_status_ptr =
+ cpu_to_be32(0x00000001);
+ dl_reply.cdc_param.gnr_arg.pkt_status =
+ cpu_to_be32(AUDIO_MVS_PKT_NORMAL);
+ } else if ((frame_mode == MVS_FRAME_MODE_GSM_DL) ||
+ (frame_mode == MVS_FRAME_MODE_HR_DL)) {
+ dl_reply.cdc_param.gnr_arg.param1 = cpu_to_be32(
+ buf_node->frame.frame_type);
+ dl_reply.cdc_param.gnr_arg.param2 =
+ cpu_to_be32(audio->rate_type);
+ dl_reply.cdc_param.\
+ gnr_arg.valid_pkt_status_ptr =
+ cpu_to_be32(0x00000001);
+ dl_reply.cdc_param.gnr_arg.pkt_status =
+ cpu_to_be32(AUDIO_MVS_PKT_NORMAL);
+ } else {
+ pr_err("%s: DL Unknown frame mode %d\n",
+ __func__, frame_mode);
+ }
+ list_add_tail(&buf_node->list, &audio->free_in_queue);
+ } else {
+ pr_debug("%s: No DL data available to send to MVS\n",
+ __func__);
+ if (frame_mode == MVS_FRAME_MODE_G711_DL) {
+ dl_reply.cdc_param.\
+ g711_arg.valid_pkt_status_ptr =
+ cpu_to_be32(0x00000001);
+ dl_reply.cdc_param.g711_arg.pkt_status =
+ cpu_to_be32(AUDIO_MVS_PKT_SLOW);
+ } else {
+ dl_reply.cdc_param.\
+ gnr_arg.valid_pkt_status_ptr =
+ cpu_to_be32(0x00000001);
+ dl_reply.cdc_param.gnr_arg.pkt_status =
+ cpu_to_be32(AUDIO_MVS_PKT_SLOW);
+ }
+ }
+
+ mutex_unlock(&audio->in_lock);
+
+ dl_reply.valid_frame_info_ptr = cpu_to_be32(0x00000001);
+
+ dl_reply.frame_mode = cpu_to_be32(audio->frame_mode);
+ dl_reply.frame_mode_again = cpu_to_be32(audio->frame_mode);
+
+ dl_reply.frame_info_hdr.frame_mode =
+ cpu_to_be32(audio->frame_mode);
+ dl_reply.frame_info_hdr.mvs_mode = cpu_to_be32(audio->mvs_mode);
+ dl_reply.frame_info_hdr.buf_free_cnt = 0;
+
+ rc = msm_rpc_write(audio->rpc_endpt,
+ &dl_reply,
+ sizeof(dl_reply));
+
+ if (rc < 0)
+ pr_err("%s: RPC write for DL response failed %d\n",
+ __func__, rc);
+
+ break;
+ }
+
+ default:
+ pr_err("%s: Unknown CB type %d\n", __func__, procedure);
+ }
+}
+
+static int audio_mvs_thread(void *data)
+{
+ struct audio_mvs_info_type *audio = data;
+ struct rpc_request_hdr *rpc_hdr = NULL;
+
+ pr_info("%s:\n", __func__);
+
+ while (!kthread_should_stop()) {
+
+ int rpc_hdr_len = msm_rpc_read(audio->rpc_endpt,
+ (void **) &rpc_hdr,
+ -1,
+ -1);
+
+ if (rpc_hdr_len < 0) {
+ pr_err("%s: RPC read failed %d\n",
+ __func__, rpc_hdr_len);
+
+ break;
+ } else if (rpc_hdr_len < RPC_COMMON_HDR_SZ) {
+ continue;
+ } else {
+ uint32_t rpc_type = be32_to_cpu(rpc_hdr->type);
+ if (rpc_type == RPC_TYPE_REPLY) {
+ struct rpc_reply_hdr *rpc_reply =
+ (void *) rpc_hdr;
+ uint32_t reply_status;
+
+ if (rpc_hdr_len < RPC_REPLY_HDR_SZ)
+ continue;
+
+ reply_status =
+ be32_to_cpu(rpc_reply->reply_stat);
+
+ if (reply_status != RPCMSG_REPLYSTAT_ACCEPTED) {
+ /* If the command is not accepted, there
+ * will be no response callback. Wake
+ * the caller and report error. */
+ audio->rpc_status = RPC_STATUS_REJECT;
+
+ wake_up(&audio->wait);
+
+ pr_err("%s: RPC reply status denied\n",
+ __func__);
+ }
+ } else if (rpc_type == RPC_TYPE_REQUEST) {
+ if (rpc_hdr_len < RPC_REQUEST_HDR_SZ)
+ continue;
+
+ audio_mvs_process_rpc_request(
+ be32_to_cpu(rpc_hdr->procedure),
+ be32_to_cpu(rpc_hdr->xid),
+ (void *) (rpc_hdr + 1),
+ (rpc_hdr_len - sizeof(*rpc_hdr)),
+ audio);
+ } else {
+ pr_err("%s: Unexpected RPC type %d\n",
+ __func__, rpc_type);
+ }
+ }
+
+ kfree(rpc_hdr);
+ rpc_hdr = NULL;
+ }
+
+ pr_info("%s: MVS thread stopped\n", __func__);
+
+ return 0;
+}
+
+static int audio_mvs_alloc_buf(struct audio_mvs_info_type *audio)
+{
+ int i = 0;
+ struct audio_mvs_buf_node *buf_node = NULL;
+ struct list_head *ptr = NULL;
+ struct list_head *next = NULL;
+
+ pr_debug("%s:\n", __func__);
+
+ /* Allocate input buffers. */
+ for (i = 0; i < MVS_MAX_Q_LEN; i++) {
+ buf_node = kmalloc(sizeof(struct audio_mvs_buf_node),
+ GFP_KERNEL);
+
+ if (buf_node != NULL) {
+ list_add_tail(&buf_node->list,
+ &audio->free_in_queue);
+ } else {
+ pr_err("%s: No memory for IO buffers\n",
+ __func__);
+ goto err;
+ }
+ buf_node = NULL;
+ }
+
+ /* Allocate output buffers. */
+ for (i = 0; i < MVS_MAX_Q_LEN; i++) {
+ buf_node = kmalloc(sizeof(struct audio_mvs_buf_node),
+ GFP_KERNEL);
+
+ if (buf_node != NULL) {
+ list_add_tail(&buf_node->list,
+ &audio->free_out_queue);
+ } else {
+ pr_err("%s: No memory for IO buffers\n",
+ __func__);
+ goto err;
+ }
+ buf_node = NULL;
+ }
+
+ return 0;
+
+err:
+ list_for_each_safe(ptr, next, &audio->free_in_queue) {
+ buf_node = list_entry(ptr, struct audio_mvs_buf_node, list);
+ list_del(&buf_node->list);
+ kfree(buf_node);
+ buf_node = NULL;
+ }
+
+ ptr = next = NULL;
+ list_for_each_safe(ptr, next, &audio->free_out_queue) {
+ buf_node = list_entry(ptr, struct audio_mvs_buf_node, list);
+ list_del(&buf_node->list);
+ kfree(buf_node);
+ buf_node = NULL;
+ }
+
+ return -ENOMEM;
+}
+
+static void audio_mvs_free_buf(struct audio_mvs_info_type *audio)
+{
+ struct list_head *ptr = NULL;
+ struct list_head *next = NULL;
+ struct audio_mvs_buf_node *buf_node = NULL;
+
+ pr_debug("%s:\n", __func__);
+
+ mutex_lock(&audio->in_lock);
+ /* Free input buffers. */
+ list_for_each_safe(ptr, next, &audio->in_queue) {
+ buf_node = list_entry(ptr, struct audio_mvs_buf_node, list);
+ list_del(&buf_node->list);
+ kfree(buf_node);
+ buf_node = NULL;
+ }
+
+ ptr = next = NULL;
+ /* Free free_input buffers. */
+ list_for_each_safe(ptr, next, &audio->free_in_queue) {
+ buf_node = list_entry(ptr, struct audio_mvs_buf_node, list);
+ list_del(&buf_node->list);
+ kfree(buf_node);
+ buf_node = NULL;
+ }
+ mutex_unlock(&audio->in_lock);
+
+ mutex_lock(&audio->out_lock);
+ ptr = next = NULL;
+ /* Free output buffers. */
+ list_for_each_safe(ptr, next, &audio->out_queue) {
+ buf_node = list_entry(ptr, struct audio_mvs_buf_node, list);
+ list_del(&buf_node->list);
+ kfree(buf_node);
+ buf_node = NULL;
+ }
+
+ /* Free free_ioutput buffers. */
+ ptr = next = NULL;
+ list_for_each_safe(ptr, next, &audio->free_out_queue) {
+ buf_node = list_entry(ptr, struct audio_mvs_buf_node, list);
+ list_del(&buf_node->list);
+ kfree(buf_node);
+ buf_node = NULL;
+ }
+ mutex_unlock(&audio->out_lock);
+}
+
+static int audio_mvs_open(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+
+ pr_info("%s:\n", __func__);
+
+ mutex_lock(&audio_mvs_info.lock);
+
+ if (audio_mvs_info.state == AUDIO_MVS_CLOSED) {
+
+ if (audio_mvs_info.task != NULL ||
+ audio_mvs_info.rpc_endpt != NULL) {
+ rc = audio_mvs_alloc_buf(&audio_mvs_info);
+
+ if (rc == 0) {
+ audio_mvs_info.state = AUDIO_MVS_OPENED;
+ file->private_data = &audio_mvs_info;
+ }
+ } else {
+ pr_err("%s: MVS thread and RPC end point do not exist\n",
+ __func__);
+
+ rc = -ENODEV;
+ }
+ } else {
+ pr_err("%s: MVS driver exists, state %d\n",
+ __func__, audio_mvs_info.state);
+
+ rc = -EBUSY;
+ }
+
+ mutex_unlock(&audio_mvs_info.lock);
+
+ return rc;
+}
+
+static int audio_mvs_release(struct inode *inode, struct file *file)
+{
+
+ struct audio_mvs_info_type *audio = file->private_data;
+
+ pr_info("%s:\n", __func__);
+
+ mutex_lock(&audio->lock);
+ if (audio->state == AUDIO_MVS_STARTED)
+ audio_mvs_stop(audio);
+ audio_mvs_free_buf(audio);
+ audio->state = AUDIO_MVS_CLOSED;
+ mutex_unlock(&audio->lock);
+
+ pr_debug("%s: Release done\n", __func__);
+ return 0;
+}
+
+static ssize_t audio_mvs_read(struct file *file,
+ char __user *buf,
+ size_t count,
+ loff_t *pos)
+{
+ int rc = 0;
+ struct audio_mvs_buf_node *buf_node = NULL;
+ struct audio_mvs_info_type *audio = file->private_data;
+
+ pr_debug("%s:\n", __func__);
+
+ rc = wait_event_interruptible_timeout(audio->out_wait,
+ (!list_empty(&audio->out_queue) ||
+ audio->state == AUDIO_MVS_STOPPED),
+ 1 * HZ);
+
+ if (rc > 0) {
+ mutex_lock(&audio->out_lock);
+ if ((audio->state == AUDIO_MVS_STARTED) &&
+ (!list_empty(&audio->out_queue))) {
+
+ if (count >= sizeof(struct msm_audio_mvs_frame)) {
+ buf_node = list_first_entry(&audio->out_queue,
+ struct audio_mvs_buf_node,
+ list);
+ list_del(&buf_node->list);
+
+ rc = copy_to_user(buf,
+ &buf_node->frame,
+ sizeof(struct msm_audio_mvs_frame));
+
+ if (rc == 0) {
+ rc = buf_node->frame.len +
+ sizeof(buf_node->frame.frame_type) +
+ sizeof(buf_node->frame.len);
+ } else {
+ pr_err("%s: Copy to user retuned %d",
+ __func__, rc);
+
+ rc = -EFAULT;
+ }
+
+ list_add_tail(&buf_node->list,
+ &audio->free_out_queue);
+ } else {
+ pr_err("%s: Read count %d < sizeof(frame) %d",
+ __func__, count,
+ sizeof(struct msm_audio_mvs_frame));
+
+ rc = -ENOMEM;
+ }
+ } else {
+ pr_err("%s: Read performed in state %d\n",
+ __func__, audio->state);
+
+ rc = -EPERM;
+ }
+ mutex_unlock(&audio->out_lock);
+
+ } else if (rc == 0) {
+ pr_err("%s: No UL data available\n", __func__);
+
+ rc = -ETIMEDOUT;
+ } else {
+ pr_err("%s: Read was interrupted\n", __func__);
+
+ rc = -ERESTARTSYS;
+ }
+
+ return rc;
+}
+
+static ssize_t audio_mvs_write(struct file *file,
+ const char __user *buf,
+ size_t count,
+ loff_t *pos)
+{
+ int rc = 0;
+ struct audio_mvs_buf_node *buf_node = NULL;
+ struct audio_mvs_info_type *audio = file->private_data;
+
+ pr_debug("%s:\n", __func__);
+
+ mutex_lock(&audio->in_lock);
+ if (audio->state == AUDIO_MVS_STARTED) {
+ if (count <= sizeof(struct msm_audio_mvs_frame)) {
+ if (!list_empty(&audio->free_in_queue)) {
+ buf_node =
+ list_first_entry(&audio->free_in_queue,
+ struct audio_mvs_buf_node,
+ list);
+ list_del(&buf_node->list);
+
+ rc = copy_from_user(&buf_node->frame,
+ buf,
+ count);
+
+ list_add_tail(&buf_node->list,
+ &audio->in_queue);
+ } else {
+ pr_err("%s: No free DL buffs\n", __func__);
+ }
+ } else {
+ pr_err("%s: Write count %d < sizeof(frame) %d",
+ __func__, count,
+ sizeof(struct msm_audio_mvs_frame));
+
+ rc = -ENOMEM;
+ }
+ } else {
+ pr_err("%s: Write performed in invalid state %d\n",
+ __func__, audio->state);
+
+ rc = -EPERM;
+ }
+ mutex_unlock(&audio->in_lock);
+
+ return rc;
+}
+
+static long audio_mvs_ioctl(struct file *file,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ int rc = 0;
+
+ struct audio_mvs_info_type *audio = file->private_data;
+
+ pr_info("%s:\n", __func__);
+
+ switch (cmd) {
+ case AUDIO_GET_MVS_CONFIG: {
+ struct msm_audio_mvs_config config;
+
+ pr_debug("%s: IOCTL GET_MVS_CONFIG\n", __func__);
+
+ mutex_lock(&audio->lock);
+ config.mvs_mode = audio->mvs_mode;
+ config.rate_type = audio->rate_type;
+ mutex_unlock(&audio->lock);
+
+ rc = copy_to_user((void *)arg, &config, sizeof(config));
+ if (rc == 0)
+ rc = sizeof(config);
+ else
+ pr_err("%s: Config copy failed %d\n", __func__, rc);
+
+ break;
+ }
+
+ case AUDIO_SET_MVS_CONFIG: {
+ struct msm_audio_mvs_config config;
+
+ pr_debug("%s: IOCTL SET_MVS_CONFIG\n", __func__);
+
+ rc = copy_from_user(&config, (void *)arg, sizeof(config));
+ if (rc == 0) {
+ mutex_lock(&audio->lock);
+
+ if (audio->state == AUDIO_MVS_OPENED) {
+ audio->mvs_mode = config.mvs_mode;
+ audio->rate_type = config.rate_type;
+ audio->dtx_mode = config.dtx_mode;
+ } else {
+ pr_err("%s: Set confg called in state %d\n",
+ __func__, audio->state);
+
+ rc = -EPERM;
+ }
+
+ mutex_unlock(&audio->lock);
+ } else {
+ pr_err("%s: Config copy failed %d\n", __func__, rc);
+ }
+
+ break;
+ }
+
+ case AUDIO_START: {
+ pr_debug("%s: IOCTL START\n", __func__);
+
+ mutex_lock(&audio->lock);
+
+ if (audio->state == AUDIO_MVS_OPENED ||
+ audio->state == AUDIO_MVS_STOPPED) {
+ rc = audio_mvs_start(audio);
+
+ if (rc != 0)
+ audio_mvs_stop(audio);
+ } else {
+ pr_err("%s: Start called in invalid state %d\n",
+ __func__, audio->state);
+
+ rc = -EPERM;
+ }
+
+ mutex_unlock(&audio->lock);
+
+ break;
+ }
+
+ case AUDIO_STOP: {
+ pr_debug("%s: IOCTL STOP\n", __func__);
+
+ mutex_lock(&audio->lock);
+
+ if (audio->state == AUDIO_MVS_STARTED) {
+ rc = audio_mvs_stop(audio);
+ } else {
+ pr_err("%s: Stop called in invalid state %d\n",
+ __func__, audio->state);
+
+ rc = -EPERM;
+ }
+
+ mutex_unlock(&audio->lock);
+ break;
+ }
+
+ default: {
+ pr_err("%s: Unknown IOCTL %d\n", __func__, cmd);
+ }
+ }
+
+ return rc;
+}
+
+static const struct file_operations audio_mvs_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_mvs_open,
+ .release = audio_mvs_release,
+ .read = audio_mvs_read,
+ .write = audio_mvs_write,
+ .unlocked_ioctl = audio_mvs_ioctl
+};
+
+struct miscdevice audio_mvs_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_mvs",
+ .fops = &audio_mvs_fops
+};
+
+static int __init audio_mvs_init(void)
+{
+ int rc;
+
+ pr_info("%s:\n", __func__);
+
+ memset(&audio_mvs_info, 0, sizeof(audio_mvs_info));
+ mutex_init(&audio_mvs_info.lock);
+ mutex_init(&audio_mvs_info.in_lock);
+ mutex_init(&audio_mvs_info.out_lock);
+
+ init_waitqueue_head(&audio_mvs_info.wait);
+ init_waitqueue_head(&audio_mvs_info.mode_wait);
+ init_waitqueue_head(&audio_mvs_info.out_wait);
+
+ INIT_LIST_HEAD(&audio_mvs_info.in_queue);
+ INIT_LIST_HEAD(&audio_mvs_info.free_in_queue);
+ INIT_LIST_HEAD(&audio_mvs_info.out_queue);
+ INIT_LIST_HEAD(&audio_mvs_info.free_out_queue);
+
+ wake_lock_init(&audio_mvs_info.suspend_lock,
+ WAKE_LOCK_SUSPEND,
+ "audio_mvs_suspend");
+ wake_lock_init(&audio_mvs_info.idle_lock,
+ WAKE_LOCK_IDLE,
+ "audio_mvs_idle");
+
+ audio_mvs_info.rpc_endpt = msm_rpc_connect_compatible(MVS_PROG,
+ MVS_VERS_COMP_VER5,
+ MSM_RPC_UNINTERRUPTIBLE);
+
+ if (IS_ERR(audio_mvs_info.rpc_endpt)) {
+ pr_err("%s: MVS RPC connect failed ver 0x%x\n", __func__,
+ MVS_VERS_COMP_VER5);
+ audio_mvs_info.rpc_endpt = msm_rpc_connect_compatible(MVS_PROG,
+ MVS_VERS_COMP_VER4,
+ MSM_RPC_UNINTERRUPTIBLE);
+ if (IS_ERR(audio_mvs_info.rpc_endpt)) {
+ pr_err("%s: MVS RPC connect failed ver 0x%x\n",
+ __func__, MVS_VERS_COMP_VER4);
+ audio_mvs_info.rpc_endpt =
+ msm_rpc_connect_compatible(MVS_PROG,
+ MVS_VERS,
+ MSM_RPC_UNINTERRUPTIBLE);
+ if (IS_ERR(audio_mvs_info.rpc_endpt)) {
+ pr_err("%s: MVS RPC connect failed ver 0x%x\n",
+ __func__, MVS_VERS);
+ rc = PTR_ERR(audio_mvs_info.rpc_endpt);
+ audio_mvs_info.rpc_endpt = NULL;
+ goto done;
+ } else {
+ pr_debug("%s: MVS RPC connect succeeded ver\
+ 0x%x\n", __func__, MVS_VERS);
+ audio_mvs_info.rpc_prog = MVS_PROG;
+ audio_mvs_info.rpc_ver = MVS_VERS;
+ }
+ } else {
+ pr_debug("%s: MVS RPC connect succeeded ver 0x%x\n",
+ __func__, MVS_VERS_COMP_VER4);
+ audio_mvs_info.rpc_prog = MVS_PROG;
+ audio_mvs_info.rpc_ver = MVS_VERS_COMP_VER4;
+ }
+ } else {
+ pr_debug("%s: MVS RPC connect succeeded ver 0x%x\n", __func__,
+ MVS_VERS_COMP_VER5);
+ audio_mvs_info.rpc_prog = MVS_PROG;
+ audio_mvs_info.rpc_ver = MVS_VERS_COMP_VER5;
+ }
+ audio_mvs_info.task = kthread_run(audio_mvs_thread,
+ &audio_mvs_info,
+ "audio_mvs");
+ if (IS_ERR(audio_mvs_info.task)) {
+ pr_err("%s: MVS thread create failed\n", __func__);
+ rc = PTR_ERR(audio_mvs_info.task);
+ audio_mvs_info.task = NULL;
+ msm_rpc_close(audio_mvs_info.rpc_endpt);
+ audio_mvs_info.rpc_endpt = NULL;
+ goto done;
+ }
+
+ rc = misc_register(&audio_mvs_misc);
+done:
+ return rc;
+}
+
+static void __exit audio_mvs_exit(void)
+{
+ pr_info("%s:\n", __func__);
+
+ misc_deregister(&audio_mvs_misc);
+}
+
+module_init(audio_mvs_init);
+module_exit(audio_mvs_exit);
+
+MODULE_DESCRIPTION("MSM MVS driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_out.c b/arch/arm/mach-msm/qdsp5v2/audio_out.c
new file mode 100644
index 0000000..7c1e5ca
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_out.c
@@ -0,0 +1,722 @@
+/*
+ * pcm audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * 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.
+ *
+ */
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/wakelock.h>
+
+#include <linux/msm_audio.h>
+#include <linux/android_pmem.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+#include <mach/qdsp5v2/qdsp5audppcmdi.h>
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/qdsp5v2/audpp.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+
+#include <mach/htc_pwrsink.h>
+#include <mach/debug_mm.h>
+
+#define BUFSZ (960 * 5)
+#define DMASZ (BUFSZ * 2)
+
+#define HOSTPCM_STREAM_ID 5
+
+struct buffer {
+ void *data;
+ unsigned size;
+ unsigned used;
+ unsigned addr;
+};
+
+struct audio {
+ struct buffer out[2];
+
+ spinlock_t dsp_lock;
+
+ uint8_t out_head;
+ uint8_t out_tail;
+ uint8_t out_needed; /* number of buffers the dsp is waiting for */
+
+ atomic_t out_bytes;
+
+ struct mutex lock;
+ struct mutex write_lock;
+ wait_queue_head_t wait;
+
+ /* configuration to use on next enable */
+ uint32_t out_sample_rate;
+ uint32_t out_channel_mode;
+ uint32_t out_weight;
+ uint32_t out_buffer_size;
+ uint32_t device_events;
+ int16_t source;
+
+ /* data allocated for various buffers */
+ char *data;
+ dma_addr_t phys;
+
+ int teos; /* valid only if tunnel mode & no data left for decoder */
+ int opened;
+ int enabled;
+ int running;
+ int stopped; /* set when stopped, cleared on flush */
+ uint16_t dec_id;
+ int voice_state;
+
+ struct wake_lock wakelock;
+ struct wake_lock idlelock;
+
+ struct audpp_cmd_cfg_object_params_volume vol_pan;
+};
+
+static void audio_out_listener(u32 evt_id, union auddev_evt_data *evt_payload,
+ void *private_data)
+{
+ struct audio *audio = private_data;
+ switch (evt_id) {
+ case AUDDEV_EVT_DEV_RDY:
+ MM_DBG(":AUDDEV_EVT_DEV_RDY\n");
+ audio->source |= (0x1 << evt_payload->routing_id);
+ if (audio->running == 1 && audio->enabled == 1)
+ audpp_route_stream(audio->dec_id, audio->source);
+ break;
+ case AUDDEV_EVT_DEV_RLS:
+ MM_DBG(":AUDDEV_EVT_DEV_RLS\n");
+ audio->source &= ~(0x1 << evt_payload->routing_id);
+ if (audio->running == 1 && audio->enabled == 1)
+ audpp_route_stream(audio->dec_id, audio->source);
+ break;
+ case AUDDEV_EVT_STREAM_VOL_CHG:
+ audio->vol_pan.volume = evt_payload->session_vol;
+ MM_DBG(":AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n",
+ audio->vol_pan.volume);
+ if (audio->running)
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ break;
+ case AUDDEV_EVT_VOICE_STATE_CHG:
+ MM_DBG("AUDDEV_EVT_VOICE_STATE_CHG, state = %d\n",
+ evt_payload->voice_state);
+ audio->voice_state = evt_payload->voice_state;
+ /* Voice uplink Rx case */
+ if (audio->running &&
+ (audio->source & AUDPP_MIXER_UPLINK_RX) &&
+ (audio->voice_state == VOICE_STATE_OFFCALL)) {
+ MM_DBG("Voice is terminated, Wake up write: %x %x\n",
+ audio->voice_state, audio->source);
+ wake_up(&audio->wait);
+ }
+ break;
+ default:
+ MM_ERR("ERROR:wrong event\n");
+ break;
+ }
+}
+
+static void audio_prevent_sleep(struct audio *audio)
+{
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ wake_lock(&audio->wakelock);
+ wake_lock(&audio->idlelock);
+}
+
+static void audio_allow_sleep(struct audio *audio)
+{
+ wake_unlock(&audio->wakelock);
+ wake_unlock(&audio->idlelock);
+ MM_DBG("\n"); /* Macro prints the file name and function */
+}
+
+static int audio_dsp_out_enable(struct audio *audio, int yes);
+static int audio_dsp_send_buffer(struct audio *audio, unsigned id,
+ unsigned len);
+
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg);
+
+/* must be called with audio->lock held */
+static int audio_enable(struct audio *audio)
+{
+ MM_DBG("\n"); /* Macro prints the file name and function */
+
+ if (audio->enabled)
+ return 0;
+
+ /* refuse to start if we're not ready */
+ if (!audio->out[0].used || !audio->out[1].used)
+ return -EIO;
+
+ /* we start buffers 0 and 1, so buffer 0 will be the
+ * next one the dsp will want
+ */
+ audio->out_tail = 0;
+ audio->out_needed = 0;
+
+ audio_prevent_sleep(audio);
+
+ if (audpp_enable(-1, audio_dsp_event, audio)) {
+ MM_ERR("audpp_enable() failed\n");
+ audio_allow_sleep(audio);
+ return -ENODEV;
+ }
+
+ audio->enabled = 1;
+ htc_pwrsink_set(PWRSINK_AUDIO, 100);
+ return 0;
+}
+
+/* must be called with audio->lock held */
+static int audio_disable(struct audio *audio)
+{
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ if (audio->enabled) {
+ audio->enabled = 0;
+ audio_dsp_out_enable(audio, 0);
+
+ audpp_disable(-1, audio);
+
+ wake_up(&audio->wait);
+ audio->out_needed = 0;
+ audio_allow_sleep(audio);
+ }
+ return 0;
+}
+
+/* ------------------- dsp --------------------- */
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg)
+{
+ struct audio *audio = private;
+ struct buffer *frame;
+ unsigned long flags;
+ static unsigned long pcmdmamsd_time;
+
+ switch (id) {
+ case AUDPP_MSG_HOST_PCM_INTF_MSG: {
+ unsigned id = msg[3];
+ unsigned idx = msg[4] - 1;
+
+ MM_DBG("HOST_PCM id %d idx %d\n", id, idx);
+ if (id != AUDPP_MSG_HOSTPCM_ID_ARM_RX) {
+ MM_ERR("bogus id\n");
+ break;
+ }
+ if (idx > 1) {
+ MM_ERR("bogus buffer idx\n");
+ break;
+ }
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (audio->running) {
+ atomic_add(audio->out[idx].used, &audio->out_bytes);
+ audio->out[idx].used = 0;
+ frame = audio->out + audio->out_tail;
+ if (frame->used) {
+ /* Reset teos flag to avoid stale
+ * PCMDMAMISS been considered
+ */
+ audio->teos = 0;
+ audio_dsp_send_buffer(
+ audio, audio->out_tail, frame->used);
+ audio->out_tail ^= 1;
+ } else {
+ audio->out_needed++;
+ }
+ wake_up(&audio->wait);
+ }
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ break;
+ }
+ case AUDPP_MSG_PCMDMAMISSED:
+ /* prints only if 1 second is elapsed since the last time
+ * this message has been printed */
+ if (printk_timed_ratelimit(&pcmdmamsd_time, 1000))
+ printk(KERN_INFO "[%s:%s] PCMDMAMISSED %d\n",
+ __MM_FILE__, __func__, msg[0]);
+ audio->teos++;
+ MM_DBG("PCMDMAMISSED Count per Buffer %d\n", audio->teos);
+ wake_up(&audio->wait);
+ break;
+ case AUDPP_MSG_CFG_MSG:
+ if (msg[0] == AUDPP_MSG_ENA_ENA) {
+ MM_DBG("CFG_MSG ENABLE\n");
+ audio->out_needed = 0;
+ audio->running = 1;
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ audpp_route_stream(audio->dec_id, audio->source);
+ audio_dsp_out_enable(audio, 1);
+ } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+ MM_DBG("CFG_MSG DISABLE\n");
+ audio->running = 0;
+ } else {
+ MM_ERR("CFG_MSG %d?\n", msg[0]);
+ }
+ break;
+ default:
+ MM_ERR("UNKNOWN (%d)\n", id);
+ }
+}
+
+static int audio_dsp_out_enable(struct audio *audio, int yes)
+{
+ struct audpp_cmd_pcm_intf cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_PCM_INTF;
+ cmd.stream = AUDPP_CMD_POPP_STREAM;
+ cmd.stream_id = audio->dec_id;
+ cmd.config = AUDPP_CMD_PCM_INTF_CONFIG_CMD_V;
+ cmd.intf_type = AUDPP_CMD_PCM_INTF_RX_ENA_ARMTODSP_V;
+
+ if (yes) {
+ cmd.write_buf1LSW = audio->out[0].addr;
+ cmd.write_buf1MSW = audio->out[0].addr >> 16;
+ if (audio->out[0].used)
+ cmd.write_buf1_len = audio->out[0].used;
+ else
+ cmd.write_buf1_len = audio->out[0].size;
+ cmd.write_buf2LSW = audio->out[1].addr;
+ cmd.write_buf2MSW = audio->out[1].addr >> 16;
+ if (audio->out[1].used)
+ cmd.write_buf2_len = audio->out[1].used;
+ else
+ cmd.write_buf2_len = audio->out[1].size;
+ cmd.arm_to_rx_flag = AUDPP_CMD_PCM_INTF_ENA_V;
+ cmd.weight_decoder_to_rx = audio->out_weight;
+ cmd.weight_arm_to_rx = 1;
+ cmd.partition_number_arm_to_dsp = 0;
+ cmd.sample_rate = audio->out_sample_rate;
+ cmd.channel_mode = audio->out_channel_mode;
+ }
+
+ return audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static int audio_dsp_send_buffer(struct audio *audio, unsigned idx,
+ unsigned len)
+{
+ struct audpp_cmd_pcm_intf_send_buffer cmd;
+
+ cmd.cmd_id = AUDPP_CMD_PCM_INTF;
+ cmd.stream = AUDPP_CMD_POPP_STREAM;
+ cmd.stream_id = audio->dec_id;
+ cmd.config = AUDPP_CMD_PCM_INTF_BUFFER_CMD_V;
+ cmd.intf_type = AUDPP_CMD_PCM_INTF_RX_ENA_ARMTODSP_V;
+ cmd.dsp_to_arm_buf_id = 0;
+ cmd.arm_to_dsp_buf_id = idx + 1;
+ cmd.arm_to_dsp_buf_len = len;
+
+ return audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+/* ------------------- device --------------------- */
+static void audio_flush(struct audio *audio)
+{
+ audio->out[0].used = 0;
+ audio->out[1].used = 0;
+ audio->out_head = 0;
+ audio->out_tail = 0;
+ audio->stopped = 0;
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct audio *audio = file->private_data;
+ int rc = -EINVAL;
+ unsigned long flags = 0;
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ stats.byte_count = atomic_read(&audio->out_bytes);
+ if (copy_to_user((void *) arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ }
+
+ switch (cmd) {
+ case AUDIO_SET_VOLUME:
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->vol_pan.volume = arg;
+ if (audio->running)
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ return 0;
+
+ case AUDIO_SET_PAN:
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->vol_pan.pan = arg;
+ if (audio->running)
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ return 0;
+ }
+
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START:
+ if ((audio->voice_state != VOICE_STATE_INCALL)
+ && (audio->source & AUDPP_MIXER_UPLINK_RX)) {
+ MM_ERR("Unable to Start : state %d source %d\n",
+ audio->voice_state, audio->source);
+ rc = -EPERM;
+ break;
+ }
+ rc = audio_enable(audio);
+ break;
+ case AUDIO_STOP:
+ rc = audio_disable(audio);
+ audio->stopped = 1;
+ break;
+ case AUDIO_FLUSH:
+ if (audio->stopped) {
+ /* Make sure we're stopped and we wake any threads
+ * that might be blocked holding the write_lock.
+ * While audio->stopped write threads will always
+ * exit immediately.
+ */
+ wake_up(&audio->wait);
+ mutex_lock(&audio->write_lock);
+ audio_flush(audio);
+ mutex_unlock(&audio->write_lock);
+ }
+ case AUDIO_SET_CONFIG: {
+ struct msm_audio_config config;
+ if (copy_from_user(&config, (void *) arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (config.channel_count == 1)
+ config.channel_count = AUDPP_CMD_PCM_INTF_MONO_V;
+ else if (config.channel_count == 2)
+ config.channel_count = AUDPP_CMD_PCM_INTF_STEREO_V;
+ else {
+ rc = -EINVAL;
+ break;
+ }
+ audio->out_sample_rate = config.sample_rate;
+ audio->out_channel_mode = config.channel_count;
+ rc = 0;
+ break;
+ }
+ case AUDIO_GET_CONFIG: {
+ struct msm_audio_config config;
+ config.buffer_size = BUFSZ;
+ config.buffer_count = 2;
+ config.sample_rate = audio->out_sample_rate;
+ if (audio->out_channel_mode == AUDPP_CMD_PCM_INTF_MONO_V)
+ config.channel_count = 1;
+ else
+ config.channel_count = 2;
+
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+ config.unused[2] = 0;
+ if (copy_to_user((void *) arg, &config, sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ case AUDIO_GET_SESSION_ID: {
+ if (copy_to_user((void *) arg, &audio->dec_id,
+ sizeof(unsigned short)))
+ return -EFAULT;
+ rc = 0;
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+/* Only useful in tunnel-mode */
+static int audio_fsync(struct file *file, int datasync)
+{
+ struct audio *audio = file->private_data;
+ int rc = 0;
+
+ if (!audio->running)
+ return -EINVAL;
+
+ mutex_lock(&audio->write_lock);
+
+ /* PCM DMAMISS message is sent only once in
+ * hpcm interface. So, wait for buffer complete
+ * and teos flag.
+ */
+ rc = wait_event_interruptible(audio->wait,
+ (!audio->out[0].used &&
+ !audio->out[1].used));
+
+ if (rc < 0)
+ goto done;
+
+ rc = wait_event_interruptible(audio->wait,
+ audio->teos);
+done:
+ mutex_unlock(&audio->write_lock);
+ return rc;
+}
+
+static ssize_t audio_read(struct file *file, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ return -EINVAL;
+}
+
+static inline int rt_policy(int policy)
+{
+ if (unlikely(policy == SCHED_FIFO) || unlikely(policy == SCHED_RR))
+ return 1;
+ return 0;
+}
+
+static inline int task_has_rt_policy(struct task_struct *p)
+{
+ return rt_policy(p->policy);
+}
+
+static ssize_t audio_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct sched_param s = { .sched_priority = 1 };
+ struct audio *audio = file->private_data;
+ unsigned long flags;
+ const char __user *start = buf;
+ struct buffer *frame;
+ size_t xfer;
+ int old_prio = current->rt_priority;
+ int old_policy = current->policy;
+ int cap_nice = cap_raised(current_cap(), CAP_SYS_NICE);
+ int rc = 0;
+
+
+ if ((audio->voice_state == VOICE_STATE_OFFCALL)
+ && (audio->source & AUDPP_MIXER_UPLINK_RX) &&
+ audio->running) {
+ MM_ERR("Not Permitted Voice Terminated: state %d source %x \
+ running %d\n",
+ audio->voice_state, audio->source, audio->running);
+ return -EPERM;
+ }
+ /* just for this write, set us real-time */
+ if (!task_has_rt_policy(current)) {
+ struct cred *new = prepare_creds();
+ cap_raise(new->cap_effective, CAP_SYS_NICE);
+ commit_creds(new);
+ if ((sched_setscheduler(current, SCHED_RR, &s)) < 0)
+ MM_ERR("sched_setscheduler failed\n");
+ }
+
+ mutex_lock(&audio->write_lock);
+ while (count > 0) {
+ frame = audio->out + audio->out_head;
+
+ rc = wait_event_interruptible(audio->wait,
+ (frame->used == 0) || (audio->stopped) ||
+ ((audio->voice_state == VOICE_STATE_OFFCALL) &&
+ (audio->source & AUDPP_MIXER_UPLINK_RX)));
+
+ if (rc < 0)
+ break;
+ if (audio->stopped) {
+ rc = -EBUSY;
+ break;
+ } else if ((audio->voice_state == VOICE_STATE_OFFCALL) &&
+ (audio->source & AUDPP_MIXER_UPLINK_RX)) {
+ MM_ERR("Not Permitted Voice Terminated: %d\n",
+ audio->voice_state);
+ rc = -EPERM;
+ break;
+ }
+
+ xfer = count > frame->size ? frame->size : count;
+ if (copy_from_user(frame->data, buf, xfer)) {
+ rc = -EFAULT;
+ break;
+ }
+ frame->used = xfer;
+ audio->out_head ^= 1;
+ count -= xfer;
+ buf += xfer;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ frame = audio->out + audio->out_tail;
+ if (frame->used && audio->out_needed) {
+ /* Reset teos flag to avoid stale
+ * PCMDMAMISS been considered
+ */
+ audio->teos = 0;
+ audio_dsp_send_buffer(audio, audio->out_tail,
+ frame->used);
+ audio->out_tail ^= 1;
+ audio->out_needed--;
+ }
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ }
+
+ mutex_unlock(&audio->write_lock);
+
+ /* restore scheduling policy and priority */
+ if (!rt_policy(old_policy)) {
+ struct sched_param v = { .sched_priority = old_prio };
+ if ((sched_setscheduler(current, old_policy, &v)) < 0)
+ MM_ERR("sched_setscheduler failed\n");
+ if (likely(!cap_nice)) {
+ struct cred *new = prepare_creds();
+ cap_lower(new->cap_effective, CAP_SYS_NICE);
+ commit_creds(new);
+ }
+ }
+
+ if (buf > start)
+ return buf - start;
+ return rc;
+}
+
+static int audio_release(struct inode *inode, struct file *file)
+{
+ struct audio *audio = file->private_data;
+
+ mutex_lock(&audio->lock);
+ auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id);
+ audio_disable(audio);
+ audio_flush(audio);
+ audio->opened = 0;
+ mutex_unlock(&audio->lock);
+ htc_pwrsink_set(PWRSINK_AUDIO, 0);
+ return 0;
+}
+
+static struct audio the_audio;
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct audio *audio = &the_audio;
+ int rc;
+
+ mutex_lock(&audio->lock);
+
+ if (audio->opened) {
+ MM_ERR("busy\n");
+ rc = -EBUSY;
+ goto done;
+ }
+
+
+ audio->dec_id = HOSTPCM_STREAM_ID;
+
+ audio->out_buffer_size = BUFSZ;
+ audio->out_sample_rate = 44100;
+ audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V;
+ audio->out_weight = 100;
+
+ audio->out[0].data = audio->data + 0;
+ audio->out[0].addr = audio->phys + 0;
+ audio->out[0].size = BUFSZ;
+
+ audio->out[1].data = audio->data + BUFSZ;
+ audio->out[1].addr = audio->phys + BUFSZ;
+ audio->out[1].size = BUFSZ;
+
+ audio->vol_pan.volume = 0x2000;
+ audio->vol_pan.pan = 0x0;
+ audio->source = 0x0;
+
+ audio_flush(audio);
+ audio->voice_state = msm_get_voice_state();
+ MM_DBG("voice_state = %x\n", audio->voice_state);
+ audio->device_events = AUDDEV_EVT_DEV_RDY
+ |AUDDEV_EVT_DEV_RLS|
+ AUDDEV_EVT_STREAM_VOL_CHG|
+ AUDDEV_EVT_VOICE_STATE_CHG;
+
+ MM_DBG("register for event callback pdata %p\n", audio);
+ rc = auddev_register_evt_listner(audio->device_events,
+ AUDDEV_CLNT_DEC,
+ audio->dec_id,
+ audio_out_listener,
+ (void *)audio);
+ if (rc) {
+ MM_ERR("%s: failed to register listener\n", __func__);
+ goto done;
+ }
+
+ file->private_data = audio;
+ audio->opened = 1;
+ rc = 0;
+done:
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static const struct file_operations audio_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_release,
+ .read = audio_read,
+ .write = audio_write,
+ .unlocked_ioctl = audio_ioctl,
+ .fsync = audio_fsync,
+};
+
+struct miscdevice audio_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_pcm_out",
+ .fops = &audio_fops,
+};
+
+static int __init audio_init(void)
+{
+ the_audio.phys = pmem_kalloc(DMASZ, PMEM_MEMTYPE_EBI1|
+ PMEM_ALIGNMENT_4K);
+ if (!IS_ERR((void *)the_audio.phys)) {
+ the_audio.data = ioremap(the_audio.phys, DMASZ);
+ if (!the_audio.data) {
+ MM_ERR("could not map pmem buffers\n");
+ pmem_kfree(the_audio.phys);
+ return -ENOMEM;
+ }
+ } else {
+ MM_ERR("could not allocate pmem buffers\n");
+ return -ENOMEM;
+ }
+ MM_DBG("Memory addr = 0x%8x phy addr = 0x%8x\n",\
+ (int) the_audio.data, (int) the_audio.phys);
+ mutex_init(&the_audio.lock);
+ mutex_init(&the_audio.write_lock);
+ spin_lock_init(&the_audio.dsp_lock);
+ init_waitqueue_head(&the_audio.wait);
+ wake_lock_init(&the_audio.wakelock, WAKE_LOCK_SUSPEND, "audio_pcm");
+ wake_lock_init(&the_audio.idlelock, WAKE_LOCK_IDLE, "audio_pcm_idle");
+ return misc_register(&audio_misc);
+}
+
+late_initcall(audio_init);
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_pcm.c b/arch/arm/mach-msm/qdsp5v2/audio_pcm.c
new file mode 100644
index 0000000..3115d52
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_pcm.c
@@ -0,0 +1,1697 @@
+/* arch/arm/mach-msm/qdsp5v2/audio_pcm.c
+ *
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/earlysuspend.h>
+#include <linux/list.h>
+#include <linux/android_pmem.h>
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+
+#include <linux/msm_audio.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+
+#include <mach/qdsp5v2/qdsp5audppcmdi.h>
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/qdsp5audplaycmdi.h>
+#include <mach/qdsp5v2/qdsp5audplaymsg.h>
+#include <mach/qdsp5v2/audpp.h>
+#include <mach/debug_mm.h>
+#include <linux/slab.h>
+
+#define ADRV_STATUS_AIO_INTF 0x00000001
+#define ADRV_STATUS_OBUF_GIVEN 0x00000002
+#define ADRV_STATUS_IBUF_GIVEN 0x00000004
+#define ADRV_STATUS_FSYNC 0x00000008
+
+/* Size must be power of 2 */
+#define BUFSZ_MAX 32768
+#define BUFSZ_MIN 4096
+#define DMASZ_MAX (BUFSZ_MAX * 2)
+#define DMASZ_MIN (BUFSZ_MIN * 2)
+
+#define AUDDEC_DEC_PCM 0
+
+/* Decoder status received from AUDPPTASK */
+#define AUDPP_DEC_STATUS_SLEEP 0
+#define AUDPP_DEC_STATUS_INIT 1
+#define AUDPP_DEC_STATUS_CFG 2
+#define AUDPP_DEC_STATUS_PLAY 3
+
+#define AUDPCM_EVENT_NUM 10 /* Default number of pre-allocated event packets */
+
+#define __CONTAINS(r, v, l) ({ \
+ typeof(r) __r = r; \
+ typeof(v) __v = v; \
+ typeof(v) __e = __v + l; \
+ int res = ((__v >= __r->vaddr) && \
+ (__e <= __r->vaddr + __r->len)); \
+ res; \
+})
+
+#define CONTAINS(r1, r2) ({ \
+ typeof(r2) __r2 = r2; \
+ __CONTAINS(r1, __r2->vaddr, __r2->len); \
+})
+
+#define IN_RANGE(r, v) ({ \
+ typeof(r) __r = r; \
+ typeof(v) __vv = v; \
+ int res = ((__vv >= __r->vaddr) && \
+ (__vv < (__r->vaddr + __r->len))); \
+ res; \
+})
+
+#define OVERLAPS(r1, r2) ({ \
+ typeof(r1) __r1 = r1; \
+ typeof(r2) __r2 = r2; \
+ typeof(__r2->vaddr) __v = __r2->vaddr; \
+ typeof(__v) __e = __v + __r2->len - 1; \
+ int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \
+ res; \
+})
+
+struct buffer {
+ void *data;
+ unsigned size;
+ unsigned used; /* Input usage actual DSP produced PCM size */
+ unsigned addr;
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+struct audpcm_suspend_ctl {
+ struct early_suspend node;
+ struct audio *audio;
+};
+#endif
+
+struct audpcm_event {
+ struct list_head list;
+ int event_type;
+ union msm_audio_event_payload payload;
+};
+
+struct audpcm_pmem_region {
+ struct list_head list;
+ struct file *file;
+ int fd;
+ void *vaddr;
+ unsigned long paddr;
+ unsigned long kvaddr;
+ unsigned long len;
+ unsigned ref_cnt;
+};
+
+struct audpcm_buffer_node {
+ struct list_head list;
+ struct msm_audio_aio_buf buf;
+ unsigned long paddr;
+};
+
+struct audpcm_drv_operations {
+ void (*send_data)(struct audio *, unsigned);
+ void (*out_flush)(struct audio *);
+ int (*fsync)(struct audio *);
+};
+
+struct audio {
+ struct buffer out[2];
+
+ spinlock_t dsp_lock;
+
+ uint8_t out_head;
+ uint8_t out_tail;
+ uint8_t out_needed; /* number of buffers the dsp is waiting for */
+ unsigned out_dma_sz;
+ struct list_head out_queue; /* queue to retain output buffers */
+ atomic_t out_bytes;
+
+ struct mutex lock;
+ struct mutex write_lock;
+ wait_queue_head_t write_wait;
+
+ struct msm_adsp_module *audplay;
+
+ /* configuration to use on next enable */
+ uint32_t out_sample_rate;
+ uint32_t out_channel_mode;
+ uint32_t out_bits; /* bits per sample */
+
+ /* data allocated for various buffers */
+ char *data;
+ int32_t phys;
+
+ uint32_t drv_status;
+ int wflush; /* Write flush */
+ int opened;
+ int enabled;
+ int running;
+ int stopped; /* set when stopped, cleared on flush */
+ int teos; /* valid only if tunnel mode & no data left for decoder */
+ enum msm_aud_decoder_state dec_state; /* Represents decoder state */
+ int reserved; /* A byte is being reserved */
+ char rsv_byte; /* Handle odd length user data */
+
+ const char *module_name;
+ unsigned queue_id;
+ uint32_t device_events;
+
+ unsigned volume;
+
+ uint16_t dec_id;
+ int16_t source;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct audpcm_suspend_ctl suspend_ctl;
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dentry;
+#endif
+ wait_queue_head_t wait;
+ struct list_head free_event_queue;
+ struct list_head event_queue;
+ wait_queue_head_t event_wait;
+ spinlock_t event_queue_lock;
+ struct mutex get_event_lock;
+ int event_abort;
+ /* AV sync Info */
+ int avsync_flag; /* Flag to indicate feedback from DSP */
+ wait_queue_head_t avsync_wait;/* Wait queue for AV Sync Message */
+ /* flags, 48 bits sample/bytes counter per channel */
+ uint16_t avsync[AUDPP_AVSYNC_CH_COUNT * AUDPP_AVSYNC_NUM_WORDS + 1];
+
+ struct list_head pmem_region_queue;
+ struct audpcm_drv_operations drv_ops;
+};
+
+static int auddec_dsp_config(struct audio *audio, int enable);
+static void audpp_cmd_cfg_adec_params(struct audio *audio);
+static void audplay_send_data(struct audio *audio, unsigned needed);
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg);
+static void audpcm_post_event(struct audio *audio, int type,
+ union msm_audio_event_payload payload);
+static unsigned long audpcm_pmem_fixup(struct audio *audio, void *addr,
+ unsigned long len, int ref_up);
+
+static void pcm_listner(u32 evt_id, union auddev_evt_data *evt_payload,
+ void *private_data)
+{
+ struct audio *audio = (struct audio *) private_data;
+ switch (evt_id) {
+ case AUDDEV_EVT_DEV_RDY:
+ MM_DBG("AUDDEV_EVT_DEV_RDY\n");
+ audio->source |= (0x1 << evt_payload->routing_id);
+ if (audio->running == 1 && audio->enabled == 1)
+ audpp_route_stream(audio->dec_id, audio->source);
+ break;
+ case AUDDEV_EVT_DEV_RLS:
+ MM_DBG("AUDDEV_EVT_DEV_RLS\n");
+ audio->source &= ~(0x1 << evt_payload->routing_id);
+ if (audio->running == 1 && audio->enabled == 1)
+ audpp_route_stream(audio->dec_id, audio->source);
+ break;
+ case AUDDEV_EVT_STREAM_VOL_CHG:
+ audio->volume = evt_payload->session_vol;
+ MM_DBG("AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n",
+ audio->volume);
+ if (audio->running)
+ audpp_set_volume_and_pan(audio->dec_id, audio->volume,
+ 0, POPP);
+ break;
+ default:
+ MM_ERR("ERROR:wrong event\n");
+ break;
+ }
+}
+/* must be called with audio->lock held */
+static int audio_enable(struct audio *audio)
+{
+ MM_DBG("\n"); /* Macro prints the file name and function */
+
+ if (audio->enabled)
+ return 0;
+
+ audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+ audio->out_tail = 0;
+ audio->out_needed = 0;
+
+ if (msm_adsp_enable(audio->audplay)) {
+ MM_ERR("msm_adsp_enable(audplay) failed\n");
+ return -ENODEV;
+ }
+
+ if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) {
+ MM_ERR("audpp_enable() failed\n");
+ msm_adsp_disable(audio->audplay);
+ return -ENODEV;
+ }
+
+ audio->enabled = 1;
+ return 0;
+}
+
+/* must be called with audio->lock held */
+static int audio_disable(struct audio *audio)
+{
+ int rc = 0;
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ if (audio->enabled) {
+ audio->enabled = 0;
+ audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+ auddec_dsp_config(audio, 0);
+ rc = wait_event_interruptible_timeout(audio->wait,
+ audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+ msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE)
+ rc = -EFAULT;
+ else
+ rc = 0;
+ wake_up(&audio->write_wait);
+ msm_adsp_disable(audio->audplay);
+ audpp_disable(audio->dec_id, audio);
+ audio->out_needed = 0;
+ }
+ return rc;
+}
+
+/* ------------------- dsp --------------------- */
+static void audplay_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent) (void *ptr, size_t len))
+{
+ struct audio *audio = data;
+ uint32_t msg[28];
+ getevent(msg, sizeof(msg));
+
+ MM_DBG("msg_id=%x\n", id);
+
+ switch (id) {
+ case AUDPLAY_MSG_DEC_NEEDS_DATA:
+ audio->drv_ops.send_data(audio, 1);
+ break;
+
+ case ADSP_MESSAGE_ID:
+ MM_DBG("Received ADSP event:module audplaytask\n");
+ break;
+
+ default:
+ MM_ERR("unexpected message from decoder\n");
+ break;
+ }
+}
+
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg)
+{
+ struct audio *audio = private;
+
+ switch (id) {
+ case AUDPP_MSG_STATUS_MSG:{
+ unsigned status = msg[1];
+
+ switch (status) {
+ case AUDPP_DEC_STATUS_SLEEP: {
+ uint16_t reason = msg[2];
+ MM_DBG("decoder status:sleep reason=0x%04x\n",
+ reason);
+ if ((reason == AUDPP_MSG_REASON_MEM)
+ || (reason ==
+ AUDPP_MSG_REASON_NODECODER)) {
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_FAILURE;
+ wake_up(&audio->wait);
+ } else if (reason == AUDPP_MSG_REASON_NONE) {
+ /* decoder is in disable state */
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_CLOSE;
+ wake_up(&audio->wait);
+ }
+ break;
+ }
+ case AUDPP_DEC_STATUS_INIT:
+ MM_DBG("decoder status: init \n");
+ audpp_cmd_cfg_adec_params(audio);
+ break;
+
+ case AUDPP_DEC_STATUS_CFG:
+ MM_DBG("decoder status: cfg \n");
+ break;
+ case AUDPP_DEC_STATUS_PLAY:
+ MM_DBG("decoder status: play \n");
+ audpp_route_stream(audio->dec_id,
+ audio->source);
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_SUCCESS;
+ wake_up(&audio->wait);
+ break;
+ default:
+ MM_ERR("unknown decoder status\n");
+ break;
+ }
+ break;
+ }
+ case AUDPP_MSG_CFG_MSG:
+ if (msg[0] == AUDPP_MSG_ENA_ENA) {
+ MM_DBG("CFG_MSG ENABLE\n");
+ auddec_dsp_config(audio, 1);
+ audio->out_needed = 0;
+ audio->running = 1;
+ audpp_set_volume_and_pan(audio->dec_id, audio->volume,
+ 0, POPP);
+ } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+ MM_DBG("CFG_MSG DISABLE\n");
+ audio->running = 0;
+ } else {
+ MM_ERR("audio_dsp_event: CFG_MSG %d?\n", msg[0]);
+ }
+ break;
+ case AUDPP_MSG_FLUSH_ACK:
+ MM_DBG("FLUSH_ACK\n");
+ audio->wflush = 0;
+ wake_up(&audio->write_wait);
+ break;
+
+ case AUDPP_MSG_PCMDMAMISSED:
+ MM_DBG("PCMDMAMISSED\n");
+ audio->teos = 1;
+ wake_up(&audio->write_wait);
+ break;
+
+ case AUDPP_MSG_AVSYNC_MSG:
+ pr_info("%s: AVSYNC_MSG\n", __func__);
+ memcpy(&audio->avsync[0], msg, sizeof(audio->avsync));
+ audio->avsync_flag = 1;
+ wake_up(&audio->avsync_wait);
+ break;
+
+ default:
+ MM_DBG("audio_dsp_event: UNKNOWN (%d)\n", id);
+ }
+
+}
+
+
+struct msm_adsp_ops audpcmdec_adsp_ops = {
+ .event = audplay_dsp_event,
+};
+
+
+#define audplay_send_queue0(audio, cmd, len) \
+ msm_adsp_write(audio->audplay, audio->queue_id, \
+ cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+ struct audpp_cmd_cfg_dec_type cfg_dec_cmd;
+
+ memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd));
+
+ cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+ if (enable)
+ cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_PCM;
+ else
+ cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_DIS_DEC_V;
+ cfg_dec_cmd.dm_mode = 0x0;
+ cfg_dec_cmd.stream_id = audio->dec_id;
+ return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd));
+}
+
+static void audpp_cmd_cfg_adec_params(struct audio *audio)
+{
+ struct audpp_cmd_cfg_adec_params_wav cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+ cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_WAV_LEN >> 1;
+ cmd.common.dec_id = audio->dec_id;
+ cmd.common.input_sampling_frequency = audio->out_sample_rate;
+ cmd.stereo_cfg = audio->out_channel_mode;
+ cmd.pcm_width = audio->out_bits;
+ cmd.sign = 0;
+ audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static int audplay_dsp_send_data_avail(struct audio *audio,
+ unsigned idx, unsigned len)
+{
+ struct audplay_cmd_bitstream_data_avail cmd;
+
+ cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL;
+ cmd.decoder_id = audio->dec_id;
+ cmd.buf_ptr = audio->out[idx].addr;
+ cmd.buf_size = len/2;
+ cmd.partition_number = 0;
+ /* complete writes to the input buffer */
+ wmb();
+ return audplay_send_queue0(audio, &cmd, sizeof(cmd));
+}
+
+static void audpcm_async_send_data(struct audio *audio, unsigned needed)
+{
+ unsigned long flags;
+
+ if (!audio->running)
+ return;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+
+ if (needed && !audio->wflush) {
+ audio->out_needed = 1;
+ if (audio->drv_status & ADRV_STATUS_OBUF_GIVEN) {
+ /* pop one node out of queue */
+ union msm_audio_event_payload payload;
+ struct audpcm_buffer_node *used_buf;
+
+ MM_DBG("consumed\n");
+
+ BUG_ON(list_empty(&audio->out_queue));
+ used_buf = list_first_entry(&audio->out_queue,
+ struct audpcm_buffer_node, list);
+ list_del(&used_buf->list);
+ payload.aio_buf = used_buf->buf;
+ audpcm_post_event(audio, AUDIO_EVENT_WRITE_DONE,
+ payload);
+ kfree(used_buf);
+ audio->drv_status &= ~ADRV_STATUS_OBUF_GIVEN;
+ }
+ }
+ if (audio->out_needed) {
+ struct audpcm_buffer_node *next_buf;
+ struct audplay_cmd_bitstream_data_avail cmd;
+ if (!list_empty(&audio->out_queue)) {
+ next_buf = list_first_entry(&audio->out_queue,
+ struct audpcm_buffer_node, list);
+ MM_DBG("next_buf %p\n", next_buf);
+ if (next_buf) {
+ MM_DBG("next buf phy %lx len %d\n",
+ next_buf->paddr, next_buf->buf.data_len);
+
+ cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL;
+ if (next_buf->buf.data_len)
+ cmd.decoder_id = audio->dec_id;
+ else {
+ cmd.decoder_id = -1;
+ MM_DBG("input EOS signaled\n");
+ }
+ cmd.buf_ptr = (unsigned) next_buf->paddr;
+ cmd.buf_size = next_buf->buf.data_len >> 1;
+ cmd.partition_number = 0;
+ /* complete writes to the input buffer */
+ wmb();
+ audplay_send_queue0(audio, &cmd, sizeof(cmd));
+ audio->out_needed = 0;
+ audio->drv_status |= ADRV_STATUS_OBUF_GIVEN;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+static void audplay_send_data(struct audio *audio, unsigned needed)
+{
+ struct buffer *frame;
+ unsigned long flags;
+
+ if (!audio->running)
+ return;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+
+ if (needed && !audio->wflush) {
+ /* We were called from the callback because the DSP
+ * requested more data. Note that the DSP does want
+ * more data, and if a buffer was in-flight, mark it
+ * as available (since the DSP must now be done with
+ * it).
+ */
+ audio->out_needed = 1;
+ frame = audio->out + audio->out_tail;
+ if (frame->used == 0xffffffff) {
+ MM_DBG("frame %d free\n", audio->out_tail);
+ frame->used = 0;
+ audio->out_tail ^= 1;
+ wake_up(&audio->write_wait);
+ }
+ }
+
+ if (audio->out_needed) {
+ /* If the DSP currently wants data and we have a
+ * buffer available, we will send it and reset
+ * the needed flag. We'll mark the buffer as in-flight
+ * so that it won't be recycled until the next buffer
+ * is requested
+ */
+
+ frame = audio->out + audio->out_tail;
+ if (frame->used) {
+ BUG_ON(frame->used == 0xffffffff);
+ MM_DBG("frame %d busy\n", audio->out_tail);
+ audplay_dsp_send_data_avail(audio, audio->out_tail,
+ frame->used);
+ frame->used = 0xffffffff;
+ audio->out_needed = 0;
+ }
+ }
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+/* ------------------- device --------------------- */
+static void audpcm_async_flush(struct audio *audio)
+{
+ struct audpcm_buffer_node *buf_node;
+ struct list_head *ptr, *next;
+ union msm_audio_event_payload payload;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ list_for_each_safe(ptr, next, &audio->out_queue) {
+ buf_node = list_entry(ptr, struct audpcm_buffer_node, list);
+ list_del(&buf_node->list);
+ payload.aio_buf = buf_node->buf;
+ audpcm_post_event(audio, AUDIO_EVENT_WRITE_DONE,
+ payload);
+ kfree(buf_node);
+ }
+ audio->drv_status &= ~ADRV_STATUS_OBUF_GIVEN;
+ audio->out_needed = 0;
+ atomic_set(&audio->out_bytes, 0);
+}
+
+static void audio_flush(struct audio *audio)
+{
+ audio->out[0].used = 0;
+ audio->out[1].used = 0;
+ audio->out_head = 0;
+ audio->out_tail = 0;
+ audio->reserved = 0;
+ audio->out_needed = 0;
+ atomic_set(&audio->out_bytes, 0);
+}
+
+static void audio_ioport_reset(struct audio *audio)
+{
+ if (audio->drv_status & ADRV_STATUS_AIO_INTF) {
+ /* If fsync is in progress, make sure
+ * return value of fsync indicates
+ * abort due to flush
+ */
+ if (audio->drv_status & ADRV_STATUS_FSYNC) {
+ MM_DBG("fsync in progress\n");
+ wake_up(&audio->write_wait);
+ mutex_lock(&audio->write_lock);
+ audio->drv_ops.out_flush(audio);
+ mutex_unlock(&audio->write_lock);
+ } else
+ audio->drv_ops.out_flush(audio);
+ } else {
+ /* Make sure read/write thread are free from
+ * sleep and knowing that system is not able
+ * to process io request at the moment
+ */
+ wake_up(&audio->write_wait);
+ mutex_lock(&audio->write_lock);
+ audio->drv_ops.out_flush(audio);
+ mutex_unlock(&audio->write_lock);
+ }
+ audio->avsync_flag = 1;
+ wake_up(&audio->avsync_wait);
+}
+
+static int audpcm_events_pending(struct audio *audio)
+{
+ unsigned long flags;
+ int empty;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ empty = !list_empty(&audio->event_queue);
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+ return empty || audio->event_abort;
+}
+
+static void audpcm_reset_event_queue(struct audio *audio)
+{
+ unsigned long flags;
+ struct audpcm_event *drv_evt;
+ struct list_head *ptr, *next;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ list_for_each_safe(ptr, next, &audio->event_queue) {
+ drv_evt = list_first_entry(&audio->event_queue,
+ struct audpcm_event, list);
+ list_del(&drv_evt->list);
+ kfree(drv_evt);
+ }
+ list_for_each_safe(ptr, next, &audio->free_event_queue) {
+ drv_evt = list_first_entry(&audio->free_event_queue,
+ struct audpcm_event, list);
+ list_del(&drv_evt->list);
+ kfree(drv_evt);
+ }
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+ return;
+}
+
+static long audpcm_process_event_req(struct audio *audio, void __user *arg)
+{
+ long rc;
+ struct msm_audio_event usr_evt;
+ struct audpcm_event *drv_evt = NULL;
+ int timeout;
+ unsigned long flags;
+
+ if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event)))
+ return -EFAULT;
+
+ timeout = (int) usr_evt.timeout_ms;
+
+ if (timeout > 0) {
+ rc = wait_event_interruptible_timeout(
+ audio->event_wait, audpcm_events_pending(audio),
+ msecs_to_jiffies(timeout));
+ if (rc == 0)
+ return -ETIMEDOUT;
+ } else {
+ rc = wait_event_interruptible(
+ audio->event_wait, audpcm_events_pending(audio));
+ }
+
+ if (rc < 0)
+ return rc;
+
+ if (audio->event_abort) {
+ audio->event_abort = 0;
+ return -ENODEV;
+ }
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ if (!list_empty(&audio->event_queue)) {
+ drv_evt = list_first_entry(&audio->event_queue,
+ struct audpcm_event, list);
+ list_del(&drv_evt->list);
+ }
+ if (drv_evt) {
+ usr_evt.event_type = drv_evt->event_type;
+ usr_evt.event_payload = drv_evt->payload;
+ list_add_tail(&drv_evt->list, &audio->free_event_queue);
+ } else
+ rc = -1;
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+ if (drv_evt && drv_evt->event_type == AUDIO_EVENT_WRITE_DONE) {
+ mutex_lock(&audio->lock);
+ audpcm_pmem_fixup(audio, drv_evt->payload.aio_buf.buf_addr,
+ drv_evt->payload.aio_buf.buf_len, 0);
+ mutex_unlock(&audio->lock);
+ }
+ if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt)))
+ rc = -EFAULT;
+
+ return rc;
+}
+
+static int audpcm_pmem_check(struct audio *audio,
+ void *vaddr, unsigned long len)
+{
+ struct audpcm_pmem_region *region_elt;
+ struct audpcm_pmem_region t = { .vaddr = vaddr, .len = len };
+
+ list_for_each_entry(region_elt, &audio->pmem_region_queue, list) {
+ if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) ||
+ OVERLAPS(region_elt, &t)) {
+ MM_ERR("region (vaddr %p len %ld)"
+ " clashes with registered region"
+ " (vaddr %p paddr %p len %ld)\n",
+ vaddr, len,
+ region_elt->vaddr,
+ (void *)region_elt->paddr,
+ region_elt->len);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int audpcm_pmem_add(struct audio *audio,
+ struct msm_audio_pmem_info *info)
+{
+ unsigned long paddr, kvaddr, len;
+ struct file *file;
+ struct audpcm_pmem_region *region;
+ int rc = -EINVAL;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ region = kmalloc(sizeof(*region), GFP_KERNEL);
+ if (!region)
+ return -ENOMEM;
+
+ if (get_pmem_file(info->fd, &paddr, &kvaddr, &len, &file)) {
+ kfree(region);
+ return -EINVAL;
+ }
+
+ rc = audpcm_pmem_check(audio, info->vaddr, len);
+ if (rc < 0) {
+ put_pmem_file(file);
+ kfree(region);
+ return rc;
+ }
+
+ region->vaddr = info->vaddr;
+ region->fd = info->fd;
+ region->paddr = paddr;
+ region->kvaddr = kvaddr;
+ region->len = len;
+ region->file = file;
+ region->ref_cnt = 0;
+ MM_DBG("add region paddr %lx vaddr %p, len %lu\n", region->paddr,
+ region->vaddr, region->len);
+ list_add_tail(®ion->list, &audio->pmem_region_queue);
+ return rc;
+}
+
+static int audpcm_pmem_remove(struct audio *audio,
+ struct msm_audio_pmem_info *info)
+{
+ struct audpcm_pmem_region *region;
+ struct list_head *ptr, *next;
+ int rc = -EINVAL;
+
+ MM_DBG("info fd %d vaddr %p\n", info->fd, info->vaddr);
+
+ list_for_each_safe(ptr, next, &audio->pmem_region_queue) {
+ region = list_entry(ptr, struct audpcm_pmem_region, list);
+
+ if ((region->fd == info->fd) &&
+ (region->vaddr == info->vaddr)) {
+ if (region->ref_cnt) {
+ MM_DBG("region %p in use ref_cnt %d\n", region,
+ region->ref_cnt);
+ break;
+ }
+ MM_DBG("remove region fd %d vaddr %p \n", info->fd,
+ info->vaddr);
+ list_del(®ion->list);
+ put_pmem_file(region->file);
+ kfree(region);
+ rc = 0;
+ break;
+ }
+ }
+
+ return rc;
+}
+
+static int audpcm_pmem_lookup_vaddr(struct audio *audio, void *addr,
+ unsigned long len, struct audpcm_pmem_region **region)
+{
+ struct audpcm_pmem_region *region_elt;
+
+ int match_count = 0;
+
+ *region = NULL;
+
+ /* returns physical address or zero */
+ list_for_each_entry(region_elt, &audio->pmem_region_queue,
+ list) {
+ if (addr >= region_elt->vaddr &&
+ addr < region_elt->vaddr + region_elt->len &&
+ addr + len <= region_elt->vaddr + region_elt->len) {
+ /* offset since we could pass vaddr inside a registerd
+ * pmem buffer
+ */
+ match_count++;
+ if (!*region)
+ *region = region_elt;
+ }
+ }
+
+ if (match_count > 1) {
+ MM_ERR("multiple hits for vaddr %p, len %ld\n", addr, len);
+ list_for_each_entry(region_elt,
+ &audio->pmem_region_queue, list) {
+ if (addr >= region_elt->vaddr &&
+ addr < region_elt->vaddr + region_elt->len &&
+ addr + len <= region_elt->vaddr + region_elt->len)
+ MM_ERR("\t%p, %ld --> %p\n",
+ region_elt->vaddr,
+ region_elt->len,
+ (void *)region_elt->paddr);
+ }
+ }
+
+ return *region ? 0 : -1;
+}
+
+static unsigned long audpcm_pmem_fixup(struct audio *audio, void *addr,
+ unsigned long len, int ref_up)
+{
+ struct audpcm_pmem_region *region;
+ unsigned long paddr;
+ int ret;
+
+ ret = audpcm_pmem_lookup_vaddr(audio, addr, len, ®ion);
+ if (ret) {
+ MM_ERR("lookup (%p, %ld) failed\n", addr, len);
+ return 0;
+ }
+ if (ref_up)
+ region->ref_cnt++;
+ else
+ region->ref_cnt--;
+ MM_DBG("found region %p ref_cnt %d\n", region, region->ref_cnt);
+ paddr = region->paddr + (addr - region->vaddr);
+ return paddr;
+}
+
+/* audio -> lock must be held at this point */
+static int audpcm_aio_buf_add(struct audio *audio, unsigned dir,
+ void __user *arg)
+{
+ unsigned long flags;
+ struct audpcm_buffer_node *buf_node;
+
+ buf_node = kmalloc(sizeof(*buf_node), GFP_KERNEL);
+
+ if (!buf_node)
+ return -ENOMEM;
+
+ if (copy_from_user(&buf_node->buf, arg, sizeof(buf_node->buf))) {
+ kfree(buf_node);
+ return -EFAULT;
+ }
+
+ MM_DBG("node %p dir %x buf_addr %p buf_len %d data_len %d\n",
+ buf_node, dir, buf_node->buf.buf_addr,
+ buf_node->buf.buf_len, buf_node->buf.data_len);
+
+ buf_node->paddr = audpcm_pmem_fixup(
+ audio, buf_node->buf.buf_addr,
+ buf_node->buf.buf_len, 1);
+ if (dir) {
+ /* write */
+ if (!buf_node->paddr ||
+ (buf_node->paddr & 0x1) ||
+ (buf_node->buf.data_len & 0x1) ||
+ (!buf_node->buf.data_len)) {
+ kfree(buf_node);
+ return -EINVAL;
+ }
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ list_add_tail(&buf_node->list, &audio->out_queue);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ audio->drv_ops.send_data(audio, 0);
+ }
+
+ MM_DBG("Add buf_node %p paddr %lx\n", buf_node, buf_node->paddr);
+
+ return 0;
+}
+
+static int audio_get_avsync_data(struct audio *audio,
+ struct msm_audio_stats *stats)
+{
+ int rc = -EINVAL;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) {
+ /* av_sync sample count */
+ stats->sample_count = (audio->avsync[2] << 16) |
+ (audio->avsync[3]);
+
+ /* av_sync byte_count */
+ stats->byte_count = (audio->avsync[5] << 16) |
+ (audio->avsync[6]);
+
+ audio->avsync_flag = 0;
+ rc = 0;
+ }
+ local_irq_restore(flags);
+ return rc;
+
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct audio *audio = file->private_data;
+ int rc = 0;
+
+ MM_DBG("cmd = %d\n", cmd);
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+
+ audio->avsync_flag = 0;
+ memset(&stats, 0, sizeof(stats));
+ if (audpp_query_avsync(audio->dec_id) < 0)
+ return rc;
+
+ rc = wait_event_interruptible_timeout(audio->avsync_wait,
+ (audio->avsync_flag == 1),
+ msecs_to_jiffies(AUDPP_AVSYNC_EVENT_TIMEOUT));
+
+ if (rc < 0)
+ return rc;
+ else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) {
+ if (audio_get_avsync_data(audio, &stats) < 0)
+ return rc;
+
+ if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ } else
+ return -EAGAIN;
+ }
+ if (cmd == AUDIO_SET_VOLUME) {
+ unsigned long flags;
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->volume = arg;
+ if (audio->running)
+ audpp_set_volume_and_pan(audio->dec_id, arg, 0,
+ POPP);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ return 0;
+ }
+ if (cmd == AUDIO_GET_EVENT) {
+ MM_DBG("AUDIO_GET_EVENT\n");
+ if (mutex_trylock(&audio->get_event_lock)) {
+ rc = audpcm_process_event_req(audio,
+ (void __user *) arg);
+ mutex_unlock(&audio->get_event_lock);
+ } else
+ rc = -EBUSY;
+ return rc;
+ }
+
+ if (cmd == AUDIO_ABORT_GET_EVENT) {
+ audio->event_abort = 1;
+ wake_up(&audio->event_wait);
+ return 0;
+ }
+
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START:
+ MM_DBG("AUDIO_START\n");
+ rc = audio_enable(audio);
+ if (!rc) {
+ rc = wait_event_interruptible_timeout(audio->wait,
+ audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+ msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+ MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc);
+
+ if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS)
+ rc = -ENODEV;
+ else
+ rc = 0;
+ }
+ break;
+ case AUDIO_STOP:
+ MM_DBG("AUDIO_STOP\n");
+ rc = audio_disable(audio);
+ audio->stopped = 1;
+ audio_ioport_reset(audio);
+ audio->stopped = 0;
+ break;
+ case AUDIO_FLUSH:
+ MM_DBG("AUDIO_FLUSH\n");
+ audio->wflush = 1;
+ audio_ioport_reset(audio);
+ if (audio->running) {
+ audpp_flush(audio->dec_id);
+ rc = wait_event_interruptible(audio->write_wait,
+ !audio->wflush);
+ if (rc < 0) {
+ MM_ERR("AUDIO_FLUSH interrupted\n");
+ rc = -EINTR;
+ }
+ } else {
+ audio->wflush = 0;
+ }
+ break;
+
+ case AUDIO_SET_CONFIG: {
+ struct msm_audio_config config;
+ if (copy_from_user(&config, (void *) arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (config.channel_count == 1) {
+ config.channel_count = AUDPP_CMD_PCM_INTF_MONO_V;
+ } else if (config.channel_count == 2) {
+ config.channel_count = AUDPP_CMD_PCM_INTF_STEREO_V;
+ } else {
+ rc = -EINVAL;
+ break;
+ }
+ if (config.bits == 8)
+ config.bits = AUDPP_CMD_WAV_PCM_WIDTH_8;
+ else if (config.bits == 16)
+ config.bits = AUDPP_CMD_WAV_PCM_WIDTH_16;
+ else if (config.bits == 24)
+ config.bits = AUDPP_CMD_WAV_PCM_WIDTH_24;
+ else {
+ rc = -EINVAL;
+ break;
+ }
+ audio->out_sample_rate = config.sample_rate;
+ audio->out_channel_mode = config.channel_count;
+ audio->out_bits = config.bits;
+ break;
+ }
+ case AUDIO_GET_CONFIG: {
+ struct msm_audio_config config;
+ config.buffer_size = (audio->out_dma_sz >> 1);
+ config.buffer_count = 2;
+ config.sample_rate = audio->out_sample_rate;
+ if (audio->out_channel_mode == AUDPP_CMD_PCM_INTF_MONO_V)
+ config.channel_count = 1;
+ else
+ config.channel_count = 2;
+ if (audio->out_bits == AUDPP_CMD_WAV_PCM_WIDTH_8)
+ config.bits = 8;
+ else if (audio->out_bits == AUDPP_CMD_WAV_PCM_WIDTH_24)
+ config.bits = 24;
+ else
+ config.bits = 16;
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+
+ if (copy_to_user((void *) arg, &config, sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+
+ case AUDIO_PAUSE:
+ MM_DBG("AUDIO_PAUSE %ld\n", arg);
+ rc = audpp_pause(audio->dec_id, (int) arg);
+ break;
+
+ case AUDIO_REGISTER_PMEM: {
+ struct msm_audio_pmem_info info;
+ MM_DBG("AUDIO_REGISTER_PMEM\n");
+ if (copy_from_user(&info, (void *) arg, sizeof(info)))
+ rc = -EFAULT;
+ else
+ rc = audpcm_pmem_add(audio, &info);
+ break;
+ }
+
+ case AUDIO_DEREGISTER_PMEM: {
+ struct msm_audio_pmem_info info;
+ MM_DBG("AUDIO_DEREGISTER_PMEM\n");
+ if (copy_from_user(&info, (void *) arg, sizeof(info)))
+ rc = -EFAULT;
+ else
+ rc = audpcm_pmem_remove(audio, &info);
+ break;
+ }
+
+ case AUDIO_ASYNC_WRITE:
+ if (audio->drv_status & ADRV_STATUS_FSYNC)
+ rc = -EBUSY;
+ else
+ rc = audpcm_aio_buf_add(audio, 1, (void __user *) arg);
+ break;
+
+ case AUDIO_ASYNC_READ:
+ MM_ERR("AUDIO_ASYNC_READ not supported\n");
+ rc = -EPERM;
+ break;
+
+ case AUDIO_GET_SESSION_ID:
+ if (copy_to_user((void *) arg, &audio->dec_id,
+ sizeof(unsigned short)))
+ return -EFAULT;
+ break;
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+/* Only useful in tunnel-mode */
+int audpcm_async_fsync(struct audio *audio)
+{
+ int rc = 0;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+
+ /* Blocking client sends more data */
+ mutex_lock(&audio->lock);
+ audio->drv_status |= ADRV_STATUS_FSYNC;
+ mutex_unlock(&audio->lock);
+
+ mutex_lock(&audio->write_lock);
+ /* pcm dmamiss message is sent continously
+ * when decoder is starved so no race
+ * condition concern
+ */
+ audio->teos = 0;
+
+ rc = wait_event_interruptible(audio->write_wait,
+ (audio->teos && audio->out_needed &&
+ list_empty(&audio->out_queue))
+ || audio->wflush || audio->stopped);
+
+ if (audio->stopped || audio->wflush)
+ rc = -EBUSY;
+
+ mutex_unlock(&audio->write_lock);
+ mutex_lock(&audio->lock);
+ audio->drv_status &= ~ADRV_STATUS_FSYNC;
+ mutex_unlock(&audio->lock);
+
+ return rc;
+}
+
+int audpcm_sync_fsync(struct audio *audio)
+{
+ struct buffer *frame;
+ int rc = 0;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+
+ mutex_lock(&audio->write_lock);
+
+ rc = wait_event_interruptible(audio->write_wait,
+ (!audio->out[0].used &&
+ !audio->out[1].used &&
+ audio->out_needed) || audio->wflush);
+
+ if (rc < 0)
+ goto done;
+ else if (audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+
+ if (audio->reserved) {
+ MM_DBG("send reserved byte\n");
+ frame = audio->out + audio->out_tail;
+ ((char *) frame->data)[0] = audio->rsv_byte;
+ ((char *) frame->data)[1] = 0;
+ frame->used = 2;
+ audio->drv_ops.send_data(audio, 0);
+
+ rc = wait_event_interruptible(audio->write_wait,
+ (!audio->out[0].used &&
+ !audio->out[1].used &&
+ audio->out_needed) || audio->wflush);
+
+ if (rc < 0)
+ goto done;
+ else if (audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+ }
+
+ /* pcm dmamiss message is sent continously
+ * when decoder is starved so no race
+ * condition concern
+ */
+ audio->teos = 0;
+
+ rc = wait_event_interruptible(audio->write_wait,
+ audio->teos || audio->wflush);
+
+ if (audio->wflush)
+ rc = -EBUSY;
+
+done:
+ mutex_unlock(&audio->write_lock);
+ return rc;
+}
+
+int audpcm_fsync(struct file *file, int datasync)
+{
+ struct audio *audio = file->private_data;
+
+ if (!audio->running)
+ return -EINVAL;
+
+ return audio->drv_ops.fsync(audio);
+}
+
+static ssize_t audio_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ struct buffer *frame;
+ size_t xfer;
+ char *cpy_ptr;
+ int rc = 0;
+ unsigned dsize;
+
+ if (audio->drv_status & ADRV_STATUS_AIO_INTF)
+ return -EPERM;
+
+ MM_DBG("cnt=%d\n", count);
+
+ mutex_lock(&audio->write_lock);
+ while (count > 0) {
+ frame = audio->out + audio->out_head;
+ cpy_ptr = frame->data;
+ dsize = 0;
+ rc = wait_event_interruptible(audio->write_wait,
+ (frame->used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+ if (rc < 0)
+ break;
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ break;
+ }
+
+ if (audio->reserved) {
+ MM_DBG("append reserved byte %x\n", audio->rsv_byte);
+ *cpy_ptr = audio->rsv_byte;
+ xfer = (count > (frame->size - 1)) ?
+ frame->size - 1 : count;
+ cpy_ptr++;
+ dsize = 1;
+ audio->reserved = 0;
+ } else
+ xfer = (count > frame->size) ? frame->size : count;
+
+ if (copy_from_user(cpy_ptr, buf, xfer)) {
+ rc = -EFAULT;
+ break;
+ }
+
+ dsize += xfer;
+ if (dsize & 1) {
+ audio->rsv_byte = ((char *) frame->data)[dsize - 1];
+ MM_DBG("odd length buf reserve last byte %x\n",
+ audio->rsv_byte);
+ audio->reserved = 1;
+ dsize--;
+ }
+ count -= xfer;
+ buf += xfer;
+
+ if (dsize > 0) {
+ audio->out_head ^= 1;
+ frame->used = dsize;
+ audio->drv_ops.send_data(audio, 0);
+ }
+ }
+ mutex_unlock(&audio->write_lock);
+ if (buf > start)
+ return buf - start;
+
+ return rc;
+}
+
+static void audpcm_reset_pmem_region(struct audio *audio)
+{
+ struct audpcm_pmem_region *region;
+ struct list_head *ptr, *next;
+
+ list_for_each_safe(ptr, next, &audio->pmem_region_queue) {
+ region = list_entry(ptr, struct audpcm_pmem_region, list);
+ list_del(®ion->list);
+ put_pmem_file(region->file);
+ kfree(region);
+ }
+
+ return;
+}
+
+static int audio_release(struct inode *inode, struct file *file)
+{
+ struct audio *audio = file->private_data;
+
+ MM_INFO("audio instance 0x%08x freeing\n", (int)audio);
+
+ mutex_lock(&audio->lock);
+ auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id);
+ audio_disable(audio);
+ audio->drv_ops.out_flush(audio);
+ audpcm_reset_pmem_region(audio);
+
+ msm_adsp_put(audio->audplay);
+ audpp_adec_free(audio->dec_id);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&audio->suspend_ctl.node);
+#endif
+ audio->opened = 0;
+ audio->event_abort = 1;
+ wake_up(&audio->event_wait);
+ audpcm_reset_event_queue(audio);
+ if (audio->data) {
+ iounmap(audio->data);
+ pmem_kfree(audio->phys);
+ }
+ mutex_unlock(&audio->lock);
+#ifdef CONFIG_DEBUG_FS
+ if (audio->dentry)
+ debugfs_remove(audio->dentry);
+#endif
+ kfree(audio);
+ return 0;
+}
+
+static void audpcm_post_event(struct audio *audio, int type,
+ union msm_audio_event_payload payload)
+{
+ struct audpcm_event *e_node = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+
+ if (!list_empty(&audio->free_event_queue)) {
+ e_node = list_first_entry(&audio->free_event_queue,
+ struct audpcm_event, list);
+ list_del(&e_node->list);
+ } else {
+ e_node = kmalloc(sizeof(struct audpcm_event), GFP_ATOMIC);
+ if (!e_node) {
+ MM_ERR("No mem to post event %d\n", type);
+ return;
+ }
+ }
+
+ e_node->event_type = type;
+ e_node->payload = payload;
+
+ list_add_tail(&e_node->list, &audio->event_queue);
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+ wake_up(&audio->event_wait);
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void audpcm_suspend(struct early_suspend *h)
+{
+ struct audpcm_suspend_ctl *ctl =
+ container_of(h, struct audpcm_suspend_ctl, node);
+ union msm_audio_event_payload payload;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ audpcm_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload);
+}
+
+static void audpcm_resume(struct early_suspend *h)
+{
+ struct audpcm_suspend_ctl *ctl =
+ container_of(h, struct audpcm_suspend_ctl, node);
+ union msm_audio_event_payload payload;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ audpcm_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload);
+}
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t audpcm_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t audpcm_debug_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ const int debug_bufmax = 4096;
+ static char buffer[4096];
+ int n = 0;
+ struct audio *audio = file->private_data;
+
+ mutex_lock(&audio->lock);
+ n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "enabled %d\n", audio->enabled);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "stopped %d\n", audio->stopped);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_buf_sz %d\n", audio->out[0].size);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "volume %x \n", audio->volume);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "sample rate %d \n", audio->out_sample_rate);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "channel mode %d \n", audio->out_channel_mode);
+ mutex_unlock(&audio->lock);
+ /* Following variables are only useful for debugging when
+ * when playback halts unexpectedly. Thus, no mutual exclusion
+ * enforced
+ */
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "wflush %d\n", audio->wflush);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "running %d \n", audio->running);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "dec state %d \n", audio->dec_state);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_needed %d \n", audio->out_needed);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_head %d \n", audio->out_head);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_tail %d \n", audio->out_tail);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out[0].used %d \n", audio->out[0].used);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out[1].used %d \n", audio->out[1].used);
+ buffer[n] = 0;
+ return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static const struct file_operations audpcm_debug_fops = {
+ .read = audpcm_debug_read,
+ .open = audpcm_debug_open,
+};
+#endif
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct audio *audio = NULL;
+ int rc, i, dec_attrb, decid;
+ struct audpcm_event *e_node = NULL;
+ unsigned pmem_sz = DMASZ_MAX;
+
+#ifdef CONFIG_DEBUG_FS
+ /* 4 bytes represents decoder number, 1 byte for terminate string */
+ char name[sizeof "msm_pcm_dec_" + 5];
+#endif
+
+ /* Allocate audio instance, set to zero */
+ audio = kzalloc(sizeof(struct audio), GFP_KERNEL);
+ if (!audio) {
+ MM_ERR("no memory to allocate audio instance \n");
+ rc = -ENOMEM;
+ goto done;
+ }
+ MM_INFO("audio instance 0x%08x created\n", (int)audio);
+
+ /* Allocate the decoder */
+ dec_attrb = AUDDEC_DEC_PCM;
+ if (file->f_mode & FMODE_READ) {
+ MM_ERR("Non-Tunneled mode not supported\n");
+ rc = -EPERM;
+ kfree(audio);
+ goto done;
+ } else
+ dec_attrb |= MSM_AUD_MODE_TUNNEL;
+
+ decid = audpp_adec_alloc(dec_attrb, &audio->module_name,
+ &audio->queue_id);
+ if (decid < 0) {
+ MM_ERR("No free decoder available, freeing instance 0x%08x\n",
+ (int)audio);
+ rc = -ENODEV;
+ kfree(audio);
+ goto done;
+ }
+ audio->dec_id = decid & MSM_AUD_DECODER_MASK;
+
+ /* AIO interface */
+ if (file->f_flags & O_NONBLOCK) {
+ MM_DBG("set to aio interface\n");
+ audio->drv_status |= ADRV_STATUS_AIO_INTF;
+ audio->drv_ops.send_data = audpcm_async_send_data;
+ audio->drv_ops.out_flush = audpcm_async_flush;
+ audio->drv_ops.fsync = audpcm_async_fsync;
+ } else {
+ MM_DBG("set to std io interface\n");
+ while (pmem_sz >= DMASZ_MIN) {
+ MM_DBG("pmemsz = %d\n", pmem_sz);
+ audio->phys = pmem_kalloc(pmem_sz, PMEM_MEMTYPE_EBI1|
+ PMEM_ALIGNMENT_4K);
+ if (!IS_ERR((void *)audio->phys)) {
+ audio->data = ioremap(audio->phys, pmem_sz);
+ if (!audio->data) {
+ MM_ERR("could not allocate write \
+ buffers freeing instance \
+ 0x%08x\n", (int)audio);
+ rc = -ENOMEM;
+ pmem_kfree(audio->phys);
+ audpp_adec_free(audio->dec_id);
+ kfree(audio);
+ goto done;
+ }
+ MM_DBG("write buf: phy addr 0x%08x \
+ kernel addr 0x%08x\n",
+ audio->phys, (int)audio->data);
+ break;
+ } else if (pmem_sz == DMASZ_MIN) {
+ MM_ERR("could not allocate write buffers \
+ freeing instance 0x%08x\n", (int)audio);
+ rc = -ENOMEM;
+ audpp_adec_free(audio->dec_id);
+ kfree(audio);
+ goto done;
+ } else
+ pmem_sz >>= 1;
+ }
+ audio->out_dma_sz = pmem_sz;
+ audio->drv_ops.send_data = audplay_send_data;
+ audio->drv_ops.out_flush = audio_flush;
+ audio->drv_ops.fsync = audpcm_sync_fsync;
+ audio->out[0].data = audio->data + 0;
+ audio->out[0].addr = audio->phys + 0;
+ audio->out[0].size = (audio->out_dma_sz >> 1);
+
+ audio->out[1].data = audio->data + audio->out[0].size;
+ audio->out[1].addr = audio->phys + audio->out[0].size;
+ audio->out[1].size = audio->out[0].size;
+ }
+
+ rc = msm_adsp_get(audio->module_name, &audio->audplay,
+ &audpcmdec_adsp_ops, audio);
+ if (rc) {
+ MM_ERR("failed to get %s module, freeing instance 0x%08x\n",
+ audio->module_name, (int)audio);
+ goto err;
+ }
+
+ /* Initialize all locks of audio instance */
+ mutex_init(&audio->lock);
+ mutex_init(&audio->write_lock);
+ mutex_init(&audio->get_event_lock);
+ spin_lock_init(&audio->dsp_lock);
+ init_waitqueue_head(&audio->write_wait);
+ INIT_LIST_HEAD(&audio->out_queue);
+ INIT_LIST_HEAD(&audio->pmem_region_queue);
+ INIT_LIST_HEAD(&audio->free_event_queue);
+ INIT_LIST_HEAD(&audio->event_queue);
+ init_waitqueue_head(&audio->wait);
+ init_waitqueue_head(&audio->event_wait);
+ spin_lock_init(&audio->event_queue_lock);
+ init_waitqueue_head(&audio->avsync_wait);
+
+ audio->out_sample_rate = 44100;
+ audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V;
+ audio->out_bits = AUDPP_CMD_WAV_PCM_WIDTH_16;
+ audio->volume = 0x7FFF;
+ audio->drv_ops.out_flush(audio);
+
+ file->private_data = audio;
+ audio->opened = 1;
+
+ audio->device_events = AUDDEV_EVT_DEV_RDY
+ |AUDDEV_EVT_DEV_RLS|
+ AUDDEV_EVT_STREAM_VOL_CHG;
+
+ rc = auddev_register_evt_listner(audio->device_events,
+ AUDDEV_CLNT_DEC,
+ audio->dec_id,
+ pcm_listner,
+ (void *)audio);
+ if (rc) {
+ MM_ERR("failed to register listnet\n");
+ goto event_err;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ snprintf(name, sizeof name, "msm_pcm_dec_%04x", audio->dec_id);
+ audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *) audio, &audpcm_debug_fops);
+
+ if (IS_ERR(audio->dentry))
+ MM_ERR("debugfs_create_file failed\n");
+#endif
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+ audio->suspend_ctl.node.resume = audpcm_resume;
+ audio->suspend_ctl.node.suspend = audpcm_suspend;
+ audio->suspend_ctl.audio = audio;
+ register_early_suspend(&audio->suspend_ctl.node);
+#endif
+ for (i = 0; i < AUDPCM_EVENT_NUM; i++) {
+ e_node = kmalloc(sizeof(struct audpcm_event), GFP_KERNEL);
+ if (e_node)
+ list_add_tail(&e_node->list, &audio->free_event_queue);
+ else {
+ MM_ERR("event pkt alloc failed\n");
+ break;
+ }
+ }
+done:
+ return rc;
+event_err:
+ msm_adsp_put(audio->audplay);
+err:
+ if (audio->data) {
+ iounmap(audio->data);
+ pmem_kfree(audio->phys);
+ }
+ audpp_adec_free(audio->dec_id);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_pcm_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_release,
+ .write = audio_write,
+ .unlocked_ioctl = audio_ioctl,
+ .fsync = audpcm_fsync,
+};
+
+struct miscdevice audio_pcm_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_pcm_dec",
+ .fops = &audio_pcm_fops,
+};
+
+static int __init audio_init(void)
+{
+ return misc_register(&audio_pcm_misc);
+}
+
+device_initcall(audio_init);
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_pcm_in.c b/arch/arm/mach-msm/qdsp5v2/audio_pcm_in.c
new file mode 100644
index 0000000..71a07e5
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_pcm_in.c
@@ -0,0 +1,940 @@
+/*
+ * pcm audio input device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/msm_audio.h>
+#include <linux/android_pmem.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+
+#include <mach/msm_adsp.h>
+#include <mach/qdsp5v2/qdsp5audreccmdi.h>
+#include <mach/qdsp5v2/qdsp5audrecmsg.h>
+#include <mach/qdsp5v2/audpreproc.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/debug_mm.h>
+#include <mach/qdsp5v2/audio_acdbi.h>
+
+/* FRAME_NUM must be a power of two */
+#define FRAME_NUM (8)
+#define FRAME_HEADER_SIZE (8) /*4 half words*/
+/* size of a mono frame with 256 samples */
+#define MONO_DATA_SIZE_256 (512) /* in bytes*/
+/*size of a mono frame with 512 samples */
+#define MONO_DATA_SIZE_512 (1024) /* in bytes*/
+/*size of a mono frame with 1024 samples */
+#define MONO_DATA_SIZE_1024 (2048) /* in bytes */
+
+/*size of a stereo frame with 256 samples per channel */
+#define STEREO_DATA_SIZE_256 (1024) /* in bytes*/
+/*size of a stereo frame with 512 samples per channel */
+#define STEREO_DATA_SIZE_512 (2048) /* in bytes*/
+/*size of a stereo frame with 1024 samples per channel */
+#define STEREO_DATA_SIZE_1024 (4096) /* in bytes */
+
+#define MAX_FRAME_SIZE ((STEREO_DATA_SIZE_1024) + FRAME_HEADER_SIZE)
+#define DMASZ (MAX_FRAME_SIZE * FRAME_NUM)
+
+struct buffer {
+ void *data;
+ uint32_t size;
+ uint32_t read;
+ uint32_t addr;
+};
+
+struct audio_in {
+ struct buffer in[FRAME_NUM];
+
+ spinlock_t dsp_lock;
+
+ atomic_t in_bytes;
+ atomic_t in_samples;
+
+ struct mutex lock;
+ struct mutex read_lock;
+ wait_queue_head_t wait;
+ wait_queue_head_t wait_enable;
+
+ struct msm_adsp_module *audrec;
+
+ /* configuration to use on next enable */
+ uint32_t samp_rate;
+ uint32_t channel_mode;
+ uint32_t buffer_size; /* 2048 for mono, 4096 for stereo */
+ uint32_t enc_type;
+
+ uint32_t dsp_cnt;
+ uint32_t in_head; /* next buffer dsp will write */
+ uint32_t in_tail; /* next buffer read() will read */
+ uint32_t in_count; /* number of buffers available to read() */
+ uint32_t mode;
+
+ const char *module_name;
+ unsigned queue_ids;
+ uint16_t enc_id; /* Session Id */
+
+ uint16_t source; /* Encoding source bit mask */
+ uint32_t device_events; /* device events interested in */
+ uint32_t in_call;
+ uint32_t dev_cnt;
+ int voice_state;
+ spinlock_t dev_lock;
+
+ struct audrec_session_info session_info; /*audrec session info*/
+ /* data allocated for various buffers */
+ char *data;
+ dma_addr_t phys;
+
+ int opened;
+ int enabled;
+ int running;
+ int stopped; /* set when stopped, cleared on flush */
+ int abort; /* set when error, like sample rate mismatch */
+ int dual_mic_config;
+};
+
+static struct audio_in the_audio_in;
+
+struct audio_frame {
+ uint16_t frame_count_lsw;
+ uint16_t frame_count_msw;
+ uint16_t frame_length;
+ uint16_t erased_pcm;
+ unsigned char raw_bitstream[]; /* samples */
+} __attribute__((packed));
+
+/* Audrec Queue command sent macro's */
+#define audrec_send_bitstreamqueue(audio, cmd, len) \
+ msm_adsp_write(audio->audrec, ((audio->queue_ids & 0xFFFF0000) >> 16),\
+ cmd, len)
+
+#define audrec_send_audrecqueue(audio, cmd, len) \
+ msm_adsp_write(audio->audrec, (audio->queue_ids & 0x0000FFFF),\
+ cmd, len)
+
+/* DSP command send functions */
+static int audpcm_in_enc_config(struct audio_in *audio, int enable);
+static int audpcm_in_param_config(struct audio_in *audio);
+static int audpcm_in_mem_config(struct audio_in *audio);
+static int audpcm_in_record_config(struct audio_in *audio, int enable);
+static int audpcm_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt);
+
+static void audpcm_in_get_dsp_frames(struct audio_in *audio);
+
+static void audpcm_in_flush(struct audio_in *audio);
+
+static void pcm_in_listener(u32 evt_id, union auddev_evt_data *evt_payload,
+ void *private_data)
+{
+ struct audio_in *audio = (struct audio_in *) private_data;
+ unsigned long flags;
+
+ MM_DBG("evt_id = 0x%8x\n", evt_id);
+ switch (evt_id) {
+ case AUDDEV_EVT_DEV_RDY: {
+ MM_DBG("AUDDEV_EVT_DEV_RDY\n");
+ spin_lock_irqsave(&audio->dev_lock, flags);
+ audio->dev_cnt++;
+ if (!audio->in_call)
+ audio->source |= (0x1 << evt_payload->routing_id);
+ spin_unlock_irqrestore(&audio->dev_lock, flags);
+
+ if ((audio->running == 1) && (audio->enabled == 1))
+ audpcm_in_record_config(audio, 1);
+
+ break;
+ }
+ case AUDDEV_EVT_DEV_RLS: {
+ MM_DBG("AUDDEV_EVT_DEV_RLS\n");
+ spin_lock_irqsave(&audio->dev_lock, flags);
+ audio->dev_cnt--;
+ if (!audio->in_call)
+ audio->source &= ~(0x1 << evt_payload->routing_id);
+ spin_unlock_irqrestore(&audio->dev_lock, flags);
+
+ if (!audio->running || !audio->enabled)
+ break;
+
+ /* Turn of as per source */
+ if (audio->source)
+ audpcm_in_record_config(audio, 1);
+ else
+ /* Turn off all */
+ audpcm_in_record_config(audio, 0);
+
+ break;
+ }
+ case AUDDEV_EVT_VOICE_STATE_CHG: {
+ MM_DBG("AUDDEV_EVT_VOICE_STATE_CHG, state = %d\n",
+ evt_payload->voice_state);
+ audio->voice_state = evt_payload->voice_state;
+ if (audio->in_call && audio->running) {
+ if (audio->voice_state == VOICE_STATE_INCALL)
+ audpcm_in_record_config(audio, 1);
+ else if (audio->voice_state == VOICE_STATE_OFFCALL) {
+ audpcm_in_record_config(audio, 0);
+ wake_up(&audio->wait);
+ }
+ }
+ break;
+ }
+ case AUDDEV_EVT_FREQ_CHG: {
+ MM_DBG("Encoder Driver got sample rate change event\n");
+ MM_DBG("sample rate %d\n", evt_payload->freq_info.sample_rate);
+ MM_DBG("dev_type %d\n", evt_payload->freq_info.dev_type);
+ MM_DBG("acdb_dev_id %d\n", evt_payload->freq_info.acdb_dev_id);
+ if (audio->running == 1) {
+ /* Stop Recording sample rate does not match
+ with device sample rate */
+ if (evt_payload->freq_info.sample_rate !=
+ audio->samp_rate) {
+ audpcm_in_record_config(audio, 0);
+ audio->abort = 1;
+ wake_up(&audio->wait);
+ }
+ }
+ break;
+ }
+ default:
+ MM_ERR("wrong event %d\n", evt_id);
+ break;
+ }
+}
+
+/* ------------------- dsp preproc event handler--------------------- */
+static void audpreproc_dsp_event(void *data, unsigned id, void *msg)
+{
+ struct audio_in *audio = data;
+
+ switch (id) {
+ case AUDPREPROC_ERROR_MSG: {
+ struct audpreproc_err_msg *err_msg = msg;
+
+ MM_ERR("ERROR_MSG: stream id %d err idx %d\n",
+ err_msg->stream_id, err_msg->aud_preproc_err_idx);
+ /* Error case */
+ wake_up(&audio->wait_enable);
+ break;
+ }
+ case AUDPREPROC_CMD_CFG_DONE_MSG: {
+ MM_DBG("CMD_CFG_DONE_MSG \n");
+ break;
+ }
+ case AUDPREPROC_CMD_ENC_CFG_DONE_MSG: {
+ struct audpreproc_cmd_enc_cfg_done_msg *enc_cfg_msg = msg;
+
+ MM_DBG("CMD_ENC_CFG_DONE_MSG: stream id %d enc type \
+ 0x%8x\n", enc_cfg_msg->stream_id,
+ enc_cfg_msg->rec_enc_type);
+ /* Encoder enable success */
+ if (enc_cfg_msg->rec_enc_type & ENCODE_ENABLE)
+ audpcm_in_param_config(audio);
+ else { /* Encoder disable success */
+ audio->running = 0;
+ audpcm_in_record_config(audio, 0);
+ }
+ break;
+ }
+ case AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG: {
+ MM_DBG("CMD_ENC_PARAM_CFG_DONE_MSG \n");
+ audpcm_in_mem_config(audio);
+ break;
+ }
+ case AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG: {
+ MM_DBG("AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG \n");
+ wake_up(&audio->wait_enable);
+ break;
+ }
+ default:
+ MM_ERR("Unknown Event id %d\n", id);
+ }
+}
+
+/* ------------------- dsp audrec event handler--------------------- */
+static void audrec_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent)(void *ptr, size_t len))
+{
+ struct audio_in *audio = data;
+
+ switch (id) {
+ case AUDREC_CMD_MEM_CFG_DONE_MSG: {
+ MM_DBG("CMD_MEM_CFG_DONE MSG DONE\n");
+ audio->running = 1;
+ if ((!audio->in_call && (audio->dev_cnt > 0)) ||
+ (audio->in_call &&
+ (audio->voice_state == VOICE_STATE_INCALL)))
+ audpcm_in_record_config(audio, 1);
+ break;
+ }
+ case AUDREC_FATAL_ERR_MSG: {
+ struct audrec_fatal_err_msg fatal_err_msg;
+
+ getevent(&fatal_err_msg, AUDREC_FATAL_ERR_MSG_LEN);
+ MM_ERR("FATAL_ERR_MSG: err id %d\n",
+ fatal_err_msg.audrec_err_id);
+ /* Error stop the encoder */
+ audio->stopped = 1;
+ wake_up(&audio->wait);
+ break;
+ }
+ case AUDREC_UP_PACKET_READY_MSG: {
+ struct audrec_up_pkt_ready_msg pkt_ready_msg;
+
+ getevent(&pkt_ready_msg, AUDREC_UP_PACKET_READY_MSG_LEN);
+ MM_DBG("UP_PACKET_READY_MSG: write cnt lsw %d \
+ write cnt msw %d read cnt lsw %d read cnt msw %d \n",\
+ pkt_ready_msg.audrec_packet_write_cnt_lsw, \
+ pkt_ready_msg.audrec_packet_write_cnt_msw, \
+ pkt_ready_msg.audrec_up_prev_read_cnt_lsw, \
+ pkt_ready_msg.audrec_up_prev_read_cnt_msw);
+
+ audpcm_in_get_dsp_frames(audio);
+ break;
+ }
+ case ADSP_MESSAGE_ID: {
+ MM_DBG("Received ADSP event :module audrectask\n");
+ break;
+ }
+ default:
+ MM_ERR("Unknown Event id %d\n", id);
+ }
+}
+
+static void audpcm_in_get_dsp_frames(struct audio_in *audio)
+{
+ struct audio_frame *frame;
+ uint32_t index;
+ unsigned long flags;
+
+ index = audio->in_head;
+
+ frame = (void *) (((char *)audio->in[index].data) - \
+ sizeof(*frame));
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->in[index].size = frame->frame_length;
+
+ /* statistics of read */
+ atomic_add(audio->in[index].size, &audio->in_bytes);
+ atomic_add(1, &audio->in_samples);
+
+ audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1);
+
+ /* If overflow, move the tail index foward. */
+ if (audio->in_head == audio->in_tail)
+ audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1);
+ else
+ audio->in_count++;
+
+ audpcm_dsp_read_buffer(audio, audio->dsp_cnt++);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+
+ wake_up(&audio->wait);
+}
+
+struct msm_adsp_ops audrec_adsp_ops = {
+ .event = audrec_dsp_event,
+};
+
+static int audpcm_in_enc_config(struct audio_in *audio, int enable)
+{
+ struct audpreproc_audrec_cmd_enc_cfg cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ENC_CFG_2;
+ cmd.stream_id = audio->enc_id;
+
+ if (enable)
+ cmd.audrec_enc_type = audio->enc_type | ENCODE_ENABLE;
+ else
+ cmd.audrec_enc_type &= ~(ENCODE_ENABLE);
+
+ return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+static int audpcm_in_param_config(struct audio_in *audio)
+{
+ struct audpreproc_audrec_cmd_parm_cfg_wav cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.cmd_id = AUDPREPROC_AUDREC_CMD_PARAM_CFG;
+ cmd.common.stream_id = audio->enc_id;
+
+ cmd.aud_rec_samplerate_idx = audio->samp_rate;
+ if (audio->dual_mic_config)
+ cmd.aud_rec_stereo_mode = DUAL_MIC_STEREO_RECORDING;
+ else
+ cmd.aud_rec_stereo_mode = audio->channel_mode;
+
+ if (audio->channel_mode == AUDREC_CMD_MODE_MONO)
+ cmd.aud_rec_frame_size = audio->buffer_size/2;
+ else
+ cmd.aud_rec_frame_size = audio->buffer_size/4;
+ return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+/* To Do: msm_snddev_route_enc(audio->enc_id); */
+static int audpcm_in_record_config(struct audio_in *audio, int enable)
+{
+ struct audpreproc_afe_cmd_audio_record_cfg cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG;
+ cmd.stream_id = audio->enc_id;
+ if (enable)
+ cmd.destination_activity = AUDIO_RECORDING_TURN_ON;
+ else
+ cmd.destination_activity = AUDIO_RECORDING_TURN_OFF;
+
+ cmd.source_mix_mask = audio->source;
+ if (audio->enc_id == 2) {
+ if ((cmd.source_mix_mask &
+ INTERNAL_CODEC_TX_SOURCE_MIX_MASK) ||
+ (cmd.source_mix_mask & AUX_CODEC_TX_SOURCE_MIX_MASK) ||
+ (cmd.source_mix_mask & VOICE_UL_SOURCE_MIX_MASK) ||
+ (cmd.source_mix_mask & VOICE_DL_SOURCE_MIX_MASK)) {
+ cmd.pipe_id = SOURCE_PIPE_1;
+ }
+ if (cmd.source_mix_mask &
+ AUDPP_A2DP_PIPE_SOURCE_MIX_MASK)
+ cmd.pipe_id |= SOURCE_PIPE_0;
+ }
+
+ return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+static int audpcm_in_mem_config(struct audio_in *audio)
+{
+ struct audrec_cmd_arecmem_cfg cmd;
+ uint16_t *data = (void *) audio->data;
+ int n;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDREC_CMD_MEM_CFG_CMD;
+ cmd.audrec_up_pkt_intm_count = 1;
+ cmd.audrec_ext_pkt_start_addr_msw = audio->phys >> 16;
+ cmd.audrec_ext_pkt_start_addr_lsw = audio->phys;
+ cmd.audrec_ext_pkt_buf_number = FRAME_NUM;
+
+ /* prepare buffer pointers:
+ * Mono: 1024 samples + 4 halfword header
+ * Stereo: 2048 samples + 4 halfword header
+ */
+ for (n = 0; n < FRAME_NUM; n++) {
+ /* word increment*/
+ audio->in[n].data = data + (FRAME_HEADER_SIZE/2);
+ data += ((FRAME_HEADER_SIZE/2) + (audio->buffer_size/2));
+ MM_DBG("0x%8x\n", (int)(audio->in[n].data - FRAME_HEADER_SIZE));
+ }
+
+ return audrec_send_audrecqueue(audio, &cmd, sizeof(cmd));
+}
+
+static int audpcm_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt)
+{
+ struct up_audrec_packet_ext_ptr cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = UP_AUDREC_PACKET_EXT_PTR;
+ cmd.audrec_up_curr_read_count_msw = read_cnt >> 16;
+ cmd.audrec_up_curr_read_count_lsw = read_cnt;
+
+ return audrec_send_bitstreamqueue(audio, &cmd, sizeof(cmd));
+}
+
+/* must be called with audio->lock held */
+static int audpcm_in_enable(struct audio_in *audio)
+{
+ if (audio->enabled)
+ return 0;
+
+ if (audpreproc_enable(audio->enc_id, &audpreproc_dsp_event, audio)) {
+ MM_ERR("msm_adsp_enable(audpreproc) failed\n");
+ return -ENODEV;
+ }
+
+ if (msm_adsp_enable(audio->audrec)) {
+ MM_ERR("msm_adsp_enable(audrec) failed\n");
+ audpreproc_disable(audio->enc_id, audio);
+ return -ENODEV;
+ }
+ audio->enabled = 1;
+ audpcm_in_enc_config(audio, 1);
+
+ return 0;
+}
+
+/* must be called with audio->lock held */
+static int audpcm_in_disable(struct audio_in *audio)
+{
+ if (audio->enabled) {
+ audio->enabled = 0;
+ audpcm_in_enc_config(audio, 0);
+ wake_up(&audio->wait);
+ wait_event_interruptible_timeout(audio->wait_enable,
+ audio->running == 0, 1*HZ);
+ msm_adsp_disable(audio->audrec);
+ audpreproc_disable(audio->enc_id, audio);
+ }
+ return 0;
+}
+
+static void audpcm_in_flush(struct audio_in *audio)
+{
+ int i;
+
+ audio->dsp_cnt = 0;
+ audio->in_head = 0;
+ audio->in_tail = 0;
+ audio->in_count = 0;
+ for (i = 0; i < FRAME_NUM; i++) {
+ audio->in[i].size = 0;
+ audio->in[i].read = 0;
+ }
+ MM_DBG("in_bytes %d\n", atomic_read(&audio->in_bytes));
+ MM_DBG("in_samples %d\n", atomic_read(&audio->in_samples));
+ atomic_set(&audio->in_bytes, 0);
+ atomic_set(&audio->in_samples, 0);
+}
+
+/* ------------------- device --------------------- */
+static long audpcm_in_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct audio_in *audio = file->private_data;
+ int rc = 0;
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ stats.byte_count = atomic_read(&audio->in_bytes);
+ stats.sample_count = atomic_read(&audio->in_samples);
+ if (copy_to_user((void *) arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return rc;
+ }
+
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START: {
+ uint32_t freq;
+ /* Poll at 48KHz always */
+ freq = 48000;
+ MM_DBG("AUDIO_START\n");
+ if (audio->in_call && (audio->voice_state !=
+ VOICE_STATE_INCALL)) {
+ rc = -EPERM;
+ break;
+ }
+ rc = msm_snddev_request_freq(&freq, audio->enc_id,
+ SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+ MM_DBG("sample rate configured %d sample rate requested %d\n",
+ freq, audio->samp_rate);
+ if (rc < 0) {
+ MM_DBG("sample rate can not be set, return code %d\n",\
+ rc);
+ msm_snddev_withdraw_freq(audio->enc_id,
+ SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+ MM_DBG("msm_snddev_withdraw_freq\n");
+ break;
+ }
+ audio->dual_mic_config = msm_get_dual_mic_config(audio->enc_id);
+ /*DSP supports fluence block and by default ACDB layer will
+ applies the fluence pre-processing feature, if dual MIC config
+ is enabled implies client want to record pure dual MIC sample
+ for this we need to over ride the fluence pre processing
+ feature at ACDB layer to not to apply if fluence preprocessing
+ feature supported*/
+ if (audio->dual_mic_config) {
+ MM_INFO("dual MIC config = %d, over ride the fluence "
+ "feature\n", audio->dual_mic_config);
+ fluence_feature_update(audio->dual_mic_config,
+ audio->enc_id);
+ }
+ /*update aurec session info in audpreproc layer*/
+ audio->session_info.session_id = audio->enc_id;
+ audio->session_info.sampling_freq = audio->samp_rate;
+ audpreproc_update_audrec_info(&audio->session_info);
+ rc = audpcm_in_enable(audio);
+ if (!rc) {
+ rc =
+ wait_event_interruptible_timeout(audio->wait_enable,
+ audio->running != 0, 1*HZ);
+ MM_DBG("state %d rc = %d\n", audio->running, rc);
+
+ if (audio->running == 0)
+ rc = -ENODEV;
+ else
+ rc = 0;
+ }
+ audio->stopped = 0;
+ break;
+ }
+ case AUDIO_STOP: {
+ /*reset the sampling frequency information at audpreproc layer*/
+ audio->session_info.sampling_freq = 0;
+ audpreproc_update_audrec_info(&audio->session_info);
+ rc = audpcm_in_disable(audio);
+ rc = msm_snddev_withdraw_freq(audio->enc_id,
+ SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+ MM_DBG("msm_snddev_withdraw_freq\n");
+ audio->stopped = 1;
+ audio->abort = 0;
+ break;
+ }
+ case AUDIO_FLUSH: {
+ if (audio->stopped) {
+ /* Make sure we're stopped and we wake any threads
+ * that might be blocked holding the read_lock.
+ * While audio->stopped read threads will always
+ * exit immediately.
+ */
+ wake_up(&audio->wait);
+ mutex_lock(&audio->read_lock);
+ audpcm_in_flush(audio);
+ mutex_unlock(&audio->read_lock);
+ }
+ break;
+ }
+ case AUDIO_SET_CONFIG: {
+ struct msm_audio_config cfg;
+ if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (cfg.channel_count == 1) {
+ cfg.channel_count = AUDREC_CMD_MODE_MONO;
+ if ((cfg.buffer_size == MONO_DATA_SIZE_256) ||
+ (cfg.buffer_size == MONO_DATA_SIZE_512) ||
+ (cfg.buffer_size == MONO_DATA_SIZE_1024)) {
+ audio->buffer_size = cfg.buffer_size;
+ } else {
+ rc = -EINVAL;
+ break;
+ }
+ } else if (cfg.channel_count == 2) {
+ cfg.channel_count = AUDREC_CMD_MODE_STEREO;
+ if ((cfg.buffer_size == STEREO_DATA_SIZE_256) ||
+ (cfg.buffer_size == STEREO_DATA_SIZE_512) ||
+ (cfg.buffer_size == STEREO_DATA_SIZE_1024)) {
+ audio->buffer_size = cfg.buffer_size;
+ } else {
+ rc = -EINVAL;
+ break;
+ }
+ } else {
+ rc = -EINVAL;
+ break;
+ }
+ audio->samp_rate = cfg.sample_rate;
+ audio->channel_mode = cfg.channel_count;
+ break;
+ }
+ case AUDIO_GET_CONFIG: {
+ struct msm_audio_config cfg;
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.buffer_size = audio->buffer_size;
+ cfg.buffer_count = FRAME_NUM;
+ cfg.sample_rate = audio->samp_rate;
+ if (audio->channel_mode == AUDREC_CMD_MODE_MONO)
+ cfg.channel_count = 1;
+ else
+ cfg.channel_count = 2;
+ if (copy_to_user((void *) arg, &cfg, sizeof(cfg)))
+ rc = -EFAULT;
+ break;
+ }
+ case AUDIO_SET_INCALL: {
+ struct msm_voicerec_mode cfg;
+ unsigned long flags;
+ if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (cfg.rec_mode != VOC_REC_BOTH &&
+ cfg.rec_mode != VOC_REC_UPLINK &&
+ cfg.rec_mode != VOC_REC_DOWNLINK) {
+ MM_ERR("invalid rec_mode\n");
+ rc = -EINVAL;
+ break;
+ } else {
+ spin_lock_irqsave(&audio->dev_lock, flags);
+ if (cfg.rec_mode == VOC_REC_UPLINK)
+ audio->source = VOICE_UL_SOURCE_MIX_MASK;
+ else if (cfg.rec_mode == VOC_REC_DOWNLINK)
+ audio->source = VOICE_DL_SOURCE_MIX_MASK;
+ else
+ audio->source = VOICE_DL_SOURCE_MIX_MASK |
+ VOICE_UL_SOURCE_MIX_MASK ;
+ audio->in_call = 1;
+ spin_unlock_irqrestore(&audio->dev_lock, flags);
+ }
+ break;
+ }
+ case AUDIO_GET_SESSION_ID: {
+ if (copy_to_user((void *) arg, &audio->enc_id,
+ sizeof(unsigned short))) {
+ rc = -EFAULT;
+ }
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static ssize_t audpcm_in_read(struct file *file,
+ char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio_in *audio = file->private_data;
+ unsigned long flags;
+ const char __user *start = buf;
+ void *data;
+ uint32_t index;
+ uint32_t size;
+ int rc = 0;
+
+ mutex_lock(&audio->read_lock);
+ while (count > 0) {
+ rc = wait_event_interruptible(
+ audio->wait, (audio->in_count > 0) || audio->stopped ||
+ audio->abort || (audio->in_call && audio->running &&
+ (audio->voice_state == VOICE_STATE_OFFCALL)));
+ if (rc < 0)
+ break;
+
+ if (!audio->in_count) {
+ if (audio->stopped) {
+ MM_DBG("Driver in stop state, No more \
+ buffer to read");
+ rc = 0;/* End of File */
+ break;
+ } else if (audio->in_call && audio->running &&
+ (audio->voice_state == VOICE_STATE_OFFCALL)) {
+ MM_DBG("Not Permitted Voice Terminated\n");
+ rc = -EPERM; /* Voice Call stopped */
+ break;
+ }
+ }
+
+ if (audio->abort) {
+ rc = -EPERM; /* Not permitted due to abort */
+ break;
+ }
+
+ index = audio->in_tail;
+ data = (uint8_t *) audio->in[index].data;
+ size = audio->in[index].size;
+ if (count >= size) {
+ if (copy_to_user(buf, data, size)) {
+ rc = -EFAULT;
+ break;
+ }
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (index != audio->in_tail) {
+ /* overrun -- data is
+ * invalid and we need to retry */
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ continue;
+ }
+ audio->in[index].size = 0;
+ audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1);
+ audio->in_count--;
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ count -= size;
+ buf += size;
+ } else {
+ MM_ERR("short read count %d\n", count);
+ break;
+ }
+ }
+ mutex_unlock(&audio->read_lock);
+
+ if (buf > start)
+ return buf - start;
+
+ return rc;
+}
+
+static ssize_t audpcm_in_write(struct file *file,
+ const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ return -EINVAL;
+}
+
+static int audpcm_in_release(struct inode *inode, struct file *file)
+{
+ struct audio_in *audio = file->private_data;
+
+ mutex_lock(&audio->lock);
+ audio->in_call = 0;
+ /* with draw frequency for session
+ incase not stopped the driver */
+ msm_snddev_withdraw_freq(audio->enc_id, SNDDEV_CAP_TX,
+ AUDDEV_CLNT_ENC);
+ auddev_unregister_evt_listner(AUDDEV_CLNT_ENC, audio->enc_id);
+ /*reset the sampling frequency information at audpreproc layer*/
+ audio->session_info.sampling_freq = 0;
+ audpreproc_update_audrec_info(&audio->session_info);
+ audpcm_in_disable(audio);
+ audpcm_in_flush(audio);
+ msm_adsp_put(audio->audrec);
+ audpreproc_aenc_free(audio->enc_id);
+ audio->audrec = NULL;
+ audio->opened = 0;
+ if (audio->data) {
+ iounmap(audio->data);
+ pmem_kfree(audio->phys);
+ audio->data = NULL;
+ }
+ mutex_unlock(&audio->lock);
+ return 0;
+}
+
+static int audpcm_in_open(struct inode *inode, struct file *file)
+{
+ struct audio_in *audio = &the_audio_in;
+ int rc;
+ int encid;
+
+ mutex_lock(&audio->lock);
+ if (audio->opened) {
+ rc = -EBUSY;
+ goto done;
+ }
+ audio->phys = pmem_kalloc(DMASZ, PMEM_MEMTYPE_EBI1|
+ PMEM_ALIGNMENT_4K);
+ if (!IS_ERR((void *)audio->phys)) {
+ audio->data = ioremap(audio->phys, DMASZ);
+ if (!audio->data) {
+ MM_ERR("could not allocate read buffers\n");
+ rc = -ENOMEM;
+ pmem_kfree(audio->phys);
+ goto done;
+ }
+ } else {
+ MM_ERR("could not allocate read buffers\n");
+ rc = -ENOMEM;
+ goto done;
+ }
+ MM_DBG("Memory addr = 0x%8x phy addr = 0x%8x\n",\
+ (int) audio->data, (int) audio->phys);
+ if ((file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ rc = -EACCES;
+ MM_ERR("Non tunnel encoding is not supported\n");
+ goto done;
+ } else if (!(file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ audio->mode = MSM_AUD_ENC_MODE_TUNNEL;
+ MM_DBG("Opened for tunnel mode encoding\n");
+ } else {
+ rc = -EACCES;
+ goto done;
+ }
+ /* Settings will be re-config at AUDIO_SET_CONFIG,
+ * but at least we need to have initial config
+ */
+ audio->channel_mode = AUDREC_CMD_MODE_MONO;
+ audio->buffer_size = MONO_DATA_SIZE_1024;
+ audio->samp_rate = 8000;
+ audio->enc_type = ENC_TYPE_EXT_WAV | audio->mode;
+
+ encid = audpreproc_aenc_alloc(audio->enc_type, &audio->module_name,
+ &audio->queue_ids);
+ if (encid < 0) {
+ MM_ERR("No free encoder available\n");
+ rc = -ENODEV;
+ goto done;
+ }
+ audio->enc_id = encid;
+
+ rc = msm_adsp_get(audio->module_name, &audio->audrec,
+ &audrec_adsp_ops, audio);
+
+ if (rc) {
+ audpreproc_aenc_free(audio->enc_id);
+ goto done;
+ }
+
+ audio->stopped = 0;
+ audio->source = 0;
+ audio->abort = 0;
+ audpcm_in_flush(audio);
+ audio->device_events = AUDDEV_EVT_DEV_RDY | AUDDEV_EVT_DEV_RLS |
+ AUDDEV_EVT_FREQ_CHG |
+ AUDDEV_EVT_VOICE_STATE_CHG;
+
+ audio->voice_state = msm_get_voice_state();
+ rc = auddev_register_evt_listner(audio->device_events,
+ AUDDEV_CLNT_ENC, audio->enc_id,
+ pcm_in_listener, (void *) audio);
+ if (rc) {
+ MM_ERR("failed to register device event listener\n");
+ goto evt_error;
+ }
+ file->private_data = audio;
+ audio->opened = 1;
+ rc = 0;
+done:
+ mutex_unlock(&audio->lock);
+ return rc;
+evt_error:
+ msm_adsp_put(audio->audrec);
+ audpreproc_aenc_free(audio->enc_id);
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static const struct file_operations audio_in_fops = {
+ .owner = THIS_MODULE,
+ .open = audpcm_in_open,
+ .release = audpcm_in_release,
+ .read = audpcm_in_read,
+ .write = audpcm_in_write,
+ .unlocked_ioctl = audpcm_in_ioctl,
+};
+
+struct miscdevice audio_in_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_pcm_in",
+ .fops = &audio_in_fops,
+};
+
+static int __init audpcm_in_init(void)
+{
+ mutex_init(&the_audio_in.lock);
+ mutex_init(&the_audio_in.read_lock);
+ spin_lock_init(&the_audio_in.dsp_lock);
+ spin_lock_init(&the_audio_in.dev_lock);
+ init_waitqueue_head(&the_audio_in.wait);
+ init_waitqueue_head(&the_audio_in.wait_enable);
+ return misc_register(&audio_in_misc);
+}
+
+device_initcall(audpcm_in_init);
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_qcelp.c b/arch/arm/mach-msm/qdsp5v2/audio_qcelp.c
new file mode 100644
index 0000000..b57d72f
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_qcelp.c
@@ -0,0 +1,1625 @@
+/*
+ * qcelp 13k audio decoder device
+ *
+ * Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
+ *
+ * This code is based in part on audio_mp3.c, which is
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ *
+ * 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/debugfs.h>
+#include <linux/earlysuspend.h>
+#include <linux/list.h>
+#include <linux/android_pmem.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+#include <linux/slab.h>
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/qdsp5audplaycmdi.h>
+#include <mach/qdsp5v2/qdsp5audplaymsg.h>
+#include <mach/qdsp5v2/audpp.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <linux/msm_audio.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/debug_mm.h>
+
+#define BUFSZ 1094 /* QCELP 13K Hold 600ms packet data = 36 * 30 and
+ 14 bytes of meta in */
+#define BUF_COUNT 2
+#define DMASZ (BUFSZ * BUF_COUNT)
+
+#define PCM_BUFSZ_MIN 1624 /* 100ms worth of data and
+ 24 bytes of meta out */
+#define PCM_BUF_MAX_COUNT 5
+
+#define AUDDEC_DEC_QCELP 9
+
+#define ROUTING_MODE_FTRT 1
+#define ROUTING_MODE_RT 2
+/* Decoder status received from AUDPPTASK */
+#define AUDPP_DEC_STATUS_SLEEP 0
+#define AUDPP_DEC_STATUS_INIT 1
+#define AUDPP_DEC_STATUS_CFG 2
+#define AUDPP_DEC_STATUS_PLAY 3
+
+#define AUDQCELP_METAFIELD_MASK 0xFFFF0000
+#define AUDQCELP_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */
+#define AUDQCELP_EOS_FLG_MASK 0x01
+#define AUDQCELP_EOS_NONE 0x0 /* No EOS detected */
+#define AUDQCELP_EOS_SET 0x1 /* EOS set in meta field */
+
+#define AUDQCELP_EVENT_NUM 10 /* Default number of pre-allocated event pkts */
+
+struct buffer {
+ void *data;
+ unsigned size;
+ unsigned used; /* Input usage actual DSP produced PCM size */
+ unsigned addr;
+ unsigned short mfield_sz; /*only useful for data has meta field */
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+struct audqcelp_suspend_ctl {
+ struct early_suspend node;
+ struct audio *audio;
+};
+#endif
+
+struct audqcelp_event{
+ struct list_head list;
+ int event_type;
+ union msm_audio_event_payload payload;
+};
+
+struct audio {
+ struct buffer out[BUF_COUNT];
+
+ spinlock_t dsp_lock;
+
+ uint8_t out_head;
+ uint8_t out_tail;
+ uint8_t out_needed; /* number of buffers the dsp is waiting for */
+
+ struct mutex lock;
+ struct mutex write_lock;
+ wait_queue_head_t write_wait;
+
+ /* Host PCM section - START */
+ struct buffer in[PCM_BUF_MAX_COUNT];
+ struct mutex read_lock;
+ wait_queue_head_t read_wait; /* Wait queue for read */
+ char *read_data; /* pointer to reader buffer */
+ int32_t read_phys; /* physical address of reader buffer */
+ uint8_t read_next; /* index to input buffers to be read next */
+ uint8_t fill_next; /* index to buffer that DSP should be filling */
+ uint8_t pcm_buf_count; /* number of pcm buffer allocated */
+ /* Host PCM section - END */
+
+ struct msm_adsp_module *audplay;
+
+ /* data allocated for various buffers */
+ char *data;
+ int32_t phys; /* physical address of write buffer */
+
+ int mfield; /* meta field embedded in data */
+ int rflush; /* Read flush */
+ int wflush; /* Write flush */
+ uint8_t opened:1;
+ uint8_t enabled:1;
+ uint8_t running:1;
+ uint8_t stopped:1; /* set when stopped, cleared on flush */
+ uint8_t pcm_feedback:1; /* set when non-tunnel mode */
+ uint8_t buf_refresh:1;
+ int teos; /* valid only if tunnel mode & no data left for decoder */
+ enum msm_aud_decoder_state dec_state; /* Represents decoder state */
+
+ const char *module_name;
+ unsigned queue_id;
+ uint16_t dec_id;
+ int16_t source;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct audqcelp_suspend_ctl suspend_ctl;
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dentry;
+#endif
+
+ wait_queue_head_t wait;
+ struct list_head free_event_queue;
+ struct list_head event_queue;
+ wait_queue_head_t event_wait;
+ spinlock_t event_queue_lock;
+ struct mutex get_event_lock;
+ int event_abort;
+ /* AV sync Info */
+ int avsync_flag; /* Flag to indicate feedback from DSP */
+ wait_queue_head_t avsync_wait;/* Wait queue for AV Sync Message */
+ /* flags, 48 bits sample/bytes counter per channel */
+ uint16_t avsync[AUDPP_AVSYNC_CH_COUNT * AUDPP_AVSYNC_NUM_WORDS + 1];
+
+ uint32_t device_events;
+
+ int eq_enable;
+ int eq_needs_commit;
+ struct audpp_cmd_cfg_object_params_eqalizer eq;
+ struct audpp_cmd_cfg_object_params_volume vol_pan;
+};
+
+static int auddec_dsp_config(struct audio *audio, int enable);
+static void audpp_cmd_cfg_adec_params(struct audio *audio);
+static void audpp_cmd_cfg_routing_mode(struct audio *audio);
+static void audqcelp_send_data(struct audio *audio, unsigned needed);
+static void audqcelp_config_hostpcm(struct audio *audio);
+static void audqcelp_buffer_refresh(struct audio *audio);
+static void audqcelp_dsp_event(void *private, unsigned id, uint16_t *msg);
+static void audqcelp_post_event(struct audio *audio, int type,
+ union msm_audio_event_payload payload);
+
+/* must be called with audio->lock held */
+static int audqcelp_enable(struct audio *audio)
+{
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ if (audio->enabled)
+ return 0;
+
+ audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+ audio->out_tail = 0;
+ audio->out_needed = 0;
+
+ if (msm_adsp_enable(audio->audplay)) {
+ MM_ERR("msm_adsp_enable(audplay) failed\n");
+ return -ENODEV;
+ }
+
+ if (audpp_enable(audio->dec_id, audqcelp_dsp_event, audio)) {
+ MM_ERR("audpp_enable() failed\n");
+ msm_adsp_disable(audio->audplay);
+ return -ENODEV;
+ }
+ audio->enabled = 1;
+ return 0;
+}
+
+static void qcelp_listner(u32 evt_id, union auddev_evt_data *evt_payload,
+ void *private_data)
+{
+ struct audio *audio = (struct audio *) private_data;
+ switch (evt_id) {
+ case AUDDEV_EVT_DEV_RDY:
+ MM_DBG(":AUDDEV_EVT_DEV_RDY\n");
+ audio->source |= (0x1 << evt_payload->routing_id);
+ if (audio->running == 1 && audio->enabled == 1)
+ audpp_route_stream(audio->dec_id, audio->source);
+ break;
+ case AUDDEV_EVT_DEV_RLS:
+ MM_DBG(":AUDDEV_EVT_DEV_RLS\n");
+ audio->source &= ~(0x1 << evt_payload->routing_id);
+ if (audio->running == 1 && audio->enabled == 1)
+ audpp_route_stream(audio->dec_id, audio->source);
+ break;
+ case AUDDEV_EVT_STREAM_VOL_CHG:
+ audio->vol_pan.volume = evt_payload->session_vol;
+ MM_DBG(":AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n",
+ audio->vol_pan.volume);
+ if (audio->running)
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ break;
+ default:
+ MM_ERR(":ERROR:wrong event\n");
+ break;
+ }
+}
+/* must be called with audio->lock held */
+static int audqcelp_disable(struct audio *audio)
+{
+ int rc = 0;
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ if (audio->enabled) {
+ audio->enabled = 0;
+ audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+ auddec_dsp_config(audio, 0);
+ rc = wait_event_interruptible_timeout(audio->wait,
+ audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+ msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE)
+ rc = -EFAULT;
+ else
+ rc = 0;
+ wake_up(&audio->write_wait);
+ wake_up(&audio->read_wait);
+ msm_adsp_disable(audio->audplay);
+ audpp_disable(audio->dec_id, audio);
+ audio->out_needed = 0;
+ }
+ return rc;
+}
+
+/* ------------------- dsp --------------------- */
+static void audqcelp_update_pcm_buf_entry(struct audio *audio,
+ uint32_t *payload)
+{
+ uint8_t index;
+ unsigned long flags;
+
+ if (audio->rflush)
+ return;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ for (index = 0; index < payload[1]; index++) {
+ if (audio->in[audio->fill_next].addr ==
+ payload[2 + index * 2]) {
+ MM_DBG("in[%d] ready\n", audio->fill_next);
+ audio->in[audio->fill_next].used =
+ payload[3 + index * 2];
+ if ((++audio->fill_next) == audio->pcm_buf_count)
+ audio->fill_next = 0;
+ } else {
+ MM_ERR("expected=%x ret=%x\n",
+ audio->in[audio->fill_next].addr,
+ payload[1 + index * 2]);
+ break;
+ }
+ }
+ if (audio->in[audio->fill_next].used == 0) {
+ audqcelp_buffer_refresh(audio);
+ } else {
+ MM_DBG("read cannot keep up\n");
+ audio->buf_refresh = 1;
+ }
+ wake_up(&audio->read_wait);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+static void audplay_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent) (void *ptr, size_t len))
+{
+ struct audio *audio = data;
+ uint32_t msg[28];
+ getevent(msg, sizeof(msg));
+
+ MM_DBG("msg_id=%x\n", id);
+
+ switch (id) {
+ case AUDPLAY_MSG_DEC_NEEDS_DATA:
+ audqcelp_send_data(audio, 1);
+ break;
+
+ case AUDPLAY_MSG_BUFFER_UPDATE:
+ audqcelp_update_pcm_buf_entry(audio, msg);
+ break;
+
+ case ADSP_MESSAGE_ID:
+ MM_DBG("Received ADSP event: module enable(audplaytask)\n");
+ break;
+
+ default:
+ MM_ERR("unexpected message from decoder \n");
+ }
+}
+
+static void audqcelp_dsp_event(void *private, unsigned id, uint16_t *msg)
+{
+ struct audio *audio = private;
+
+ switch (id) {
+ case AUDPP_MSG_STATUS_MSG:{
+ unsigned status = msg[1];
+
+ switch (status) {
+ case AUDPP_DEC_STATUS_SLEEP: {
+ uint16_t reason = msg[2];
+ MM_DBG("decoder status:sleep reason = \
+ 0x%04x\n", reason);
+ if ((reason == AUDPP_MSG_REASON_MEM)
+ || (reason ==
+ AUDPP_MSG_REASON_NODECODER)) {
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_FAILURE;
+ wake_up(&audio->wait);
+ } else if (reason == AUDPP_MSG_REASON_NONE) {
+ /* decoder is in disable state */
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_CLOSE;
+ wake_up(&audio->wait);
+ }
+ break;
+ }
+ case AUDPP_DEC_STATUS_INIT:
+ MM_DBG("decoder status: init \n");
+ if (audio->pcm_feedback)
+ audpp_cmd_cfg_routing_mode(audio);
+ else
+ audpp_cmd_cfg_adec_params(audio);
+ break;
+
+ case AUDPP_DEC_STATUS_CFG:
+ MM_DBG("decoder status: cfg \n");
+ break;
+ case AUDPP_DEC_STATUS_PLAY:
+ MM_DBG("decoder status: play \n");
+ /* send mixer command */
+ audpp_route_stream(audio->dec_id,
+ audio->source);
+ if (audio->pcm_feedback) {
+ audqcelp_config_hostpcm(audio);
+ audqcelp_buffer_refresh(audio);
+ }
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_SUCCESS;
+ wake_up(&audio->wait);
+ break;
+ default:
+ MM_ERR("unknown decoder status\n");
+ }
+ break;
+ }
+ case AUDPP_MSG_CFG_MSG:
+ if (msg[0] == AUDPP_MSG_ENA_ENA) {
+ MM_DBG("CFG_MSG ENABLE\n");
+ auddec_dsp_config(audio, 1);
+ audio->out_needed = 0;
+ audio->running = 1;
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ audpp_dsp_set_eq(audio->dec_id, audio->eq_enable,
+ &audio->eq, POPP);
+ } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+ MM_DBG("CFG_MSG DISABLE\n");
+ audio->running = 0;
+ } else {
+ MM_DBG("CFG_MSG %d?\n", msg[0]);
+ }
+ break;
+ case AUDPP_MSG_ROUTING_ACK:
+ MM_DBG("ROUTING_ACK mode=%d\n", msg[1]);
+ audpp_cmd_cfg_adec_params(audio);
+ break;
+ case AUDPP_MSG_FLUSH_ACK:
+ MM_DBG("FLUSH_ACK\n");
+ audio->wflush = 0;
+ audio->rflush = 0;
+ wake_up(&audio->write_wait);
+ if (audio->pcm_feedback)
+ audqcelp_buffer_refresh(audio);
+ break;
+ case AUDPP_MSG_PCMDMAMISSED:
+ MM_DBG("PCMDMAMISSED\n");
+ audio->teos = 1;
+ wake_up(&audio->write_wait);
+ break;
+
+ case AUDPP_MSG_AVSYNC_MSG:
+ MM_DBG("AUDPP_MSG_AVSYNC_MSG\n");
+ memcpy(&audio->avsync[0], msg, sizeof(audio->avsync));
+ audio->avsync_flag = 1;
+ wake_up(&audio->avsync_wait);
+ break;
+
+ default:
+ MM_ERR("UNKNOWN (%d)\n", id);
+ }
+
+}
+
+struct msm_adsp_ops audplay_adsp_ops_qcelp = {
+ .event = audplay_dsp_event,
+};
+
+#define audplay_send_queue0(audio, cmd, len) \
+ msm_adsp_write(audio->audplay, audio->queue_id, \
+ cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+ struct audpp_cmd_cfg_dec_type cfg_dec_cmd;
+
+ memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd));
+
+ cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+ if (enable)
+ cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_QCELP;
+ else
+ cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_DIS_DEC_V;
+ cfg_dec_cmd.dm_mode = 0x0;
+ cfg_dec_cmd.stream_id = audio->dec_id;
+
+ return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd));
+}
+
+static void audpp_cmd_cfg_adec_params(struct audio *audio)
+{
+ struct audpp_cmd_cfg_adec_params_v13k cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+ cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_V13K_LEN;
+ cmd.common.dec_id = audio->dec_id;
+ cmd.common.input_sampling_frequency = 8000;
+ cmd.stereo_cfg = AUDPP_CMD_PCM_INTF_MONO_V;
+
+ audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_routing_mode(struct audio *audio)
+{
+ struct audpp_cmd_routing_mode cmd;
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_ROUTING_MODE;
+ cmd.object_number = audio->dec_id;
+ if (audio->pcm_feedback)
+ cmd.routing_mode = ROUTING_MODE_FTRT;
+ else
+ cmd.routing_mode = ROUTING_MODE_RT;
+ audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static int audplay_dsp_send_data_avail(struct audio *audio,
+ unsigned idx, unsigned len)
+{
+ struct audplay_cmd_bitstream_data_avail_nt2 cmd;
+
+ cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2;
+ if (audio->mfield)
+ cmd.decoder_id = AUDQCELP_METAFIELD_MASK |
+ (audio->out[idx].mfield_sz >> 1);
+ else
+ cmd.decoder_id = audio->dec_id;
+ cmd.buf_ptr = audio->out[idx].addr;
+ cmd.buf_size = len / 2;
+ cmd.partition_number = 0;
+ return audplay_send_queue0(audio, &cmd, sizeof(cmd));
+}
+
+static void audqcelp_buffer_refresh(struct audio *audio)
+{
+ struct audplay_cmd_buffer_refresh refresh_cmd;
+
+ refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH;
+ refresh_cmd.num_buffers = 1;
+ refresh_cmd.buf0_address = audio->in[audio->fill_next].addr;
+ refresh_cmd.buf0_length = audio->in[audio->fill_next].size;
+ refresh_cmd.buf_read_count = 0;
+ MM_DBG("buf0_addr=%x buf0_len=%d\n", refresh_cmd.buf0_address,
+ refresh_cmd.buf0_length);
+
+ (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd));
+}
+
+static void audqcelp_config_hostpcm(struct audio *audio)
+{
+ struct audplay_cmd_hpcm_buf_cfg cfg_cmd;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG;
+ cfg_cmd.max_buffers = 1;
+ cfg_cmd.byte_swap = 0;
+ cfg_cmd.hostpcm_config = (0x8000) | (0x4000);
+ cfg_cmd.feedback_frequency = 1;
+ cfg_cmd.partition_number = 0;
+
+ (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd));
+}
+
+static void audqcelp_send_data(struct audio *audio, unsigned needed)
+{
+ struct buffer *frame;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (!audio->running)
+ goto done;
+
+ if (needed && !audio->wflush) {
+ /* We were called from the callback because the DSP
+ * requested more data. Note that the DSP does want
+ * more data, and if a buffer was in-flight, mark it
+ * as available (since the DSP must now be done with
+ * it).
+ */
+ audio->out_needed = 1;
+ frame = audio->out + audio->out_tail;
+ if (frame->used == 0xffffffff) {
+ MM_DBG("frame %d free\n", audio->out_tail);
+ frame->used = 0;
+ audio->out_tail ^= 1;
+ wake_up(&audio->write_wait);
+ }
+ }
+
+ if (audio->out_needed) {
+ /* If the DSP currently wants data and we have a
+ * buffer available, we will send it and reset
+ * the needed flag. We'll mark the buffer as in-flight
+ * so that it won't be recycled until the next buffer
+ * is requested
+ */
+
+ frame = audio->out + audio->out_tail;
+ if (frame->used) {
+ BUG_ON(frame->used == 0xffffffff);
+ MM_DBG("frame %d busy\n", audio->out_tail);
+ audplay_dsp_send_data_avail(audio, audio->out_tail,
+ frame->used);
+ frame->used = 0xffffffff;
+ audio->out_needed = 0;
+ }
+ }
+ done:
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+/* ------------------- device --------------------- */
+
+static void audqcelp_flush(struct audio *audio)
+{
+ audio->out[0].used = 0;
+ audio->out[1].used = 0;
+ audio->out_head = 0;
+ audio->out_tail = 0;
+ audio->out_needed = 0;
+}
+
+static void audqcelp_flush_pcm_buf(struct audio *audio)
+{
+ uint8_t index;
+
+ for (index = 0; index < PCM_BUF_MAX_COUNT; index++)
+ audio->in[index].used = 0;
+
+ audio->buf_refresh = 0;
+ audio->read_next = 0;
+ audio->fill_next = 0;
+}
+
+static void audqcelp_ioport_reset(struct audio *audio)
+{
+ /* Make sure read/write thread are free from
+ * sleep and knowing that system is not able
+ * to process io request at the moment
+ */
+ wake_up(&audio->write_wait);
+ mutex_lock(&audio->write_lock);
+ audqcelp_flush(audio);
+ mutex_unlock(&audio->write_lock);
+ wake_up(&audio->read_wait);
+ mutex_lock(&audio->read_lock);
+ audqcelp_flush_pcm_buf(audio);
+ mutex_unlock(&audio->read_lock);
+ audio->avsync_flag = 1;
+ wake_up(&audio->avsync_wait);
+}
+
+static int audqcelp_events_pending(struct audio *audio)
+{
+ unsigned long flags;
+ int empty;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ empty = !list_empty(&audio->event_queue);
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+ return empty || audio->event_abort;
+}
+
+static void audqcelp_reset_event_queue(struct audio *audio)
+{
+ unsigned long flags;
+ struct audqcelp_event *drv_evt;
+ struct list_head *ptr, *next;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ list_for_each_safe(ptr, next, &audio->event_queue) {
+ drv_evt = list_first_entry(&audio->event_queue,
+ struct audqcelp_event, list);
+ list_del(&drv_evt->list);
+ kfree(drv_evt);
+ }
+ list_for_each_safe(ptr, next, &audio->free_event_queue) {
+ drv_evt = list_first_entry(&audio->free_event_queue,
+ struct audqcelp_event, list);
+ list_del(&drv_evt->list);
+ kfree(drv_evt);
+ }
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+ return;
+}
+
+
+static long audqcelp_process_event_req(struct audio *audio, void __user *arg)
+{
+ long rc;
+ struct msm_audio_event usr_evt;
+ struct audqcelp_event *drv_evt = NULL;
+ int timeout;
+ unsigned long flags;
+
+ if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event)))
+ return -EFAULT;
+
+ timeout = (int) usr_evt.timeout_ms;
+
+ if (timeout > 0) {
+ rc = wait_event_interruptible_timeout(
+ audio->event_wait, audqcelp_events_pending(audio),
+ msecs_to_jiffies(timeout));
+ if (rc == 0)
+ return -ETIMEDOUT;
+ } else {
+ rc = wait_event_interruptible(
+ audio->event_wait, audqcelp_events_pending(audio));
+ }
+
+ if (rc < 0)
+ return rc;
+
+ if (audio->event_abort) {
+ audio->event_abort = 0;
+ return -ENODEV;
+ }
+
+ rc = 0;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ if (!list_empty(&audio->event_queue)) {
+ drv_evt = list_first_entry(&audio->event_queue,
+ struct audqcelp_event, list);
+ list_del(&drv_evt->list);
+ }
+
+ if (drv_evt) {
+ usr_evt.event_type = drv_evt->event_type;
+ usr_evt.event_payload = drv_evt->payload;
+ list_add_tail(&drv_evt->list, &audio->free_event_queue);
+ } else
+ rc = -1;
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+ if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt)))
+ rc = -EFAULT;
+
+ return rc;
+}
+
+static int audio_enable_eq(struct audio *audio, int enable)
+{
+ if (audio->eq_enable == enable && !audio->eq_needs_commit)
+ return 0;
+
+ audio->eq_enable = enable;
+
+ if (audio->running) {
+ audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq, POPP);
+ audio->eq_needs_commit = 0;
+ }
+ return 0;
+}
+
+static int audio_get_avsync_data(struct audio *audio,
+ struct msm_audio_stats *stats)
+{
+ int rc = -EINVAL;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) {
+ /* av_sync sample count */
+ stats->sample_count = (audio->avsync[2] << 16) |
+ (audio->avsync[3]);
+
+ /* av_sync byte_count */
+ stats->byte_count = (audio->avsync[5] << 16) |
+ (audio->avsync[6]);
+
+ audio->avsync_flag = 0;
+ rc = 0;
+ }
+ local_irq_restore(flags);
+ return rc;
+
+}
+
+static long audqcelp_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct audio *audio = file->private_data;
+ int rc = -EINVAL;
+ unsigned long flags = 0;
+ uint16_t enable_mask;
+ int enable;
+ int prev_state;
+
+ MM_DBG("cmd = %d\n", cmd);
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+
+ audio->avsync_flag = 0;
+ memset(&stats, 0, sizeof(stats));
+ if (audpp_query_avsync(audio->dec_id) < 0)
+ return rc;
+
+ rc = wait_event_interruptible_timeout(audio->avsync_wait,
+ (audio->avsync_flag == 1),
+ msecs_to_jiffies(AUDPP_AVSYNC_EVENT_TIMEOUT));
+
+ if (rc < 0)
+ return rc;
+ else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) {
+ if (audio_get_avsync_data(audio, &stats) < 0)
+ return rc;
+
+ if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ } else
+ return -EAGAIN;
+ }
+
+ switch (cmd) {
+ case AUDIO_ENABLE_AUDPP:
+ if (copy_from_user(&enable_mask, (void *) arg,
+ sizeof(enable_mask))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ enable = (enable_mask & EQ_ENABLE) ? 1 : 0;
+ audio_enable_eq(audio, enable);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ rc = 0;
+ break;
+ case AUDIO_SET_VOLUME:
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->vol_pan.volume = arg;
+ if (audio->running)
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ rc = 0;
+ break;
+
+ case AUDIO_SET_PAN:
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->vol_pan.pan = arg;
+ if (audio->running)
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ rc = 0;
+ break;
+
+ case AUDIO_SET_EQ:
+ prev_state = audio->eq_enable;
+ audio->eq_enable = 0;
+ if (copy_from_user(&audio->eq.num_bands, (void *) arg,
+ sizeof(audio->eq) -
+ (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) {
+ rc = -EFAULT;
+ break;
+ }
+ audio->eq_enable = prev_state;
+ audio->eq_needs_commit = 1;
+ rc = 0;
+ break;
+ }
+
+ if (-EINVAL != rc)
+ return rc;
+
+ if (cmd == AUDIO_GET_EVENT) {
+ MM_DBG("AUDIO_GET_EVENT\n");
+ if (mutex_trylock(&audio->get_event_lock)) {
+ rc = audqcelp_process_event_req(audio,
+ (void __user *) arg);
+ mutex_unlock(&audio->get_event_lock);
+ } else
+ rc = -EBUSY;
+ return rc;
+ }
+
+ if (cmd == AUDIO_ABORT_GET_EVENT) {
+ audio->event_abort = 1;
+ wake_up(&audio->event_wait);
+ return 0;
+ }
+
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START:
+ MM_DBG("AUDIO_START\n");
+ rc = audqcelp_enable(audio);
+ if (!rc) {
+ rc = wait_event_interruptible_timeout(audio->wait,
+ audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+ msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+ MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc);
+
+ if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS)
+ rc = -ENODEV;
+ else
+ rc = 0;
+ }
+ break;
+ case AUDIO_STOP:
+ MM_DBG("AUDIO_STOP\n");
+ rc = audqcelp_disable(audio);
+ audio->stopped = 1;
+ audqcelp_ioport_reset(audio);
+ audio->stopped = 0;
+ break;
+ case AUDIO_FLUSH:
+ MM_DBG("AUDIO_FLUSH\n");
+ audio->rflush = 1;
+ audio->wflush = 1;
+ audqcelp_ioport_reset(audio);
+ if (audio->running) {
+ audpp_flush(audio->dec_id);
+ rc = wait_event_interruptible(audio->write_wait,
+ !audio->wflush);
+ if (rc < 0) {
+ MM_ERR("AUDIO_FLUSH interrupted\n");
+ rc = -EINTR;
+ }
+ } else {
+ audio->rflush = 0;
+ audio->wflush = 0;
+ }
+ break;
+ case AUDIO_SET_CONFIG:{
+ struct msm_audio_config config;
+ if (copy_from_user(&config, (void *)arg,
+ sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ audio->mfield = config.meta_field;
+ MM_DBG("AUDIO_SET_CONFIG applicable \
+ for metafield configuration\n");
+ rc = 0;
+ break;
+ }
+ case AUDIO_GET_CONFIG:{
+ struct msm_audio_config config;
+ config.buffer_size = BUFSZ;
+ config.buffer_count = BUF_COUNT;
+ config.sample_rate = 8000;
+ config.channel_count = 1;
+ config.meta_field = 0;
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+ config.unused[2] = 0;
+ if (copy_to_user((void *)arg, &config,
+ sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+
+ break;
+ }
+ case AUDIO_GET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+
+ config.pcm_feedback = audio->pcm_feedback;
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+ config.buffer_size = PCM_BUFSZ_MIN;
+ if (copy_to_user((void *)arg, &config,
+ sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ case AUDIO_SET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+
+ if (copy_from_user(&config, (void *)arg,
+ sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (config.pcm_feedback != audio->pcm_feedback) {
+ MM_ERR("Not sufficient permission to"
+ "change the playback mode\n");
+ rc = -EACCES;
+ break;
+ }
+ if ((config.buffer_count > PCM_BUF_MAX_COUNT) ||
+ (config.buffer_count == 1))
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+
+ if (config.buffer_size < PCM_BUFSZ_MIN)
+ config.buffer_size = PCM_BUFSZ_MIN;
+
+ /* Check if pcm feedback is required */
+ if ((config.pcm_feedback) && (!audio->read_data)) {
+ MM_DBG("allocate PCM buf %d\n",
+ config.buffer_count * config.buffer_size);
+ audio->read_phys = pmem_kalloc(
+ config.buffer_size *
+ config.buffer_count,
+ PMEM_MEMTYPE_EBI1|
+ PMEM_ALIGNMENT_4K);
+ if (IS_ERR((void *)audio->read_phys)) {
+ rc = -ENOMEM;
+ break;
+ }
+ audio->read_data = ioremap(audio->read_phys,
+ config.buffer_size *
+ config.buffer_count);
+ if (!audio->read_data) {
+ MM_ERR("no mem for read buf\n");
+ rc = -ENOMEM;
+ pmem_kfree(audio->read_phys);
+ } else {
+ uint8_t index;
+ uint32_t offset = 0;
+
+ audio->buf_refresh = 0;
+ audio->pcm_buf_count =
+ config.buffer_count;
+ audio->read_next = 0;
+ audio->fill_next = 0;
+
+ for (index = 0;
+ index < config.buffer_count; index++) {
+ audio->in[index].data =
+ audio->read_data + offset;
+ audio->in[index].addr =
+ audio->read_phys + offset;
+ audio->in[index].size =
+ config.buffer_size;
+ audio->in[index].used = 0;
+ offset += config.buffer_size;
+ }
+ MM_DBG("read buf: phy addr 0x%08x \
+ kernel addr 0x%08x\n",
+ audio->read_phys,
+ (int)audio->read_data);
+ rc = 0;
+ }
+ } else {
+ rc = 0;
+ }
+ break;
+ }
+ case AUDIO_PAUSE:
+ MM_DBG("AUDIO_PAUSE %ld\n", arg);
+ rc = audpp_pause(audio->dec_id, (int) arg);
+ break;
+ case AUDIO_GET_SESSION_ID:
+ if (copy_to_user((void *) arg, &audio->dec_id,
+ sizeof(unsigned short)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+/* Only useful in tunnel-mode */
+static int audqcelp_fsync(struct file *file, int datasync)
+{
+ struct audio *audio = file->private_data;
+ int rc = 0;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ if (!audio->running || audio->pcm_feedback) {
+ rc = -EINVAL;
+ goto done_nolock;
+ }
+
+ mutex_lock(&audio->write_lock);
+
+ rc = wait_event_interruptible(audio->write_wait,
+ (!audio->out[0].used &&
+ !audio->out[1].used &&
+ audio->out_needed) || audio->wflush);
+
+ if (rc < 0)
+ goto done;
+ else if (audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+
+ /* pcm dmamiss message is sent continously
+ * when decoder is starved so no race
+ * condition concern
+ */
+ audio->teos = 0;
+
+ rc = wait_event_interruptible(audio->write_wait,
+ audio->teos || audio->wflush);
+
+ if (audio->wflush)
+ rc = -EBUSY;
+
+done:
+ mutex_unlock(&audio->write_lock);
+done_nolock:
+ return rc;
+}
+
+static ssize_t audqcelp_read(struct file *file, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ int rc = 0;
+
+ if (!audio->pcm_feedback)
+ return 0; /* PCM feedback is not enabled. Nothing to read */
+
+ mutex_lock(&audio->read_lock);
+ MM_DBG("%d\n", count);
+ while (count > 0) {
+ rc = wait_event_interruptible(audio->read_wait,
+ (audio->in[audio->read_next].used > 0) ||
+ (audio->stopped) || (audio->rflush));
+ if (rc < 0)
+ break;
+
+ if (audio->stopped || audio->rflush) {
+ rc = -EBUSY;
+ break;
+ }
+
+ if (count < audio->in[audio->read_next].used) {
+ /* Read must happen in frame boundary. Since driver does
+ not know frame size, read count must be greater or equal
+ to size of PCM samples */
+ MM_DBG("read stop - partial frame\n");
+ break;
+ } else {
+ MM_DBG("read from in[%d]\n", audio->read_next);
+
+ if (copy_to_user(buf,
+ audio->in[audio->read_next].data,
+ audio->in[audio->read_next].used)) {
+ MM_ERR("invalid addr %x\n", (unsigned int)buf);
+ rc = -EFAULT;
+ break;
+ }
+ count -= audio->in[audio->read_next].used;
+ buf += audio->in[audio->read_next].used;
+ audio->in[audio->read_next].used = 0;
+ if ((++audio->read_next) == audio->pcm_buf_count)
+ audio->read_next = 0;
+ break;
+ /* Force to exit while loop
+ * to prevent output thread
+ * sleep too long if data is
+ * not ready at this moment.
+ */
+ }
+ }
+
+ /* don't feed output buffer to HW decoder during flushing
+ * buffer refresh command will be sent once flush completes
+ * send buf refresh command here can confuse HW decoder
+ */
+ if (audio->buf_refresh && !audio->rflush) {
+ audio->buf_refresh = 0;
+ MM_DBG("kick start pcm feedback again\n");
+ audqcelp_buffer_refresh(audio);
+ }
+
+ mutex_unlock(&audio->read_lock);
+
+ if (buf > start)
+ rc = buf - start;
+
+ MM_DBG("read %d bytes\n", rc);
+ return rc;
+}
+
+static int audqcelp_process_eos(struct audio *audio,
+ const char __user *buf_start, unsigned short mfield_size)
+{
+ struct buffer *frame;
+ int rc = 0;
+
+ frame = audio->out + audio->out_head;
+
+ rc = wait_event_interruptible(audio->write_wait,
+ (audio->out_needed &&
+ audio->out[0].used == 0 &&
+ audio->out[1].used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+
+ if (rc < 0)
+ goto done;
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+
+ if (copy_from_user(frame->data, buf_start, mfield_size)) {
+ rc = -EFAULT;
+ goto done;
+ }
+
+ frame->mfield_sz = mfield_size;
+ audio->out_head ^= 1;
+ frame->used = mfield_size;
+ audqcelp_send_data(audio, 0);
+
+done:
+ return rc;
+}
+
+static ssize_t audqcelp_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ struct buffer *frame;
+ size_t xfer;
+ char *cpy_ptr;
+ int rc = 0, eos_condition = AUDQCELP_EOS_NONE;
+ unsigned short mfield_size = 0;
+
+ MM_DBG("cnt=%d\n", count);
+
+ if (count & 1)
+ return -EINVAL;
+
+ mutex_lock(&audio->write_lock);
+ while (count > 0) {
+ frame = audio->out + audio->out_head;
+ cpy_ptr = frame->data;
+ rc = wait_event_interruptible(audio->write_wait,
+ (frame->used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+ MM_DBG("buffer available\n");
+ if (rc < 0)
+ break;
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ break;
+ }
+
+ if (audio->mfield) {
+ if (buf == start) {
+ /* Processing beginning of user buffer */
+ if (__get_user(mfield_size,
+ (unsigned short __user *) buf)) {
+ rc = -EFAULT;
+ break;
+ } else if (mfield_size > count) {
+ rc = -EINVAL;
+ break;
+ }
+ MM_DBG("mf offset_val %x\n", mfield_size);
+ if (copy_from_user(cpy_ptr, buf, mfield_size)) {
+ rc = -EFAULT;
+ break;
+ }
+ /* Check if EOS flag is set and buffer has
+ * contains just meta field
+ */
+ if (cpy_ptr[AUDQCELP_EOS_FLG_OFFSET] &
+ AUDQCELP_EOS_FLG_MASK) {
+ MM_DBG("EOS SET\n");
+ eos_condition = AUDQCELP_EOS_SET;
+ if (mfield_size == count) {
+ buf += mfield_size;
+ break;
+ } else
+ cpy_ptr[AUDQCELP_EOS_FLG_OFFSET] &=
+ ~AUDQCELP_EOS_FLG_MASK;
+ }
+ cpy_ptr += mfield_size;
+ count -= mfield_size;
+ buf += mfield_size;
+ } else {
+ mfield_size = 0;
+ MM_DBG("continuous buffer\n");
+ }
+ frame->mfield_sz = mfield_size;
+ }
+
+ xfer = (count > (frame->size - mfield_size)) ?
+ (frame->size - mfield_size) : count;
+ if (copy_from_user(cpy_ptr, buf, xfer)) {
+ rc = -EFAULT;
+ break;
+ }
+
+ frame->used = xfer + mfield_size;
+ audio->out_head ^= 1;
+ count -= xfer;
+ buf += xfer;
+ audqcelp_send_data(audio, 0);
+ }
+ if (eos_condition == AUDQCELP_EOS_SET)
+ rc = audqcelp_process_eos(audio, start, mfield_size);
+ mutex_unlock(&audio->write_lock);
+ if (!rc) {
+ if (buf > start)
+ return buf - start;
+ }
+ return rc;
+}
+
+static int audqcelp_release(struct inode *inode, struct file *file)
+{
+ struct audio *audio = file->private_data;
+
+ MM_INFO("audio instance 0x%08x freeing\n", (int) audio);
+ mutex_lock(&audio->lock);
+ auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id);
+ audqcelp_disable(audio);
+ audqcelp_flush(audio);
+ audqcelp_flush_pcm_buf(audio);
+ msm_adsp_put(audio->audplay);
+ audpp_adec_free(audio->dec_id);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&audio->suspend_ctl.node);
+#endif
+ audio->opened = 0;
+ audio->event_abort = 1;
+ wake_up(&audio->event_wait);
+ audqcelp_reset_event_queue(audio);
+ iounmap(audio->data);
+ pmem_kfree(audio->phys);
+ if (audio->read_data) {
+ iounmap(audio->read_data);
+ pmem_kfree(audio->read_phys);
+ }
+ mutex_unlock(&audio->lock);
+#ifdef CONFIG_DEBUG_FS
+ if (audio->dentry)
+ debugfs_remove(audio->dentry);
+#endif
+ kfree(audio);
+ return 0;
+}
+
+static void audqcelp_post_event(struct audio *audio, int type,
+ union msm_audio_event_payload payload)
+{
+ struct audqcelp_event *e_node = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+
+ if (!list_empty(&audio->free_event_queue)) {
+ e_node = list_first_entry(&audio->free_event_queue,
+ struct audqcelp_event, list);
+ list_del(&e_node->list);
+ } else {
+ e_node = kmalloc(sizeof(struct audqcelp_event), GFP_ATOMIC);
+ if (!e_node) {
+ MM_ERR("No mem to post event %d\n", type);
+ return;
+ }
+ }
+
+ e_node->event_type = type;
+ e_node->payload = payload;
+
+ list_add_tail(&e_node->list, &audio->event_queue);
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+ wake_up(&audio->event_wait);
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void audqcelp_suspend(struct early_suspend *h)
+{
+ struct audqcelp_suspend_ctl *ctl =
+ container_of(h, struct audqcelp_suspend_ctl, node);
+ union msm_audio_event_payload payload;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ audqcelp_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload);
+}
+
+static void audqcelp_resume(struct early_suspend *h)
+{
+ struct audqcelp_suspend_ctl *ctl =
+ container_of(h, struct audqcelp_suspend_ctl, node);
+ union msm_audio_event_payload payload;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ audqcelp_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload);
+}
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t audqcelp_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t audqcelp_debug_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ const int debug_bufmax = 1024;
+ static char buffer[1024];
+ int n = 0, i;
+ struct audio *audio = file->private_data;
+
+ mutex_lock(&audio->lock);
+ n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "enabled %d\n", audio->enabled);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "stopped %d\n", audio->stopped);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "pcm_feedback %d\n", audio->pcm_feedback);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_buf_sz %d\n", audio->out[0].size);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "pcm_buf_count %d \n", audio->pcm_buf_count);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "pcm_buf_sz %d \n", audio->in[0].size);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "volume %x \n", audio->vol_pan.volume);
+ mutex_unlock(&audio->lock);
+ /* Following variables are only useful for debugging when
+ * when playback halts unexpectedly. Thus, no mutual exclusion
+ * enforced
+ */
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "wflush %d\n", audio->wflush);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "rflush %d\n", audio->rflush);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "running %d \n", audio->running);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "dec state %d \n", audio->dec_state);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_needed %d \n", audio->out_needed);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_head %d \n", audio->out_head);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_tail %d \n", audio->out_tail);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out[0].used %d \n", audio->out[0].used);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out[1].used %d \n", audio->out[1].used);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "buffer_refresh %d \n", audio->buf_refresh);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "read_next %d \n", audio->read_next);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "fill_next %d \n", audio->fill_next);
+ for (i = 0; i < audio->pcm_buf_count; i++)
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "in[%d].size %d \n", i, audio->in[i].used);
+ buffer[n] = 0;
+ return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static const struct file_operations audqcelp_debug_fops = {
+ .read = audqcelp_debug_read,
+ .open = audqcelp_debug_open,
+};
+#endif
+
+static int audqcelp_open(struct inode *inode, struct file *file)
+{
+ struct audio *audio = NULL;
+ int rc, dec_attrb, decid, i;
+ struct audqcelp_event *e_node = NULL;
+#ifdef CONFIG_DEBUG_FS
+ /* 4 bytes represents decoder number, 1 byte for terminate string */
+ char name[sizeof "msm_qcelp_" + 5];
+#endif
+
+ /* Create audio instance, set to zero */
+ audio = kzalloc(sizeof(struct audio), GFP_KERNEL);
+ if (!audio) {
+ MM_ERR("no memory to allocate audio instance\n");
+ rc = -ENOMEM;
+ goto done;
+ }
+ MM_INFO("audio instance 0x%08x created\n", (int)audio);
+
+ /* Allocate the decoder */
+ dec_attrb = AUDDEC_DEC_QCELP;
+ if ((file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ dec_attrb |= MSM_AUD_MODE_NONTUNNEL;
+ audio->pcm_feedback = NON_TUNNEL_MODE_PLAYBACK;
+ } else if ((file->f_mode & FMODE_WRITE) &&
+ !(file->f_mode & FMODE_READ)) {
+ dec_attrb |= MSM_AUD_MODE_TUNNEL;
+ audio->pcm_feedback = TUNNEL_MODE_PLAYBACK;
+ } else {
+ kfree(audio);
+ rc = -EACCES;
+ goto done;
+ }
+ decid = audpp_adec_alloc(dec_attrb, &audio->module_name,
+ &audio->queue_id);
+ if (decid < 0) {
+ MM_ERR("No free decoder available, freeing instance 0x%08x\n",
+ (int)audio);
+ rc = -ENODEV;
+ kfree(audio);
+ goto done;
+ }
+ audio->dec_id = decid & MSM_AUD_DECODER_MASK;
+
+ audio->phys = pmem_kalloc(DMASZ, PMEM_MEMTYPE_EBI1|PMEM_ALIGNMENT_4K);
+ if (IS_ERR((void *)audio->phys)) {
+ MM_ERR("could not allocate write buffers, freeing instance \
+ 0x%08x\n", (int)audio);
+ rc = -ENOMEM;
+ audpp_adec_free(audio->dec_id);
+ kfree(audio);
+ goto done;
+ } else {
+ audio->data = ioremap(audio->phys, DMASZ);
+ if (!audio->data) {
+ MM_ERR("could not allocate write buffers, freeing \
+ instance 0x%08x\n", (int)audio);
+ rc = -ENOMEM;
+ pmem_kfree(audio->phys);
+ audpp_adec_free(audio->dec_id);
+ kfree(audio);
+ goto done;
+ }
+ MM_DBG("write buf: phy addr 0x%08x kernel addr 0x%08x\n",
+ audio->phys, (int)audio->data);
+ }
+
+ rc = msm_adsp_get(audio->module_name, &audio->audplay,
+ &audplay_adsp_ops_qcelp, audio);
+ if (rc) {
+ MM_ERR("failed to get %s module, freeing instance 0x%08x\n",
+ audio->module_name, (int)audio);
+ goto err;
+ }
+
+ /* Initialize all locks of audio instance */
+ mutex_init(&audio->lock);
+ mutex_init(&audio->write_lock);
+ mutex_init(&audio->read_lock);
+ mutex_init(&audio->get_event_lock);
+ spin_lock_init(&audio->dsp_lock);
+ init_waitqueue_head(&audio->write_wait);
+ init_waitqueue_head(&audio->read_wait);
+ INIT_LIST_HEAD(&audio->free_event_queue);
+ INIT_LIST_HEAD(&audio->event_queue);
+ init_waitqueue_head(&audio->wait);
+ init_waitqueue_head(&audio->event_wait);
+ spin_lock_init(&audio->event_queue_lock);
+ init_waitqueue_head(&audio->avsync_wait);
+
+ /* Initialize buffer */
+ audio->out[0].data = audio->data + 0;
+ audio->out[0].addr = audio->phys + 0;
+ audio->out[0].size = BUFSZ;
+
+ audio->out[1].data = audio->data + BUFSZ;
+ audio->out[1].addr = audio->phys + BUFSZ;
+ audio->out[1].size = BUFSZ;
+
+ audio->vol_pan.volume = 0x2000;
+
+ audqcelp_flush(audio);
+
+ file->private_data = audio;
+ audio->opened = 1;
+
+ audio->device_events = AUDDEV_EVT_DEV_RDY
+ |AUDDEV_EVT_DEV_RLS|
+ AUDDEV_EVT_STREAM_VOL_CHG;
+
+ rc = auddev_register_evt_listner(audio->device_events,
+ AUDDEV_CLNT_DEC,
+ audio->dec_id,
+ qcelp_listner,
+ (void *)audio);
+ if (rc) {
+ MM_ERR("%s: failed to register listnet\n", __func__);
+ goto event_err;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ snprintf(name, sizeof name, "msm_qcelp_%04x", audio->dec_id);
+ audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *) audio, &audqcelp_debug_fops);
+
+ if (IS_ERR(audio->dentry))
+ MM_DBG("debugfs_create_file failed\n");
+#endif
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+ audio->suspend_ctl.node.resume = audqcelp_resume;
+ audio->suspend_ctl.node.suspend = audqcelp_suspend;
+ audio->suspend_ctl.audio = audio;
+ register_early_suspend(&audio->suspend_ctl.node);
+#endif
+ for (i = 0; i < AUDQCELP_EVENT_NUM; i++) {
+ e_node = kmalloc(sizeof(struct audqcelp_event), GFP_KERNEL);
+ if (e_node)
+ list_add_tail(&e_node->list, &audio->free_event_queue);
+ else {
+ MM_ERR("event pkt alloc failed\n");
+ break;
+ }
+ }
+done:
+ return rc;
+event_err:
+ msm_adsp_put(audio->audplay);
+err:
+ iounmap(audio->data);
+ pmem_kfree(audio->phys);
+ audpp_adec_free(audio->dec_id);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_qcelp_fops = {
+ .owner = THIS_MODULE,
+ .open = audqcelp_open,
+ .release = audqcelp_release,
+ .read = audqcelp_read,
+ .write = audqcelp_write,
+ .unlocked_ioctl = audqcelp_ioctl,
+ .fsync = audqcelp_fsync,
+};
+
+struct miscdevice audio_qcelp_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_qcelp",
+ .fops = &audio_qcelp_fops,
+};
+
+static int __init audqcelp_init(void)
+{
+ return misc_register(&audio_qcelp_misc);
+}
+
+static void __exit audqcelp_exit(void)
+{
+ misc_deregister(&audio_qcelp_misc);
+}
+
+module_init(audqcelp_init);
+module_exit(audqcelp_exit);
+
+MODULE_DESCRIPTION("MSM QCELP 13K driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_qcelp_in.c b/arch/arm/mach-msm/qdsp5v2/audio_qcelp_in.c
new file mode 100644
index 0000000..47bd589
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_qcelp_in.c
@@ -0,0 +1,1497 @@
+/*
+ * qcelp audio input device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/msm_audio_qcp.h>
+#include <linux/android_pmem.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+
+#include <mach/msm_adsp.h>
+#include <mach/qdsp5v2/qdsp5audreccmdi.h>
+#include <mach/qdsp5v2/qdsp5audrecmsg.h>
+#include <mach/qdsp5v2/audpreproc.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/debug_mm.h>
+
+#define META_OUT_SIZE 24
+/* FRAME_NUM must be a power of two */
+#define FRAME_NUM 8
+#define QCELP_FRAME_SIZE 36 /* 36 bytes data */
+#define FRAME_SIZE (22 * 2) /* 36 bytes data */
+ /* 36 bytes data + 24 meta field*/
+#define NT_FRAME_SIZE (QCELP_FRAME_SIZE + META_OUT_SIZE)
+#define DMASZ (NT_FRAME_SIZE * FRAME_NUM)
+#define OUT_FRAME_NUM (2)
+#define OUT_BUFFER_SIZE (4 * 1024 + META_OUT_SIZE)
+#define BUFFER_SIZE (OUT_BUFFER_SIZE * OUT_FRAME_NUM)
+
+
+#define AUDPREPROC_QCELP_EOS_FLG_OFFSET 0x0A
+#define AUDPREPROC_QCELP_EOS_FLG_MASK 0x01
+#define AUDPREPROC_QCELP_EOS_NONE 0x0 /* No EOS detected */
+#define AUDPREPROC_QCELP_EOS_SET 0x1 /* EOS set in meta field */
+
+struct buffer {
+ void *data;
+ uint32_t size;
+ uint32_t read;
+ uint32_t addr;
+ uint32_t used;
+ uint32_t mfield_sz;
+};
+
+struct audio_in {
+ struct buffer in[FRAME_NUM];
+
+ spinlock_t dsp_lock;
+
+ atomic_t in_bytes;
+ atomic_t in_samples;
+
+ struct mutex lock;
+ struct mutex read_lock;
+ wait_queue_head_t wait;
+ wait_queue_head_t wait_enable;
+ /*write section*/
+ struct buffer out[OUT_FRAME_NUM];
+
+ uint8_t out_head;
+ uint8_t out_tail;
+ uint8_t out_needed; /* number of buffers the dsp is waiting for */
+ uint32_t out_count;
+
+ struct mutex write_lock;
+ wait_queue_head_t write_wait;
+ int32_t out_phys; /* physical address of write buffer */
+ char *out_data;
+ int mfield; /* meta field embedded in data */
+ int wflush; /*write flush */
+ int rflush; /*read flush*/
+ int out_frame_cnt;
+
+ struct msm_adsp_module *audrec;
+
+ struct audrec_session_info session_info; /*audrec session info*/
+
+ /* configuration to use on next enable */
+ uint32_t buffer_size; /* Frame size (36 bytes) */
+ uint32_t samp_rate;
+ uint32_t channel_mode;
+ uint32_t enc_type;
+
+ struct msm_audio_qcelp_enc_config cfg;
+ uint32_t rec_mode;
+
+ uint32_t dsp_cnt;
+ uint32_t in_head; /* next buffer dsp will write */
+ uint32_t in_tail; /* next buffer read() will read */
+ uint32_t in_count; /* number of buffers available to read() */
+ uint32_t mode;
+ uint32_t eos_ack;
+ uint32_t flush_ack;
+
+ const char *module_name;
+ unsigned queue_ids;
+ uint16_t enc_id;
+
+ uint16_t source; /* Encoding source bit mask */
+ uint32_t device_events;
+ uint32_t in_call;
+ uint32_t dev_cnt;
+ int voice_state;
+ spinlock_t dev_lock;
+
+ /* data allocated for various buffers */
+ char *data;
+ dma_addr_t phys;
+
+ int opened;
+ int enabled;
+ int running;
+ int stopped; /* set when stopped, cleared on flush */
+};
+
+struct audio_frame {
+ uint16_t frame_count_lsw;
+ uint16_t frame_count_msw;
+ uint16_t frame_length;
+ uint16_t erased_pcm;
+ unsigned char raw_bitstream[]; /* samples */
+} __attribute__((packed));
+
+struct audio_frame_nt {
+ uint16_t metadata_len;
+ uint16_t frame_count_lsw;
+ uint16_t frame_count_msw;
+ uint16_t frame_length;
+ uint16_t erased_pcm;
+ uint16_t reserved;
+ uint16_t time_stamp_dword_lsw;
+ uint16_t time_stamp_dword_msw;
+ uint16_t time_stamp_lsw;
+ uint16_t time_stamp_msw;
+ uint16_t nflag_lsw;
+ uint16_t nflag_msw;
+ unsigned char raw_bitstream[]; /* samples */
+} __attribute__((packed));
+
+struct qcelp_encoded_meta_out {
+ uint16_t metadata_len;
+ uint16_t time_stamp_dword_lsw;
+ uint16_t time_stamp_dword_msw;
+ uint16_t time_stamp_lsw;
+ uint16_t time_stamp_msw;
+ uint16_t nflag_lsw;
+ uint16_t nflag_msw;
+};
+
+/* Audrec Queue command sent macro's */
+#define audrec_send_bitstreamqueue(audio, cmd, len) \
+ msm_adsp_write(audio->audrec, ((audio->queue_ids & 0xFFFF0000) >> 16),\
+ cmd, len)
+
+#define audrec_send_audrecqueue(audio, cmd, len) \
+ msm_adsp_write(audio->audrec, (audio->queue_ids & 0x0000FFFF),\
+ cmd, len)
+
+/* DSP command send functions */
+static int audqcelp_in_enc_config(struct audio_in *audio, int enable);
+static int audqcelp_in_param_config(struct audio_in *audio);
+static int audqcelp_in_mem_config(struct audio_in *audio);
+static int audqcelp_in_record_config(struct audio_in *audio, int enable);
+static int audqcelp_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt);
+
+static void audqcelp_in_get_dsp_frames(struct audio_in *audio);
+static int audpcm_config(struct audio_in *audio);
+static void audqcelp_out_flush(struct audio_in *audio);
+static int audpreproc_cmd_cfg_routing_mode(struct audio_in *audio);
+static void audpreproc_pcm_send_data(struct audio_in *audio, unsigned needed);
+static void audqcelp_nt_in_get_dsp_frames(struct audio_in *audio);
+
+static void audqcelp_in_flush(struct audio_in *audio);
+
+static void qcelp_in_listener(u32 evt_id, union auddev_evt_data *evt_payload,
+ void *private_data)
+{
+ struct audio_in *audio = (struct audio_in *) private_data;
+ unsigned long flags;
+
+ MM_DBG("evt_id = 0x%8x\n", evt_id);
+ switch (evt_id) {
+ case AUDDEV_EVT_DEV_RDY: {
+ MM_DBG("AUDDEV_EVT_DEV_RDY\n");
+ spin_lock_irqsave(&audio->dev_lock, flags);
+ audio->dev_cnt++;
+ if (!audio->in_call)
+ audio->source |= (0x1 << evt_payload->routing_id);
+ spin_unlock_irqrestore(&audio->dev_lock, flags);
+
+ if ((audio->running == 1) && (audio->enabled == 1) &&
+ (audio->mode == MSM_AUD_ENC_MODE_TUNNEL))
+ audqcelp_in_record_config(audio, 1);
+ }
+ break;
+ case AUDDEV_EVT_DEV_RLS: {
+ MM_DBG("AUDDEV_EVT_DEV_RLS\n");
+ spin_lock_irqsave(&audio->dev_lock, flags);
+ audio->dev_cnt--;
+ if (!audio->in_call)
+ audio->source &= ~(0x1 << evt_payload->routing_id);
+ spin_unlock_irqrestore(&audio->dev_lock, flags);
+
+ if ((!audio->running) || (!audio->enabled))
+ break;
+
+ if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+ /* Turn of as per source */
+ if (audio->source)
+ audqcelp_in_record_config(audio, 1);
+ else
+ /* Turn off all */
+ audqcelp_in_record_config(audio, 0);
+ }
+ }
+ break;
+ case AUDDEV_EVT_VOICE_STATE_CHG: {
+ MM_DBG("AUDDEV_EVT_VOICE_STATE_CHG, state = %d\n",
+ evt_payload->voice_state);
+ audio->voice_state = evt_payload->voice_state;
+ if (audio->in_call && audio->running &&
+ (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)) {
+ if (audio->voice_state == VOICE_STATE_INCALL)
+ audqcelp_in_record_config(audio, 1);
+ else if (audio->voice_state == VOICE_STATE_OFFCALL) {
+ audqcelp_in_record_config(audio, 0);
+ wake_up(&audio->wait);
+ }
+ }
+
+ break;
+ }
+ default:
+ MM_ERR("wrong event %d\n", evt_id);
+ break;
+ }
+}
+
+/* ------------------- dsp preproc event handler--------------------- */
+static void audpreproc_dsp_event(void *data, unsigned id, void *msg)
+{
+ struct audio_in *audio = data;
+
+ switch (id) {
+ case AUDPREPROC_ERROR_MSG: {
+ struct audpreproc_err_msg *err_msg = msg;
+
+ MM_ERR("ERROR_MSG: stream id %d err idx %d\n",
+ err_msg->stream_id, err_msg->aud_preproc_err_idx);
+ /* Error case */
+ wake_up(&audio->wait_enable);
+ break;
+ }
+ case AUDPREPROC_CMD_CFG_DONE_MSG: {
+ MM_DBG("CMD_CFG_DONE_MSG \n");
+ break;
+ }
+ case AUDPREPROC_CMD_ENC_CFG_DONE_MSG: {
+ struct audpreproc_cmd_enc_cfg_done_msg *enc_cfg_msg = msg;
+
+ MM_DBG("CMD_ENC_CFG_DONE_MSG: stream id %d enc type \
+ 0x%8x\n", enc_cfg_msg->stream_id,
+ enc_cfg_msg->rec_enc_type);
+ /* Encoder enable success */
+ if (enc_cfg_msg->rec_enc_type & ENCODE_ENABLE) {
+ if(audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) {
+ MM_DBG("routing command\n");
+ audpreproc_cmd_cfg_routing_mode(audio);
+ } else {
+ audqcelp_in_param_config(audio);
+ }
+ } else { /* Encoder disable success */
+ audio->running = 0;
+ if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)
+ audqcelp_in_record_config(audio, 0);
+ else
+ wake_up(&audio->wait_enable);
+ }
+ break;
+ }
+ case AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG: {
+ MM_DBG("CMD_ENC_PARAM_CFG_DONE_MSG\n");
+ if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)
+ audqcelp_in_mem_config(audio);
+ else
+ audpcm_config(audio);
+ break;
+ }
+ case AUDPREPROC_CMD_ROUTING_MODE_DONE_MSG: {
+ struct audpreproc_cmd_routing_mode_done\
+ *routing_cfg_done_msg = msg;
+ if (routing_cfg_done_msg->configuration == 0) {
+ MM_INFO("routing configuration failed\n");
+ audio->running = 0;
+ } else
+ audqcelp_in_param_config(audio);
+ break;
+ }
+ case AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG: {
+ MM_DBG("AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG \n");
+ wake_up(&audio->wait_enable);
+ break;
+ }
+ default:
+ MM_ERR("Unknown Event id %d\n", id);
+ }
+}
+
+/* ------------------- dsp audrec event handler--------------------- */
+static void audrec_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent)(void *ptr, size_t len))
+{
+ struct audio_in *audio = data;
+
+ switch (id) {
+ case AUDREC_CMD_MEM_CFG_DONE_MSG: {
+ MM_DBG("CMD_MEM_CFG_DONE MSG DONE\n");
+ audio->running = 1;
+ if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+ if ((!audio->in_call && (audio->dev_cnt > 0)) ||
+ (audio->in_call &&
+ (audio->voice_state \
+ == VOICE_STATE_INCALL)))
+ audqcelp_in_record_config(audio, 1);
+ } else {
+ audpreproc_pcm_send_data(audio, 1);
+ wake_up(&audio->wait_enable);
+ }
+ break;
+ }
+ case AUDREC_FATAL_ERR_MSG: {
+ struct audrec_fatal_err_msg fatal_err_msg;
+
+ getevent(&fatal_err_msg, AUDREC_FATAL_ERR_MSG_LEN);
+ MM_ERR("FATAL_ERR_MSG: err id %d\n",
+ fatal_err_msg.audrec_err_id);
+ /* Error stop the encoder */
+ audio->stopped = 1;
+ wake_up(&audio->wait);
+ if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL)
+ wake_up(&audio->write_wait);
+ break;
+ }
+ case AUDREC_UP_PACKET_READY_MSG: {
+ struct audrec_up_pkt_ready_msg pkt_ready_msg;
+
+ getevent(&pkt_ready_msg, AUDREC_UP_PACKET_READY_MSG_LEN);
+ MM_DBG("UP_PACKET_READY_MSG: write cnt lsw %d \
+ write cnt msw %d read cnt lsw %d read cnt msw %d \n",\
+ pkt_ready_msg.audrec_packet_write_cnt_lsw, \
+ pkt_ready_msg.audrec_packet_write_cnt_msw, \
+ pkt_ready_msg.audrec_up_prev_read_cnt_lsw, \
+ pkt_ready_msg.audrec_up_prev_read_cnt_msw);
+
+ audqcelp_in_get_dsp_frames(audio);
+ break;
+ }
+ case AUDREC_CMD_PCM_BUFFER_PTR_UPDATE_ARM_TO_ENC_MSG: {
+ MM_DBG("ptr_update recieved from DSP\n");
+ audpreproc_pcm_send_data(audio, 1);
+ break;
+ }
+ case AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG: {
+ MM_ERR("AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG");
+ audqcelp_in_mem_config(audio);
+ break;
+ }
+ case AUDREC_UP_NT_PACKET_READY_MSG: {
+ struct audrec_up_nt_packet_ready_msg pkt_ready_msg;
+
+ getevent(&pkt_ready_msg, AUDREC_UP_NT_PACKET_READY_MSG_LEN);
+ MM_DBG("UP_NT_PACKET_READY_MSG: write cnt lsw %d \
+ write cnt msw %d read cnt lsw %d read cnt msw %d \n",\
+ pkt_ready_msg.audrec_packetwrite_cnt_lsw, \
+ pkt_ready_msg.audrec_packetwrite_cnt_msw, \
+ pkt_ready_msg.audrec_upprev_readcount_lsw, \
+ pkt_ready_msg.audrec_upprev_readcount_msw);
+
+ audqcelp_nt_in_get_dsp_frames(audio);
+ break;
+ }
+ case AUDREC_CMD_EOS_ACK_MSG: {
+ MM_DBG("eos ack recieved\n");
+ break;
+ }
+ case AUDREC_CMD_FLUSH_DONE_MSG: {
+ audio->wflush = 0;
+ audio->rflush = 0;
+ audio->flush_ack = 1;
+ wake_up(&audio->write_wait);
+ MM_DBG("flush ack recieved\n");
+ break;
+ }
+ case ADSP_MESSAGE_ID: {
+ MM_DBG("Received ADSP event:module audrectask\n");
+ break;
+ }
+ default:
+ MM_ERR("Unknown Event id %d\n", id);
+ }
+}
+
+static void audqcelp_in_get_dsp_frames(struct audio_in *audio)
+{
+ struct audio_frame *frame;
+ uint32_t index;
+ unsigned long flags;
+
+ MM_DBG("head = %d\n", audio->in_head);
+ index = audio->in_head;
+
+ frame = (void *) (((char *)audio->in[index].data) - \
+ sizeof(*frame));
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->in[index].size = frame->frame_length;
+
+ /* statistics of read */
+ atomic_add(audio->in[index].size, &audio->in_bytes);
+ atomic_add(1, &audio->in_samples);
+
+ audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1);
+
+ /* If overflow, move the tail index foward. */
+ if (audio->in_head == audio->in_tail) {
+ MM_ERR("Error! not able to keep up the read\n");
+ audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1);
+ MM_ERR("in_count = %d\n", audio->in_count);
+ } else
+ audio->in_count++;
+
+ audqcelp_dsp_read_buffer(audio, audio->dsp_cnt++);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+
+ wake_up(&audio->wait);
+}
+
+static void audqcelp_nt_in_get_dsp_frames(struct audio_in *audio)
+{
+ struct audio_frame_nt *nt_frame;
+ uint32_t index;
+ unsigned long flags;
+ MM_DBG("head = %d\n", audio->in_head);
+ index = audio->in_head;
+ nt_frame = (void *) (((char *)audio->in[index].data) - \
+ sizeof(struct audio_frame_nt));
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->in[index].size = nt_frame->frame_length;
+ /* statistics of read */
+ atomic_add(audio->in[index].size, &audio->in_bytes);
+ atomic_add(1, &audio->in_samples);
+
+ audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1);
+
+ /* If overflow, move the tail index foward. */
+ if (audio->in_head == audio->in_tail)
+ MM_DBG("Error! not able to keep up the read\n");
+ else
+ audio->in_count++;
+
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ wake_up(&audio->wait);
+}
+
+
+struct msm_adsp_ops audrec_qcelp_adsp_ops = {
+ .event = audrec_dsp_event,
+};
+
+static int audpreproc_pcm_buffer_ptr_refresh(struct audio_in *audio,
+ unsigned idx, unsigned len)
+{
+ struct audrec_cmd_pcm_buffer_ptr_refresh_arm_enc cmd;
+
+ if (len == META_OUT_SIZE)
+ len = len / 2;
+ else
+ len = (len + META_OUT_SIZE) / 2;
+ MM_DBG("len = %d\n", len);
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDREC_CMD_PCM_BUFFER_PTR_REFRESH_ARM_TO_ENC;
+ cmd.num_buffers = 1;
+ if (cmd.num_buffers == 1) {
+ cmd.buf_address_length[0] = (audio->out[idx].addr &
+ 0xffff0000) >> 16;
+ cmd.buf_address_length[1] = (audio->out[idx].addr &
+ 0x0000ffff);
+ cmd.buf_address_length[2] = (len & 0xffff0000) >> 16;
+ cmd.buf_address_length[3] = (len & 0x0000ffff);
+ }
+ audio->out_frame_cnt++;
+ return audrec_send_audrecqueue(audio, (void *)&cmd,
+ (unsigned int)sizeof(cmd));
+}
+
+
+static int audpcm_config(struct audio_in *audio)
+{
+ struct audrec_cmd_pcm_cfg_arm_to_enc cmd;
+ MM_DBG("\n");
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDREC_CMD_PCM_CFG_ARM_TO_ENC;
+ cmd.config_update_flag = AUDREC_PCM_CONFIG_UPDATE_FLAG_ENABLE;
+ cmd.enable_flag = AUDREC_ENABLE_FLAG_VALUE;
+ cmd.sampling_freq = audio->samp_rate;
+ if (!audio->channel_mode)
+ cmd.channels = 1;
+ else
+ cmd.channels = 2;
+ cmd.frequency_of_intimation = 1;
+ cmd.max_number_of_buffers = OUT_FRAME_NUM;
+ return audrec_send_audrecqueue(audio, (void *)&cmd,
+ (unsigned int)sizeof(cmd));
+}
+
+
+static int audpreproc_cmd_cfg_routing_mode(struct audio_in *audio)
+{
+ struct audpreproc_audrec_cmd_routing_mode cmd;
+
+ MM_DBG("\n");
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ROUTING_MODE;
+ cmd.stream_id = audio->enc_id;
+ if (audio->mode == MSM_ADSP_ENC_MODE_NON_TUNNEL)
+ cmd.routing_mode = 1;
+ return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+
+
+static int audqcelp_in_enc_config(struct audio_in *audio, int enable)
+{
+ struct audpreproc_audrec_cmd_enc_cfg cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ENC_CFG_2;
+ cmd.stream_id = audio->enc_id;
+
+ if (enable)
+ cmd.audrec_enc_type = audio->enc_type | ENCODE_ENABLE;
+ else
+ cmd.audrec_enc_type &= ~(ENCODE_ENABLE);
+
+ return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+static int audqcelp_in_param_config(struct audio_in *audio)
+{
+ struct audpreproc_audrec_cmd_parm_cfg_qcelp13k cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.cmd_id = AUDPREPROC_AUDREC_CMD_PARAM_CFG;
+ cmd.common.stream_id = audio->enc_id;
+
+ cmd.enc_min_rate = audio->cfg.min_bit_rate;
+ cmd.enc_max_rate = audio->cfg.max_bit_rate;
+ cmd.rate_modulation_cmd = 0; /* Default set to 0 */
+ cmd.reduced_rate_level = 0; /* Default set to 0 */
+
+ return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+/* To Do: msm_snddev_route_enc(audio->enc_id); */
+static int audqcelp_in_record_config(struct audio_in *audio, int enable)
+{
+ struct audpreproc_afe_cmd_audio_record_cfg cmd;
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG;
+ cmd.stream_id = audio->enc_id;
+ if (enable)
+ cmd.destination_activity = AUDIO_RECORDING_TURN_ON;
+ else
+ cmd.destination_activity = AUDIO_RECORDING_TURN_OFF;
+
+ cmd.source_mix_mask = audio->source;
+ if (audio->enc_id == 2) {
+ if ((cmd.source_mix_mask &
+ INTERNAL_CODEC_TX_SOURCE_MIX_MASK) ||
+ (cmd.source_mix_mask & AUX_CODEC_TX_SOURCE_MIX_MASK) ||
+ (cmd.source_mix_mask & VOICE_UL_SOURCE_MIX_MASK) ||
+ (cmd.source_mix_mask & VOICE_DL_SOURCE_MIX_MASK)) {
+ cmd.pipe_id = SOURCE_PIPE_1;
+ }
+ if (cmd.source_mix_mask &
+ AUDPP_A2DP_PIPE_SOURCE_MIX_MASK)
+ cmd.pipe_id |= SOURCE_PIPE_0;
+ }
+ MM_DBG("stream_id %x destination_activity %x \
+ source_mix_mask %x pipe_id %x",\
+ cmd.stream_id, cmd.destination_activity,
+ cmd.source_mix_mask, cmd.pipe_id);
+ return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+static int audqcelp_in_mem_config(struct audio_in *audio)
+{
+ struct audrec_cmd_arecmem_cfg cmd;
+ uint16_t *data = (void *) audio->data;
+ int n;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDREC_CMD_MEM_CFG_CMD;
+ cmd.audrec_up_pkt_intm_count = 1;
+ cmd.audrec_ext_pkt_start_addr_msw = audio->phys >> 16;
+ cmd.audrec_ext_pkt_start_addr_lsw = audio->phys;
+ cmd.audrec_ext_pkt_buf_number = FRAME_NUM;
+ MM_DBG("audio->phys = %x\n", audio->phys);
+ /* prepare buffer pointers:
+ * T:36 bytes qcelp ppacket + 4 halfword header
+ * NT:36 bytes qcelp packet + 12 halfword header
+ */
+ for (n = 0; n < FRAME_NUM; n++) {
+ if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+ audio->in[n].data = data + 4;
+ data += (FRAME_SIZE/2);
+ MM_DBG("0x%8x\n", (int)(audio->in[n].data - 8));
+ } else {
+ audio->in[n].data = data + 12;
+ data += ((QCELP_FRAME_SIZE) / 2) + 12;
+ MM_DBG("0x%8x\n", (int)(audio->in[n].data - 24));
+ }
+ }
+ return audrec_send_audrecqueue(audio, &cmd, sizeof(cmd));
+}
+
+static int audqcelp_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt)
+{
+ struct up_audrec_packet_ext_ptr cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = UP_AUDREC_PACKET_EXT_PTR;
+ cmd.audrec_up_curr_read_count_msw = read_cnt >> 16;
+ cmd.audrec_up_curr_read_count_lsw = read_cnt;
+
+ return audrec_send_bitstreamqueue(audio, &cmd, sizeof(cmd));
+}
+static int audqcelp_flush_command(struct audio_in *audio)
+{
+ struct audrec_cmd_flush cmd;
+ MM_DBG("\n");
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDREC_CMD_FLUSH;
+ return audrec_send_audrecqueue(audio, &cmd, sizeof(cmd));
+}
+
+/* must be called with audio->lock held */
+static int audqcelp_in_enable(struct audio_in *audio)
+{
+ if (audio->enabled)
+ return 0;
+
+ if (audpreproc_enable(audio->enc_id, &audpreproc_dsp_event, audio)) {
+ MM_ERR("msm_adsp_enable(audpreproc) failed\n");
+ return -ENODEV;
+ }
+
+ if (msm_adsp_enable(audio->audrec)) {
+ MM_ERR("msm_adsp_enable(audrec) failed\n");
+ audpreproc_disable(audio->enc_id, audio);
+ return -ENODEV;
+ }
+ audio->enabled = 1;
+ audqcelp_in_enc_config(audio, 1);
+
+ return 0;
+}
+
+/* must be called with audio->lock held */
+static int audqcelp_in_disable(struct audio_in *audio)
+{
+ if (audio->enabled) {
+ audio->enabled = 0;
+ audqcelp_in_enc_config(audio, 0);
+ wake_up(&audio->wait);
+ wait_event_interruptible_timeout(audio->wait_enable,
+ audio->running == 0, 1*HZ);
+ msm_adsp_disable(audio->audrec);
+ audpreproc_disable(audio->enc_id, audio);
+ }
+ return 0;
+}
+
+static void audqcelp_ioport_reset(struct audio_in *audio)
+{
+ /* Make sure read/write thread are free from
+ * sleep and knowing that system is not able
+ * to process io request at the moment
+ */
+ wake_up(&audio->write_wait);
+ mutex_lock(&audio->write_lock);
+ audqcelp_in_flush(audio);
+ mutex_unlock(&audio->write_lock);
+ wake_up(&audio->wait);
+ mutex_lock(&audio->read_lock);
+ audqcelp_out_flush(audio);
+ mutex_unlock(&audio->read_lock);
+}
+
+static void audqcelp_in_flush(struct audio_in *audio)
+{
+ int i;
+
+ audio->dsp_cnt = 0;
+ audio->in_head = 0;
+ audio->in_tail = 0;
+ audio->in_count = 0;
+ audio->eos_ack = 0;
+ for (i = 0; i < FRAME_NUM; i++) {
+ audio->in[i].size = 0;
+ audio->in[i].read = 0;
+ }
+ MM_DBG("in_bytes %d\n", atomic_read(&audio->in_bytes));
+ MM_DBG("in_samples %d\n", atomic_read(&audio->in_samples));
+ atomic_set(&audio->in_bytes, 0);
+ atomic_set(&audio->in_samples, 0);
+}
+
+static void audqcelp_out_flush(struct audio_in *audio)
+{
+ int i;
+
+ audio->out_head = 0;
+ audio->out_tail = 0;
+ audio->out_count = 0;
+ for (i = 0; i < OUT_FRAME_NUM; i++) {
+ audio->out[i].size = 0;
+ audio->out[i].read = 0;
+ audio->out[i].used = 0;
+ }
+}
+
+/* ------------------- device --------------------- */
+static long audqcelp_in_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct audio_in *audio = file->private_data;
+ int rc = 0;
+
+ MM_DBG("\n");
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ stats.byte_count = atomic_read(&audio->in_bytes);
+ stats.sample_count = atomic_read(&audio->in_samples);
+ if (copy_to_user((void *) arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return rc;
+ }
+
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START: {
+ uint32_t freq;
+ freq = 48000;
+ MM_DBG("AUDIO_START\n");
+ if (audio->in_call && (audio->voice_state !=
+ VOICE_STATE_INCALL)) {
+ rc = -EPERM;
+ break;
+ }
+ rc = msm_snddev_request_freq(&freq, audio->enc_id,
+ SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+ MM_DBG("sample rate configured %d\n", freq);
+ if (rc < 0) {
+ MM_DBG(" Sample rate can not be set, return code %d\n",
+ rc);
+ msm_snddev_withdraw_freq(audio->enc_id,
+ SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+ MM_DBG("msm_snddev_withdraw_freq\n");
+ break;
+ }
+ /*update aurec session info in audpreproc layer*/
+ audio->session_info.session_id = audio->enc_id;
+ audio->session_info.sampling_freq = audio->samp_rate;
+ audpreproc_update_audrec_info(&audio->session_info);
+ rc = audqcelp_in_enable(audio);
+ if (!rc) {
+ rc =
+ wait_event_interruptible_timeout(audio->wait_enable,
+ audio->running != 0, 1*HZ);
+ MM_DBG("state %d rc = %d\n", audio->running, rc);
+
+ if (audio->running == 0)
+ rc = -ENODEV;
+ else
+ rc = 0;
+ }
+ audio->stopped = 0;
+ break;
+ }
+ case AUDIO_STOP: {
+ /*reset the sampling frequency information at audpreproc layer*/
+ audio->session_info.sampling_freq = 0;
+ audpreproc_update_audrec_info(&audio->session_info);
+ rc = audqcelp_in_disable(audio);
+ rc = msm_snddev_withdraw_freq(audio->enc_id,
+ SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+ MM_DBG("msm_snddev_withdraw_freq\n");
+ audio->stopped = 1;
+ break;
+ }
+ case AUDIO_FLUSH: {
+ MM_DBG("AUDIO_FLUSH\n");
+ audio->rflush = 1;
+ audio->wflush = 1;
+ audqcelp_ioport_reset(audio);
+ if (audio->running) {
+ audqcelp_flush_command(audio);
+ rc = wait_event_interruptible(audio->write_wait,
+ !audio->wflush);
+ if (rc < 0) {
+ MM_ERR("AUDIO_FLUSH interrupted\n");
+ rc = -EINTR;
+ }
+ } else {
+ audio->rflush = 0;
+ audio->wflush = 0;
+ }
+ break;
+ }
+ case AUDIO_SET_STREAM_CONFIG: {
+ struct msm_audio_stream_config cfg;
+ if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) {
+ rc = -EFAULT;
+ break;
+ }
+ /* Allow only single frame */
+ if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+ if (cfg.buffer_size != (FRAME_SIZE - 8)) {
+ rc = -EINVAL;
+ break;
+ }
+ } else {
+ if (cfg.buffer_size != (QCELP_FRAME_SIZE + 14)) {
+ rc = -EINVAL;
+ break;
+ }
+ }
+ audio->buffer_size = cfg.buffer_size;
+ break;
+ }
+ case AUDIO_GET_STREAM_CONFIG: {
+ struct msm_audio_stream_config cfg;
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.buffer_size = audio->buffer_size;
+ cfg.buffer_count = FRAME_NUM;
+ if (copy_to_user((void *) arg, &cfg, sizeof(cfg)))
+ rc = -EFAULT;
+ break;
+ }
+ case AUDIO_GET_QCELP_ENC_CONFIG: {
+ if (copy_to_user((void *) arg, &audio->cfg, sizeof(audio->cfg)))
+ rc = -EFAULT;
+ break;
+ }
+ case AUDIO_SET_QCELP_ENC_CONFIG: {
+ struct msm_audio_qcelp_enc_config cfg;
+ if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) {
+ rc = -EFAULT;
+ break;
+ }
+ MM_DBG("0X%8x, 0x%8x, 0x%8x\n", cfg.min_bit_rate, \
+ cfg.max_bit_rate, cfg.cdma_rate);
+ if (cfg.min_bit_rate > CDMA_RATE_FULL || \
+ cfg.min_bit_rate < CDMA_RATE_EIGHTH) {
+ MM_ERR("invalid min bitrate\n");
+ rc = -EFAULT;
+ break;
+ }
+ if (cfg.max_bit_rate > CDMA_RATE_FULL || \
+ cfg.max_bit_rate < CDMA_RATE_EIGHTH) {
+ MM_ERR("invalid max bitrate\n");
+ rc = -EFAULT;
+ break;
+ }
+ /* Recording Does not support Erase and Blank */
+ if (cfg.cdma_rate > CDMA_RATE_FULL ||
+ cfg.cdma_rate < CDMA_RATE_EIGHTH) {
+ MM_ERR("invalid qcelp cdma rate\n");
+ rc = -EFAULT;
+ break;
+ }
+ memcpy(&audio->cfg, &cfg, sizeof(cfg));
+ break;
+ }
+ case AUDIO_GET_CONFIG: {
+ struct msm_audio_config cfg;
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.buffer_size = OUT_BUFFER_SIZE;
+ cfg.buffer_count = OUT_FRAME_NUM;
+ cfg.sample_rate = audio->samp_rate;
+ cfg.channel_count = audio->channel_mode;
+ if (copy_to_user((void *)arg, &cfg, sizeof(cfg)))
+ rc = -EFAULT;
+ break;
+ }
+ case AUDIO_SET_INCALL: {
+ struct msm_voicerec_mode cfg;
+ unsigned long flags;
+ if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+ if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (cfg.rec_mode != VOC_REC_BOTH &&
+ cfg.rec_mode != VOC_REC_UPLINK &&
+ cfg.rec_mode != VOC_REC_DOWNLINK) {
+ MM_ERR("invalid rec_mode\n");
+ rc = -EINVAL;
+ break;
+ } else {
+ spin_lock_irqsave(&audio->dev_lock, flags);
+ if (cfg.rec_mode == VOC_REC_UPLINK)
+ audio->source = \
+ VOICE_UL_SOURCE_MIX_MASK;
+ else if (cfg.rec_mode == VOC_REC_DOWNLINK)
+ audio->source = \
+ VOICE_DL_SOURCE_MIX_MASK;
+ else
+ audio->source = \
+ VOICE_DL_SOURCE_MIX_MASK |
+ VOICE_UL_SOURCE_MIX_MASK ;
+ audio->in_call = 1;
+ spin_unlock_irqrestore(&audio->dev_lock, flags);
+ }
+ }
+ break;
+ }
+ case AUDIO_GET_SESSION_ID: {
+ if (copy_to_user((void *) arg, &audio->enc_id,
+ sizeof(unsigned short))) {
+ rc = -EFAULT;
+ }
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static ssize_t audqcelp_in_read(struct file *file,
+ char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio_in *audio = file->private_data;
+ unsigned long flags;
+ const char __user *start = buf;
+ void *data;
+ uint32_t index;
+ uint32_t size;
+ int rc = 0;
+ struct qcelp_encoded_meta_out meta_field;
+ struct audio_frame_nt *nt_frame;
+ MM_DBG(" count = %d\n", count);
+ mutex_lock(&audio->read_lock);
+ while (count > 0) {
+ rc = wait_event_interruptible(
+ audio->wait, (audio->in_count > 0) || audio->stopped ||
+ audio->rflush ||
+ ((audio->mode == MSM_AUD_ENC_MODE_TUNNEL) &&
+ audio->in_call && audio->running &&
+ (audio->voice_state == VOICE_STATE_OFFCALL)));
+ if (rc < 0)
+ break;
+
+ if (audio->rflush) {
+ rc = -EBUSY;
+ break;
+ }
+ if (audio->stopped && !audio->in_count) {
+ MM_DBG("Driver in stop state, No more buffer to read");
+ rc = 0;/* End of File */
+ break;
+ } else if ((audio->mode == MSM_AUD_ENC_MODE_TUNNEL) &&
+ audio->in_call && audio->running &&
+ (audio->voice_state \
+ == VOICE_STATE_OFFCALL)) {
+ MM_DBG("Not Permitted Voice Terminated\n");
+ rc = -EPERM; /* Voice Call stopped */
+ break;
+ }
+
+ index = audio->in_tail;
+ data = (uint8_t *) audio->in[index].data;
+ size = audio->in[index].size;
+
+ if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) {
+ nt_frame = (struct audio_frame_nt *)(data -
+ sizeof(struct audio_frame_nt));
+ memcpy((char *)&meta_field.time_stamp_dword_lsw,
+ (char *)&nt_frame->time_stamp_dword_lsw,
+ (sizeof(struct qcelp_encoded_meta_out) - \
+ sizeof(uint16_t)));
+ meta_field.metadata_len =
+ sizeof(struct qcelp_encoded_meta_out);
+ if (copy_to_user((char *)start,
+ (char *)&meta_field,
+ sizeof(struct qcelp_encoded_meta_out))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (nt_frame->nflag_lsw & 0x0001) {
+ MM_ERR("recieved EOS in read call\n");
+ audio->eos_ack = 1;
+ }
+ buf += sizeof(struct qcelp_encoded_meta_out);
+ count -= sizeof(struct qcelp_encoded_meta_out);
+ }
+ if (count >= size) {
+ if (copy_to_user(buf, data, size)) {
+ rc = -EFAULT;
+ break;
+ }
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (index != audio->in_tail) {
+ /* overrun -- data is
+ * invalid and we need to retry */
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ continue;
+ }
+ audio->in[index].size = 0;
+ audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1);
+ audio->in_count--;
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ count -= size;
+ buf += size;
+ if ((audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL)) {
+ if (!audio->eos_ack) {
+ MM_DBG("sending read ptr command\
+ %d %d\n",
+ audio->dsp_cnt,
+ audio->in_tail);
+ audqcelp_dsp_read_buffer(audio,
+ audio->dsp_cnt++);
+ }
+ }
+ } else {
+ MM_ERR("short read\n");
+ break;
+ }
+ break;
+ }
+ mutex_unlock(&audio->read_lock);
+
+ if (buf > start)
+ return buf - start;
+
+ return rc;
+}
+
+static void audpreproc_pcm_send_data(struct audio_in *audio, unsigned needed)
+{
+ struct buffer *frame;
+ unsigned long flags;
+ MM_DBG("\n");
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (!audio->running)
+ goto done;
+
+ if (needed && !audio->wflush) {
+ /* We were called from the callback because the DSP
+ * requested more data. Note that the DSP does want
+ * more data, and if a buffer was in-flight, mark it
+ * as available (since the DSP must now be done with
+ * it).
+ */
+ audio->out_needed = 1;
+ frame = audio->out + audio->out_tail;
+ if (frame->used == 0xffffffff) {
+ MM_DBG("frame %d free\n", audio->out_tail);
+ frame->used = 0;
+ audio->out_tail ^= 1;
+ wake_up(&audio->write_wait);
+ }
+ }
+
+ if (audio->out_needed) {
+ /* If the DSP currently wants data and we have a
+ * buffer available, we will send it and reset
+ * the needed flag. We'll mark the buffer as in-flight
+ * so that it won't be recycled until the next buffer
+ * is requested
+ */
+
+ frame = audio->out + audio->out_tail;
+ if (frame->used) {
+ BUG_ON(frame->used == 0xffffffff);
+ audpreproc_pcm_buffer_ptr_refresh(audio,
+ audio->out_tail,
+ frame->used);
+ frame->used = 0xffffffff;
+ audio->out_needed = 0;
+ }
+ }
+ done:
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+
+static int audqcelp_in_fsync(struct file *file, int datasync)
+
+{
+ struct audio_in *audio = file->private_data;
+ int rc = 0;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ if (!audio->running || (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)) {
+ rc = -EINVAL;
+ goto done_nolock;
+ }
+
+ mutex_lock(&audio->write_lock);
+
+ rc = wait_event_interruptible(audio->write_wait,
+ audio->wflush);
+ MM_DBG("waked on by some event audio->wflush = %d\n", audio->wflush);
+
+ if (rc < 0)
+ goto done;
+ else if (audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+done:
+ mutex_unlock(&audio->write_lock);
+done_nolock:
+ return rc;
+
+}
+
+ int audpreproc_qcelp_process_eos(struct audio_in *audio,
+ const char __user *buf_start, unsigned short mfield_size)
+{
+ struct buffer *frame;
+ int rc = 0;
+
+ frame = audio->out + audio->out_head;
+
+ rc = wait_event_interruptible(audio->write_wait,
+ (audio->out_needed &&
+ audio->out[0].used == 0 &&
+ audio->out[1].used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+
+ if (rc < 0)
+ goto done;
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+ if (copy_from_user(frame->data, buf_start, mfield_size)) {
+ rc = -EFAULT;
+ goto done;
+ }
+
+ frame->mfield_sz = mfield_size;
+ audio->out_head ^= 1;
+ frame->used = mfield_size;
+ MM_DBG("copying meta_out frame->used = %d\n", frame->used);
+ audpreproc_pcm_send_data(audio, 0);
+done:
+ return rc;
+}
+
+static ssize_t audqcelp_in_write(struct file *file,
+ const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio_in *audio = file->private_data;
+ const char __user *start = buf;
+ struct buffer *frame;
+ char *cpy_ptr;
+ int rc = 0, eos_condition = AUDPREPROC_QCELP_EOS_NONE;
+ unsigned short mfield_size = 0;
+ int write_count = 0;
+
+ MM_DBG("cnt=%d\n", count);
+ if (count & 1)
+ return -EINVAL;
+
+ if (audio->mode != MSM_AUD_ENC_MODE_NONTUNNEL)
+ return -EINVAL;
+
+ mutex_lock(&audio->write_lock);
+ frame = audio->out + audio->out_head;
+ /* if supplied count is more than driver buffer size
+ * then only copy driver buffer size
+ */
+ if (count > frame->size)
+ count = frame->size;
+
+ write_count = count;
+ cpy_ptr = frame->data;
+ rc = wait_event_interruptible(audio->write_wait,
+ (frame->used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+ if (rc < 0)
+ goto error;
+
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ goto error;
+ }
+ if (audio->mfield) {
+ if (buf == start) {
+ /* Processing beginning of user buffer */
+ if (__get_user(mfield_size,
+ (unsigned short __user *) buf)) {
+ rc = -EFAULT;
+ goto error;
+ } else if (mfield_size > count) {
+ rc = -EINVAL;
+ goto error;
+ }
+ MM_DBG("mf offset_val %x\n", mfield_size);
+ if (copy_from_user(cpy_ptr, buf, mfield_size)) {
+ rc = -EFAULT;
+ goto error;
+ }
+ /* Check if EOS flag is set and buffer has
+ * contains just meta field
+ */
+ if (cpy_ptr[AUDPREPROC_QCELP_EOS_FLG_OFFSET] &
+ AUDPREPROC_QCELP_EOS_FLG_MASK) {
+ eos_condition = AUDPREPROC_QCELP_EOS_SET;
+ MM_DBG("EOS SET\n");
+ if (mfield_size == count) {
+ buf += mfield_size;
+ eos_condition = 0;
+ goto exit;
+ } else
+ cpy_ptr[AUDPREPROC_QCELP_EOS_FLG_OFFSET] &=
+ ~AUDPREPROC_QCELP_EOS_FLG_MASK;
+ }
+ cpy_ptr += mfield_size;
+ count -= mfield_size;
+ buf += mfield_size;
+ } else {
+ mfield_size = 0;
+ MM_DBG("continuous buffer\n");
+ }
+ frame->mfield_sz = mfield_size;
+ }
+ MM_DBG("copying the stream count = %d\n", count);
+ if (copy_from_user(cpy_ptr, buf, count)) {
+ rc = -EFAULT;
+ goto error;
+ }
+exit:
+ frame->used = count;
+ audio->out_head ^= 1;
+ if (!audio->flush_ack)
+ audpreproc_pcm_send_data(audio, 0);
+ else {
+ audpreproc_pcm_send_data(audio, 1);
+ audio->flush_ack = 0;
+ }
+ if (eos_condition == AUDPREPROC_QCELP_EOS_SET)
+ rc = audpreproc_qcelp_process_eos(audio, start, mfield_size);
+ mutex_unlock(&audio->write_lock);
+ return write_count;
+error:
+ mutex_unlock(&audio->write_lock);
+ return rc;
+}
+
+static int audqcelp_in_release(struct inode *inode, struct file *file)
+{
+ struct audio_in *audio = file->private_data;
+
+ mutex_lock(&audio->lock);
+ audio->in_call = 0;
+ /* with draw frequency for session
+ incase not stopped the driver */
+ msm_snddev_withdraw_freq(audio->enc_id, SNDDEV_CAP_TX,
+ AUDDEV_CLNT_ENC);
+ auddev_unregister_evt_listner(AUDDEV_CLNT_ENC, audio->enc_id);
+ /*reset the sampling frequency information at audpreproc layer*/
+ audio->session_info.sampling_freq = 0;
+ audpreproc_update_audrec_info(&audio->session_info);
+ audqcelp_in_disable(audio);
+ audqcelp_in_flush(audio);
+ msm_adsp_put(audio->audrec);
+ audpreproc_aenc_free(audio->enc_id);
+ audio->audrec = NULL;
+ audio->opened = 0;
+ if (audio->data) {
+ iounmap(audio->data);
+ pmem_kfree(audio->phys);
+ audio->data = NULL;
+ }
+ if (audio->out_data) {
+ iounmap(audio->out_data);
+ pmem_kfree(audio->out_phys);
+ audio->out_data = NULL;
+ }
+ mutex_unlock(&audio->lock);
+ return 0;
+}
+
+struct audio_in the_audio_qcelp_in;
+static int audqcelp_in_open(struct inode *inode, struct file *file)
+{
+ struct audio_in *audio = &the_audio_qcelp_in;
+ int rc;
+ int encid;
+
+ mutex_lock(&audio->lock);
+ if (audio->opened) {
+ rc = -EBUSY;
+ goto done;
+ }
+ audio->phys = pmem_kalloc(DMASZ, PMEM_MEMTYPE_EBI1|
+ PMEM_ALIGNMENT_4K);
+ if (!IS_ERR((void *)audio->phys)) {
+ audio->data = ioremap(audio->phys, DMASZ);
+ if (!audio->data) {
+ MM_ERR("could not allocate DMA buffers\n");
+ rc = -ENOMEM;
+ pmem_kfree(audio->phys);
+ goto done;
+ }
+ } else {
+ MM_ERR("could not allocate DMA buffers\n");
+ rc = -ENOMEM;
+ goto done;
+ }
+ MM_DBG("Memory addr = 0x%8x phy addr = 0x%8x\n",\
+ (int) audio->data, (int) audio->phys);
+ if ((file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ audio->mode = MSM_AUD_ENC_MODE_NONTUNNEL;
+ MM_DBG("Opened for non tunnel mode encoding\n");
+ } else if (!(file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ audio->mode = MSM_AUD_ENC_MODE_TUNNEL;
+ MM_DBG("Opened for tunnel mode encoding\n");
+ } else {
+ MM_ERR("Invalid mode\n");
+ rc = -EACCES;
+ goto done;
+ }
+
+ /* Settings will be re-config at AUDIO_SET_CONFIG,
+ * but at least we need to have initial config
+ */
+ if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL)
+ audio->buffer_size = (QCELP_FRAME_SIZE + 14);
+ else
+ audio->buffer_size = (FRAME_SIZE - 8);
+ audio->enc_type = ENC_TYPE_V13K | audio->mode;
+ audio->samp_rate = 8000;
+ audio->channel_mode = AUDREC_CMD_MODE_MONO;
+ audio->cfg.cdma_rate = CDMA_RATE_FULL;
+ audio->cfg.min_bit_rate = CDMA_RATE_FULL;
+ audio->cfg.max_bit_rate = CDMA_RATE_FULL;
+ audio->source = INTERNAL_CODEC_TX_SOURCE_MIX_MASK;
+ audio->rec_mode = VOC_REC_UPLINK;
+
+ encid = audpreproc_aenc_alloc(audio->enc_type, &audio->module_name,
+ &audio->queue_ids);
+ if (encid < 0) {
+ MM_ERR("No free encoder available\n");
+ rc = -ENODEV;
+ goto done;
+ }
+ audio->enc_id = encid;
+
+ rc = msm_adsp_get(audio->module_name, &audio->audrec,
+ &audrec_qcelp_adsp_ops, audio);
+
+ if (rc) {
+ audpreproc_aenc_free(audio->enc_id);
+ goto done;
+ }
+
+ audio->stopped = 0;
+ audio->source = 0;
+ audio->wflush = 0;
+ audio->rflush = 0;
+ audio->flush_ack = 0;
+
+ audqcelp_in_flush(audio);
+ audqcelp_out_flush(audio);
+
+ audio->out_phys = pmem_kalloc(BUFFER_SIZE,
+ PMEM_MEMTYPE_EBI1 | PMEM_ALIGNMENT_4K);
+ if (IS_ERR((void *)audio->out_phys)) {
+ MM_ERR("could not allocate write buffers\n");
+ rc = -ENOMEM;
+ goto evt_error;
+ } else {
+ audio->out_data = ioremap(audio->out_phys, BUFFER_SIZE);
+ if (!audio->out_data) {
+ MM_ERR("could not allocate write buffers\n");
+ rc = -ENOMEM;
+ pmem_kfree(audio->out_phys);
+ goto evt_error;
+ }
+ MM_DBG("write buf: phy addr 0x%08x kernel addr 0x%08x\n",
+ audio->out_phys, (int)audio->out_data);
+ }
+
+ /* Initialize buffer */
+ audio->out[0].data = audio->out_data + 0;
+ audio->out[0].addr = audio->out_phys + 0;
+ audio->out[0].size = OUT_BUFFER_SIZE;
+
+ audio->out[1].data = audio->out_data + OUT_BUFFER_SIZE;
+ audio->out[1].addr = audio->out_phys + OUT_BUFFER_SIZE;
+ audio->out[1].size = OUT_BUFFER_SIZE;
+
+ MM_DBG("audio->out[0].data = %d audio->out[1].data = %d",
+ (unsigned int)audio->out[0].data,
+ (unsigned int)audio->out[1].data);
+ audio->device_events = AUDDEV_EVT_DEV_RDY | AUDDEV_EVT_DEV_RLS |
+ AUDDEV_EVT_VOICE_STATE_CHG;
+
+ audio->voice_state = msm_get_voice_state();
+ rc = auddev_register_evt_listner(audio->device_events,
+ AUDDEV_CLNT_ENC, audio->enc_id,
+ qcelp_in_listener, (void *) audio);
+ if (rc) {
+ MM_ERR("failed to register device event listener\n");
+ iounmap(audio->out_data);
+ pmem_kfree(audio->out_phys);
+ goto evt_error;
+ }
+ audio->mfield = META_OUT_SIZE;
+ file->private_data = audio;
+ audio->opened = 1;
+ audio->out_frame_cnt++;
+done:
+ mutex_unlock(&audio->lock);
+ return rc;
+evt_error:
+ msm_adsp_put(audio->audrec);
+ audpreproc_aenc_free(audio->enc_id);
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static const struct file_operations audio_in_fops = {
+ .owner = THIS_MODULE,
+ .open = audqcelp_in_open,
+ .release = audqcelp_in_release,
+ .read = audqcelp_in_read,
+ .write = audqcelp_in_write,
+ .fsync = audqcelp_in_fsync,
+ .unlocked_ioctl = audqcelp_in_ioctl,
+};
+
+struct miscdevice audio_qcelp_in_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_qcelp_in",
+ .fops = &audio_in_fops,
+};
+
+static int __init audqcelp_in_init(void)
+{
+ mutex_init(&the_audio_qcelp_in.lock);
+ mutex_init(&the_audio_qcelp_in.read_lock);
+ spin_lock_init(&the_audio_qcelp_in.dsp_lock);
+ spin_lock_init(&the_audio_qcelp_in.dev_lock);
+ init_waitqueue_head(&the_audio_qcelp_in.wait);
+ init_waitqueue_head(&the_audio_qcelp_in.wait_enable);
+ mutex_init(&the_audio_qcelp_in.write_lock);
+ init_waitqueue_head(&the_audio_qcelp_in.write_wait);
+ return misc_register(&audio_qcelp_in_misc);
+}
+
+device_initcall(audqcelp_in_init);
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_wma.c b/arch/arm/mach-msm/qdsp5v2/audio_wma.c
new file mode 100644
index 0000000..2695b83
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_wma.c
@@ -0,0 +1,1785 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Based on the mp3 native driver in arch/arm/mach-msm/qdsp5v2/audio_mp3.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ *
+ * All source code in this file is licensed under the following license except
+ * where indicated.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/earlysuspend.h>
+#include <linux/android_pmem.h>
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+#include <linux/slab.h>
+#include <linux/msm_audio.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+
+#include <linux/msm_audio_wma.h>
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/qdsp5audplaycmdi.h>
+#include <mach/qdsp5v2/qdsp5audplaymsg.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/qdsp5v2/audpp.h>
+#include <mach/debug_mm.h>
+
+/* Size must be power of 2 */
+#define BUFSZ_MAX 4110 /* Includes meta in size */
+#define BUFSZ_MIN 1038 /* Includes meta in size */
+#define DMASZ_MAX (BUFSZ_MAX * 2)
+#define DMASZ_MIN (BUFSZ_MIN * 2)
+
+#define AUDPLAY_INVALID_READ_PTR_OFFSET 0xFFFF
+#define AUDDEC_DEC_WMA 4
+
+#define PCM_BUFSZ_MIN 8216 /* Hold one stereo WMA frame and meta out*/
+#define PCM_BUF_MAX_COUNT 5 /* DSP only accepts 5 buffers at most
+ but support 2 buffers currently */
+#define ROUTING_MODE_FTRT 1
+#define ROUTING_MODE_RT 2
+/* Decoder status received from AUDPPTASK */
+#define AUDPP_DEC_STATUS_SLEEP 0
+#define AUDPP_DEC_STATUS_INIT 1
+#define AUDPP_DEC_STATUS_CFG 2
+#define AUDPP_DEC_STATUS_PLAY 3
+
+#define AUDWMA_METAFIELD_MASK 0xFFFF0000
+#define AUDWMA_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */
+#define AUDWMA_EOS_FLG_MASK 0x01
+#define AUDWMA_EOS_NONE 0x0 /* No EOS detected */
+#define AUDWMA_EOS_SET 0x1 /* EOS set in meta field */
+
+#define AUDWMA_EVENT_NUM 10 /* Default number of pre-allocated event packets */
+
+struct buffer {
+ void *data;
+ unsigned size;
+ unsigned used; /* Input usage actual DSP produced PCM size */
+ unsigned addr;
+ unsigned short mfield_sz; /*only useful for data has meta field */
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+struct audwma_suspend_ctl {
+ struct early_suspend node;
+ struct audio *audio;
+};
+#endif
+
+struct audwma_event{
+ struct list_head list;
+ int event_type;
+ union msm_audio_event_payload payload;
+};
+
+struct audio {
+ struct buffer out[2];
+
+ spinlock_t dsp_lock;
+
+ uint8_t out_head;
+ uint8_t out_tail;
+ uint8_t out_needed; /* number of buffers the dsp is waiting for */
+ unsigned out_dma_sz;
+
+ atomic_t out_bytes;
+
+ struct mutex lock;
+ struct mutex write_lock;
+ wait_queue_head_t write_wait;
+
+ /* Host PCM section */
+ struct buffer in[PCM_BUF_MAX_COUNT];
+ struct mutex read_lock;
+ wait_queue_head_t read_wait; /* Wait queue for read */
+ char *read_data; /* pointer to reader buffer */
+ int32_t read_phys; /* physical address of reader buffer */
+ uint8_t read_next; /* index to input buffers to be read next */
+ uint8_t fill_next; /* index to buffer that DSP should be filling */
+ uint8_t pcm_buf_count; /* number of pcm buffer allocated */
+ /* ---- End of Host PCM section */
+
+ struct msm_adsp_module *audplay;
+
+ /* configuration to use on next enable */
+ uint32_t out_sample_rate;
+ uint32_t out_channel_mode;
+
+ struct msm_audio_wma_config wma_config;
+
+ /* data allocated for various buffers */
+ char *data;
+ int32_t phys; /* physical address of write buffer */
+
+ int mfield; /* meta field embedded in data */
+ int rflush; /* Read flush */
+ int wflush; /* Write flush */
+ int opened;
+ int enabled;
+ int running;
+ int stopped; /* set when stopped, cleared on flush */
+ int pcm_feedback;
+ int buf_refresh;
+ int teos; /* valid only if tunnel mode & no data left for decoder */
+ enum msm_aud_decoder_state dec_state; /* Represents decoder state */
+ int reserved; /* A byte is being reserved */
+ char rsv_byte; /* Handle odd length user data */
+
+ const char *module_name;
+ unsigned queue_id;
+ uint16_t dec_id;
+ uint32_t read_ptr_offset;
+ int16_t source;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct audwma_suspend_ctl suspend_ctl;
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dentry;
+#endif
+
+ wait_queue_head_t wait;
+ struct list_head free_event_queue;
+ struct list_head event_queue;
+ wait_queue_head_t event_wait;
+ spinlock_t event_queue_lock;
+ struct mutex get_event_lock;
+ int event_abort;
+ /* AV sync Info */
+ int avsync_flag; /* Flag to indicate feedback from DSP */
+ wait_queue_head_t avsync_wait;/* Wait queue for AV Sync Message */
+ /* flags, 48 bits sample/bytes counter per channel */
+ uint16_t avsync[AUDPP_AVSYNC_CH_COUNT * AUDPP_AVSYNC_NUM_WORDS + 1];
+
+ uint32_t device_events;
+
+ int eq_enable;
+ int eq_needs_commit;
+ struct audpp_cmd_cfg_object_params_eqalizer eq;
+ struct audpp_cmd_cfg_object_params_volume vol_pan;
+};
+
+static int auddec_dsp_config(struct audio *audio, int enable);
+static void audpp_cmd_cfg_adec_params(struct audio *audio);
+static void audpp_cmd_cfg_routing_mode(struct audio *audio);
+static void audplay_send_data(struct audio *audio, unsigned needed);
+static void audplay_config_hostpcm(struct audio *audio);
+static void audplay_buffer_refresh(struct audio *audio);
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg);
+static void audwma_post_event(struct audio *audio, int type,
+ union msm_audio_event_payload payload);
+
+/* must be called with audio->lock held */
+static int audio_enable(struct audio *audio)
+{
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ if (audio->enabled)
+ return 0;
+
+ audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+ audio->out_tail = 0;
+ audio->out_needed = 0;
+
+ if (msm_adsp_enable(audio->audplay)) {
+ MM_ERR("msm_adsp_enable(audplay) failed\n");
+ return -ENODEV;
+ }
+
+ if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) {
+ MM_ERR("audpp_enable() failed\n");
+ msm_adsp_disable(audio->audplay);
+ return -ENODEV;
+ }
+
+ audio->enabled = 1;
+ return 0;
+}
+
+static void wma_listner(u32 evt_id, union auddev_evt_data *evt_payload,
+ void *private_data)
+{
+ struct audio *audio = (struct audio *) private_data;
+ switch (evt_id) {
+ case AUDDEV_EVT_DEV_RDY:
+ MM_DBG(":AUDDEV_EVT_DEV_RDY\n");
+ audio->source |= (0x1 << evt_payload->routing_id);
+ if (audio->running == 1 && audio->enabled == 1)
+ audpp_route_stream(audio->dec_id, audio->source);
+ break;
+ case AUDDEV_EVT_DEV_RLS:
+ MM_DBG(":AUDDEV_EVT_DEV_RLS\n");
+ audio->source &= ~(0x1 << evt_payload->routing_id);
+ if (audio->running == 1 && audio->enabled == 1)
+ audpp_route_stream(audio->dec_id, audio->source);
+ break;
+ case AUDDEV_EVT_STREAM_VOL_CHG:
+ audio->vol_pan.volume = evt_payload->session_vol;
+ MM_DBG(":AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n",
+ audio->vol_pan.volume);
+ if (audio->running)
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ break;
+ default:
+ MM_ERR(":ERROR:wrong event\n");
+ break;
+ }
+}
+/* must be called with audio->lock held */
+static int audio_disable(struct audio *audio)
+{
+ int rc = 0;
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ if (audio->enabled) {
+ audio->enabled = 0;
+ audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+ auddec_dsp_config(audio, 0);
+ rc = wait_event_interruptible_timeout(audio->wait,
+ audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+ msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE)
+ rc = -EFAULT;
+ else
+ rc = 0;
+ wake_up(&audio->write_wait);
+ wake_up(&audio->read_wait);
+ msm_adsp_disable(audio->audplay);
+ audpp_disable(audio->dec_id, audio);
+ audio->out_needed = 0;
+ }
+ return rc;
+}
+
+/* ------------------- dsp --------------------- */
+static void audio_update_pcm_buf_entry(struct audio *audio,
+ uint32_t *payload)
+{
+ uint8_t index;
+ unsigned long flags;
+
+ if (audio->rflush)
+ return;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ for (index = 0; index < payload[1]; index++) {
+ if (audio->in[audio->fill_next].addr ==
+ payload[2 + index * 2]) {
+ MM_DBG("audio_update_pcm_buf_entry: \
+ in[%d] ready\n", audio->fill_next);
+ audio->in[audio->fill_next].used =
+ payload[3 + index * 2];
+ if ((++audio->fill_next) == audio->pcm_buf_count)
+ audio->fill_next = 0;
+ } else {
+ MM_ERR("audio_update_pcm_buf_entry: \
+ expected=%x ret=%x\n",
+ audio->in[audio->fill_next].addr,
+ payload[1 + index * 2]);
+ break;
+ }
+ }
+ if (audio->in[audio->fill_next].used == 0) {
+ audplay_buffer_refresh(audio);
+ } else {
+ MM_DBG("read cannot keep up\n");
+ audio->buf_refresh = 1;
+ }
+ wake_up(&audio->read_wait);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+static void audplay_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent) (void *ptr, size_t len))
+{
+ struct audio *audio = data;
+ uint32_t msg[28];
+
+ getevent(msg, sizeof(msg));
+
+ MM_DBG("msg_id=%x\n", id);
+
+ switch (id) {
+ case AUDPLAY_MSG_DEC_NEEDS_DATA:
+ audplay_send_data(audio, 1);
+ break;
+
+ case AUDPLAY_MSG_BUFFER_UPDATE:
+ audio_update_pcm_buf_entry(audio, msg);
+ break;
+
+ case ADSP_MESSAGE_ID:
+ MM_DBG("Received ADSP event: module enable(audplaytask)\n");
+ break;
+
+ default:
+ MM_ERR("unexpected message from decoder \n");
+ break;
+ }
+}
+
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg)
+{
+ struct audio *audio = private;
+
+ switch (id) {
+ case AUDPP_MSG_STATUS_MSG:{
+ unsigned status = msg[1];
+
+ switch (status) {
+ case AUDPP_DEC_STATUS_SLEEP: {
+ uint16_t reason = msg[2];
+ MM_DBG("decoder status:sleep reason = \
+ 0x%04x\n", reason);
+ if ((reason == AUDPP_MSG_REASON_MEM)
+ || (reason ==
+ AUDPP_MSG_REASON_NODECODER)) {
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_FAILURE;
+ wake_up(&audio->wait);
+ } else if (reason == AUDPP_MSG_REASON_NONE) {
+ /* decoder is in disable state */
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_CLOSE;
+ wake_up(&audio->wait);
+ }
+ break;
+ }
+ case AUDPP_DEC_STATUS_INIT:
+ MM_DBG("decoder status: init\n");
+ if (audio->pcm_feedback)
+ audpp_cmd_cfg_routing_mode(audio);
+ else
+ audpp_cmd_cfg_adec_params(audio);
+ break;
+
+ case AUDPP_DEC_STATUS_CFG:
+ MM_DBG("decoder status: cfg\n");
+ break;
+ case AUDPP_DEC_STATUS_PLAY:
+ MM_DBG("decoder status: play \n");
+ /* send mixer command */
+ audpp_route_stream(audio->dec_id,
+ audio->source);
+ if (audio->pcm_feedback) {
+ audplay_config_hostpcm(audio);
+ audplay_buffer_refresh(audio);
+ }
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_SUCCESS;
+ wake_up(&audio->wait);
+ break;
+ default:
+ MM_ERR("unknown decoder status\n");
+ }
+ break;
+ }
+ case AUDPP_MSG_CFG_MSG:
+ if (msg[0] == AUDPP_MSG_ENA_ENA) {
+ MM_DBG("CFG_MSG ENABLE\n");
+ auddec_dsp_config(audio, 1);
+ audio->out_needed = 0;
+ audio->running = 1;
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ audpp_dsp_set_eq(audio->dec_id, audio->eq_enable,
+ &audio->eq, POPP);
+ } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+ MM_DBG("CFG_MSG DISABLE\n");
+ audio->running = 0;
+ } else {
+ MM_DBG("CFG_MSG %d?\n", msg[0]);
+ }
+ break;
+ case AUDPP_MSG_ROUTING_ACK:
+ MM_DBG("ROUTING_ACK mode=%d\n", msg[1]);
+ audpp_cmd_cfg_adec_params(audio);
+ break;
+
+ case AUDPP_MSG_FLUSH_ACK:
+ MM_DBG("FLUSH_ACK\n");
+ audio->wflush = 0;
+ audio->rflush = 0;
+ wake_up(&audio->write_wait);
+ if (audio->pcm_feedback)
+ audplay_buffer_refresh(audio);
+ break;
+
+ case AUDPP_MSG_PCMDMAMISSED:
+ MM_DBG("PCMDMAMISSED\n");
+ audio->teos = 1;
+ wake_up(&audio->write_wait);
+ break;
+
+ case AUDPP_MSG_AVSYNC_MSG:
+ MM_DBG("AUDPP_MSG_AVSYNC_MSG\n");
+ memcpy(&audio->avsync[0], msg, sizeof(audio->avsync));
+ audio->avsync_flag = 1;
+ wake_up(&audio->avsync_wait);
+ break;
+
+ default:
+ MM_ERR("UNKNOWN (%d)\n", id);
+ }
+
+}
+
+static struct msm_adsp_ops audplay_adsp_ops_wma = {
+ .event = audplay_dsp_event,
+};
+
+#define audplay_send_queue0(audio, cmd, len) \
+ msm_adsp_write(audio->audplay, audio->queue_id, \
+ cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+ struct audpp_cmd_cfg_dec_type cfg_dec_cmd;
+
+ memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd));
+
+ cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+ if (enable)
+ cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_WMA;
+ else
+ cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_DIS_DEC_V;
+ cfg_dec_cmd.dm_mode = 0x0;
+ cfg_dec_cmd.stream_id = audio->dec_id;
+ return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd));
+}
+
+static void audpp_cmd_cfg_adec_params(struct audio *audio)
+{
+ struct audpp_cmd_cfg_adec_params_wma cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+ cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_WMA_LEN;
+ cmd.common.dec_id = audio->dec_id;
+ cmd.common.input_sampling_frequency = audio->out_sample_rate;
+
+ /*
+ * Test done for sample with the following configuration
+ * armdatareqthr = 1262
+ * channelsdecoded = 1(MONO)/2(STEREO)
+ * wmabytespersec = Tested with 6003 Bytes per sec
+ * wmasamplingfreq = 44100
+ * wmaencoderopts = 31
+ */
+
+ cmd.armdatareqthr = audio->wma_config.armdatareqthr;
+ cmd.channelsdecoded = audio->wma_config.channelsdecoded;
+ cmd.wmabytespersec = audio->wma_config.wmabytespersec;
+ cmd.wmasamplingfreq = audio->wma_config.wmasamplingfreq;
+ cmd.wmaencoderopts = audio->wma_config.wmaencoderopts;
+
+ audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_routing_mode(struct audio *audio)
+{
+ struct audpp_cmd_routing_mode cmd;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_ROUTING_MODE;
+ cmd.object_number = audio->dec_id;
+ if (audio->pcm_feedback)
+ cmd.routing_mode = ROUTING_MODE_FTRT;
+ else
+ cmd.routing_mode = ROUTING_MODE_RT;
+
+ audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static void audplay_buffer_refresh(struct audio *audio)
+{
+ struct audplay_cmd_buffer_refresh refresh_cmd;
+
+ refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH;
+ refresh_cmd.num_buffers = 1;
+ refresh_cmd.buf0_address = audio->in[audio->fill_next].addr;
+ refresh_cmd.buf0_length = audio->in[audio->fill_next].size;
+ refresh_cmd.buf_read_count = 0;
+
+ MM_DBG("buf0_addr=%x buf0_len=%d\n",
+ refresh_cmd.buf0_address,
+ refresh_cmd.buf0_length);
+
+ (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd));
+}
+
+static void audplay_config_hostpcm(struct audio *audio)
+{
+ struct audplay_cmd_hpcm_buf_cfg cfg_cmd;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG;
+ cfg_cmd.max_buffers = audio->pcm_buf_count;
+ cfg_cmd.byte_swap = 0;
+ cfg_cmd.hostpcm_config = (0x8000) | (0x4000);
+ cfg_cmd.feedback_frequency = 1;
+ cfg_cmd.partition_number = 0;
+
+ (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd));
+}
+
+
+static int audplay_dsp_send_data_avail(struct audio *audio,
+ unsigned idx, unsigned len)
+{
+ struct audplay_cmd_bitstream_data_avail_nt2 cmd;
+
+ cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2;
+ if (!audio->pcm_feedback)
+ cmd.decoder_id = 0;
+ else {
+ if (audio->mfield)
+ cmd.decoder_id = AUDWMA_METAFIELD_MASK |
+ (audio->out[idx].mfield_sz >> 1);
+ else
+ cmd.decoder_id = audio->dec_id;
+ }
+ cmd.buf_ptr = audio->out[idx].addr;
+ cmd.buf_size = len/2;
+ cmd.partition_number = 0;
+ return audplay_send_queue0(audio, &cmd, sizeof(cmd));
+}
+
+static void audplay_send_data(struct audio *audio, unsigned needed)
+{
+ struct buffer *frame;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (!audio->running)
+ goto done;
+
+ if (audio->wflush) {
+ audio->out_needed = 1;
+ goto done;
+ }
+
+ if (needed && !audio->wflush) {
+ /* We were called from the callback because the DSP
+ * requested more data. Note that the DSP does want
+ * more data, and if a buffer was in-flight, mark it
+ * as available (since the DSP must now be done with
+ * it).
+ */
+ audio->out_needed = 1;
+ frame = audio->out + audio->out_tail;
+ if (frame->used == 0xffffffff) {
+ MM_DBG("frame %d free\n", audio->out_tail);
+ frame->used = 0;
+ audio->out_tail ^= 1;
+ wake_up(&audio->write_wait);
+ }
+ }
+
+ if (audio->out_needed) {
+ /* If the DSP currently wants data and we have a
+ * buffer available, we will send it and reset
+ * the needed flag. We'll mark the buffer as in-flight
+ * so that it won't be recycled until the next buffer
+ * is requested
+ */
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ frame = audio->out + audio->out_tail;
+ if (frame->used) {
+ BUG_ON(frame->used == 0xffffffff);
+ MM_DBG("frame %d busy\n", audio->out_tail);
+ audplay_dsp_send_data_avail(audio, audio->out_tail,
+ frame->used);
+ frame->used = 0xffffffff;
+ audio->out_needed = 0;
+ }
+ }
+done:
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+/* ------------------- device --------------------- */
+
+static void audio_flush(struct audio *audio)
+{
+ audio->out[0].used = 0;
+ audio->out[1].used = 0;
+ audio->out_head = 0;
+ audio->out_tail = 0;
+ audio->reserved = 0;
+ atomic_set(&audio->out_bytes, 0);
+}
+
+static void audio_flush_pcm_buf(struct audio *audio)
+{
+ uint8_t index;
+
+ for (index = 0; index < PCM_BUF_MAX_COUNT; index++)
+ audio->in[index].used = 0;
+ audio->buf_refresh = 0;
+ audio->read_next = 0;
+ audio->fill_next = 0;
+}
+
+static void audio_ioport_reset(struct audio *audio)
+{
+ /* Make sure read/write thread are free from
+ * sleep and knowing that system is not able
+ * to process io request at the moment
+ */
+ wake_up(&audio->write_wait);
+ mutex_lock(&audio->write_lock);
+ audio_flush(audio);
+ mutex_unlock(&audio->write_lock);
+ wake_up(&audio->read_wait);
+ mutex_lock(&audio->read_lock);
+ audio_flush_pcm_buf(audio);
+ mutex_unlock(&audio->read_lock);
+ audio->avsync_flag = 1;
+ wake_up(&audio->avsync_wait);
+}
+
+static int audwma_events_pending(struct audio *audio)
+{
+ unsigned long flags;
+ int empty;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ empty = !list_empty(&audio->event_queue);
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+ return empty || audio->event_abort;
+}
+
+static void audwma_reset_event_queue(struct audio *audio)
+{
+ unsigned long flags;
+ struct audwma_event *drv_evt;
+ struct list_head *ptr, *next;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ list_for_each_safe(ptr, next, &audio->event_queue) {
+ drv_evt = list_first_entry(&audio->event_queue,
+ struct audwma_event, list);
+ list_del(&drv_evt->list);
+ kfree(drv_evt);
+ }
+ list_for_each_safe(ptr, next, &audio->free_event_queue) {
+ drv_evt = list_first_entry(&audio->free_event_queue,
+ struct audwma_event, list);
+ list_del(&drv_evt->list);
+ kfree(drv_evt);
+ }
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+ return;
+}
+
+static long audwma_process_event_req(struct audio *audio, void __user *arg)
+{
+ long rc;
+ struct msm_audio_event usr_evt;
+ struct audwma_event *drv_evt = NULL;
+ int timeout;
+ unsigned long flags;
+
+ if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event)))
+ return -EFAULT;
+
+ timeout = (int) usr_evt.timeout_ms;
+
+ if (timeout > 0) {
+ rc = wait_event_interruptible_timeout(
+ audio->event_wait, audwma_events_pending(audio),
+ msecs_to_jiffies(timeout));
+ if (rc == 0)
+ return -ETIMEDOUT;
+ } else {
+ rc = wait_event_interruptible(
+ audio->event_wait, audwma_events_pending(audio));
+ }
+
+ if (rc < 0)
+ return rc;
+
+ if (audio->event_abort) {
+ audio->event_abort = 0;
+ return -ENODEV;
+ }
+
+ rc = 0;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ if (!list_empty(&audio->event_queue)) {
+ drv_evt = list_first_entry(&audio->event_queue,
+ struct audwma_event, list);
+ list_del(&drv_evt->list);
+ }
+
+ if (drv_evt) {
+ usr_evt.event_type = drv_evt->event_type;
+ usr_evt.event_payload = drv_evt->payload;
+ list_add_tail(&drv_evt->list, &audio->free_event_queue);
+ } else
+ rc = -1;
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+ if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt)))
+ rc = -EFAULT;
+
+ return rc;
+}
+
+static int audio_enable_eq(struct audio *audio, int enable)
+{
+ if (audio->eq_enable == enable && !audio->eq_needs_commit)
+ return 0;
+
+ audio->eq_enable = enable;
+
+ if (audio->running) {
+ audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq, POPP);
+ audio->eq_needs_commit = 0;
+ }
+ return 0;
+}
+
+static int audio_get_avsync_data(struct audio *audio,
+ struct msm_audio_stats *stats)
+{
+ int rc = -EINVAL;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) {
+ /* av_sync sample count */
+ stats->sample_count = (audio->avsync[2] << 16) |
+ (audio->avsync[3]);
+
+ /* av_sync byte_count */
+ stats->byte_count = (audio->avsync[5] << 16) |
+ (audio->avsync[6]);
+
+ audio->avsync_flag = 0;
+ rc = 0;
+ }
+ local_irq_restore(flags);
+ return rc;
+
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct audio *audio = file->private_data;
+ int rc = -EINVAL;
+ unsigned long flags = 0;
+ uint16_t enable_mask;
+ int enable;
+ int prev_state;
+
+ MM_DBG("cmd = %d\n", cmd);
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+
+ audio->avsync_flag = 0;
+ memset(&stats, 0, sizeof(stats));
+ if (audpp_query_avsync(audio->dec_id) < 0)
+ return rc;
+
+ rc = wait_event_interruptible_timeout(audio->avsync_wait,
+ (audio->avsync_flag == 1),
+ msecs_to_jiffies(AUDPP_AVSYNC_EVENT_TIMEOUT));
+
+ if (rc < 0)
+ return rc;
+ else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) {
+ if (audio_get_avsync_data(audio, &stats) < 0)
+ return rc;
+
+ if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ } else
+ return -EAGAIN;
+ }
+
+ switch (cmd) {
+ case AUDIO_ENABLE_AUDPP:
+ if (copy_from_user(&enable_mask, (void *) arg,
+ sizeof(enable_mask))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ enable = (enable_mask & EQ_ENABLE) ? 1 : 0;
+ audio_enable_eq(audio, enable);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ rc = 0;
+ break;
+ case AUDIO_SET_VOLUME:
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->vol_pan.volume = arg;
+ if (audio->running)
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ rc = 0;
+ break;
+
+ case AUDIO_SET_PAN:
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->vol_pan.pan = arg;
+ if (audio->running)
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ rc = 0;
+ break;
+
+ case AUDIO_SET_EQ:
+ prev_state = audio->eq_enable;
+ audio->eq_enable = 0;
+ if (copy_from_user(&audio->eq.num_bands, (void *) arg,
+ sizeof(audio->eq) -
+ (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) {
+ rc = -EFAULT;
+ break;
+ }
+ audio->eq_enable = prev_state;
+ audio->eq_needs_commit = 1;
+ rc = 0;
+ break;
+ }
+
+ if (-EINVAL != rc)
+ return rc;
+
+ if (cmd == AUDIO_GET_EVENT) {
+ MM_DBG("AUDIO_GET_EVENT\n");
+ if (mutex_trylock(&audio->get_event_lock)) {
+ rc = audwma_process_event_req(audio,
+ (void __user *) arg);
+ mutex_unlock(&audio->get_event_lock);
+ } else
+ rc = -EBUSY;
+ return rc;
+ }
+
+ if (cmd == AUDIO_ABORT_GET_EVENT) {
+ audio->event_abort = 1;
+ wake_up(&audio->event_wait);
+ return 0;
+ }
+
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START:
+ MM_DBG("AUDIO_START\n");
+ rc = audio_enable(audio);
+ if (!rc) {
+ rc = wait_event_interruptible_timeout(audio->wait,
+ audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+ msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+ MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc);
+
+ if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS)
+ rc = -ENODEV;
+ else
+ rc = 0;
+ }
+ break;
+ case AUDIO_STOP:
+ MM_DBG("AUDIO_STOP\n");
+ rc = audio_disable(audio);
+ audio->stopped = 1;
+ audio_ioport_reset(audio);
+ audio->stopped = 0;
+ break;
+ case AUDIO_FLUSH:
+ MM_DBG("AUDIO_FLUSH\n");
+ audio->rflush = 1;
+ audio->wflush = 1;
+ audio_ioport_reset(audio);
+ if (audio->running) {
+ audpp_flush(audio->dec_id);
+ rc = wait_event_interruptible(audio->write_wait,
+ !audio->wflush);
+ if (rc < 0) {
+ MM_ERR("AUDIO_FLUSH interrupted\n");
+ rc = -EINTR;
+ }
+ } else {
+ audio->rflush = 0;
+ audio->wflush = 0;
+ }
+ break;
+ case AUDIO_SET_CONFIG: {
+ struct msm_audio_config config;
+ if (copy_from_user(&config, (void *) arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (config.channel_count == 1) {
+ config.channel_count = AUDPP_CMD_PCM_INTF_MONO_V;
+ } else if (config.channel_count == 2) {
+ config.channel_count = AUDPP_CMD_PCM_INTF_STEREO_V;
+ } else {
+ rc = -EINVAL;
+ break;
+ }
+ audio->mfield = config.meta_field;
+ audio->out_sample_rate = config.sample_rate;
+ audio->out_channel_mode = config.channel_count;
+ rc = 0;
+ break;
+ }
+ case AUDIO_GET_CONFIG: {
+ struct msm_audio_config config;
+ config.buffer_size = (audio->out_dma_sz >> 1);
+ config.buffer_count = 2;
+ config.sample_rate = audio->out_sample_rate;
+ if (audio->out_channel_mode == AUDPP_CMD_PCM_INTF_MONO_V)
+ config.channel_count = 1;
+ else
+ config.channel_count = 2;
+ config.meta_field = 0;
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+ config.unused[2] = 0;
+ if (copy_to_user((void *) arg, &config, sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+
+ break;
+ }
+ case AUDIO_GET_WMA_CONFIG:{
+ if (copy_to_user((void *)arg, &audio->wma_config,
+ sizeof(audio->wma_config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ case AUDIO_SET_WMA_CONFIG:{
+ struct msm_audio_wma_config usr_config;
+
+ if (copy_from_user
+ (&usr_config, (void *)arg,
+ sizeof(usr_config))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ audio->wma_config = usr_config;
+ rc = 0;
+ break;
+ }
+ case AUDIO_GET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+ config.pcm_feedback = audio->pcm_feedback;
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+ config.buffer_size = PCM_BUFSZ_MIN;
+ if (copy_to_user((void *)arg, &config,
+ sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ case AUDIO_SET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+ if (copy_from_user
+ (&config, (void *)arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (config.pcm_feedback != audio->pcm_feedback) {
+ MM_ERR("Not sufficient permission to"
+ "change the playback mode\n");
+ rc = -EACCES;
+ break;
+ }
+ if ((config.buffer_count > PCM_BUF_MAX_COUNT) ||
+ (config.buffer_count == 1))
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+
+ if (config.buffer_size < PCM_BUFSZ_MIN)
+ config.buffer_size = PCM_BUFSZ_MIN;
+
+ /* Check if pcm feedback is required */
+ if ((config.pcm_feedback) && (!audio->read_data)) {
+ MM_DBG("allocate PCM buffer %d\n",
+ config.buffer_count *
+ config.buffer_size);
+ audio->read_phys = pmem_kalloc(
+ config.buffer_size *
+ config.buffer_count,
+ PMEM_MEMTYPE_EBI1|
+ PMEM_ALIGNMENT_4K);
+ if (IS_ERR((void *)audio->read_phys)) {
+ rc = -ENOMEM;
+ break;
+ }
+ audio->read_data = ioremap(audio->read_phys,
+ config.buffer_size *
+ config.buffer_count);
+ if (!audio->read_data) {
+ MM_ERR("read buf alloc fail\n");
+ rc = -ENOMEM;
+ pmem_kfree(audio->read_phys);
+ } else {
+ uint8_t index;
+ uint32_t offset = 0;
+ audio->buf_refresh = 0;
+ audio->pcm_buf_count =
+ config.buffer_count;
+ audio->read_next = 0;
+ audio->fill_next = 0;
+
+ for (index = 0;
+ index < config.buffer_count;
+ index++) {
+ audio->in[index].data =
+ audio->read_data + offset;
+ audio->in[index].addr =
+ audio->read_phys + offset;
+ audio->in[index].size =
+ config.buffer_size;
+ audio->in[index].used = 0;
+ offset += config.buffer_size;
+ }
+ MM_DBG("read buf: phy addr \
+ 0x%08x kernel addr 0x%08x\n",
+ audio->read_phys,
+ (int)audio->read_data);
+ rc = 0;
+ }
+ } else {
+ rc = 0;
+ }
+ break;
+ }
+ case AUDIO_PAUSE:
+ MM_DBG("AUDIO_PAUSE %ld\n", arg);
+ rc = audpp_pause(audio->dec_id, (int) arg);
+ break;
+ case AUDIO_GET_SESSION_ID:
+ if (copy_to_user((void *) arg, &audio->dec_id,
+ sizeof(unsigned short)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+/* Only useful in tunnel-mode */
+static int audio_fsync(struct file *file, int datasync)
+{
+ struct audio *audio = file->private_data;
+ struct buffer *frame;
+ int rc = 0;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+
+ if (!audio->running || audio->pcm_feedback) {
+ rc = -EINVAL;
+ goto done_nolock;
+ }
+
+ mutex_lock(&audio->write_lock);
+
+ rc = wait_event_interruptible(audio->write_wait,
+ (!audio->out[0].used &&
+ !audio->out[1].used &&
+ audio->out_needed) || audio->wflush);
+
+ if (rc < 0)
+ goto done;
+ else if (audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+
+ if (audio->reserved) {
+ MM_DBG("send reserved byte\n");
+ frame = audio->out + audio->out_tail;
+ ((char *) frame->data)[0] = audio->rsv_byte;
+ ((char *) frame->data)[1] = 0;
+ frame->used = 2;
+ audplay_send_data(audio, 0);
+
+ rc = wait_event_interruptible(audio->write_wait,
+ (!audio->out[0].used &&
+ !audio->out[1].used &&
+ audio->out_needed) || audio->wflush);
+
+ if (rc < 0)
+ goto done;
+ else if (audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+ }
+
+ /* pcm dmamiss message is sent continously
+ * when decoder is starved so no race
+ * condition concern
+ */
+ audio->teos = 0;
+
+ rc = wait_event_interruptible(audio->write_wait,
+ audio->teos || audio->wflush);
+
+ if (audio->wflush)
+ rc = -EBUSY;
+
+done:
+ mutex_unlock(&audio->write_lock);
+done_nolock:
+ return rc;
+}
+
+static ssize_t audio_read(struct file *file, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ int rc = 0;
+
+ if (!audio->pcm_feedback)
+ return 0; /* PCM feedback is not enabled. Nothing to read */
+
+ mutex_lock(&audio->read_lock);
+ MM_DBG("%d \n", count);
+ while (count > 0) {
+ rc = wait_event_interruptible(audio->read_wait,
+ (audio->in[audio->read_next].used > 0) ||
+ (audio->stopped) || (audio->rflush));
+
+ if (rc < 0)
+ break;
+
+ if (audio->stopped || audio->rflush) {
+ rc = -EBUSY;
+ break;
+ }
+
+ if (count < audio->in[audio->read_next].used) {
+ /* Read must happen in frame boundary. Since driver
+ does not know frame size, read count must be greater
+ or equal to size of PCM samples */
+ MM_DBG("audio_read: no partial frame done reading\n");
+ break;
+ } else {
+ MM_DBG("audio_read: read from in[%d]\n",
+ audio->read_next);
+ if (copy_to_user
+ (buf, audio->in[audio->read_next].data,
+ audio->in[audio->read_next].used)) {
+ MM_ERR("invalid addr %x \n", (unsigned int)buf);
+ rc = -EFAULT;
+ break;
+ }
+ count -= audio->in[audio->read_next].used;
+ buf += audio->in[audio->read_next].used;
+ audio->in[audio->read_next].used = 0;
+ if ((++audio->read_next) == audio->pcm_buf_count)
+ audio->read_next = 0;
+ break; /* Force to exit while loop
+ * to prevent output thread
+ * sleep too long if data is
+ * not ready at this moment.
+ */
+ }
+ }
+
+ /* don't feed output buffer to HW decoder during flushing
+ * buffer refresh command will be sent once flush completes
+ * send buf refresh command here can confuse HW decoder
+ */
+ if (audio->buf_refresh && !audio->rflush) {
+ audio->buf_refresh = 0;
+ MM_DBG("kick start pcm feedback again\n");
+ audplay_buffer_refresh(audio);
+ }
+
+ mutex_unlock(&audio->read_lock);
+
+ if (buf > start)
+ rc = buf - start;
+
+ MM_DBG("read %d bytes\n", rc);
+ return rc;
+}
+
+static int audwma_process_eos(struct audio *audio,
+ const char __user *buf_start, unsigned short mfield_size)
+{
+ int rc = 0;
+ struct buffer *frame;
+ char *buf_ptr;
+
+ if (audio->reserved) {
+ MM_DBG("flush reserve byte\n");
+ frame = audio->out + audio->out_head;
+ buf_ptr = frame->data;
+ rc = wait_event_interruptible(audio->write_wait,
+ (frame->used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+ if (rc < 0)
+ goto done;
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+
+ buf_ptr[0] = audio->rsv_byte;
+ buf_ptr[1] = 0;
+ audio->out_head ^= 1;
+ frame->mfield_sz = 0;
+ frame->used = 2;
+ audio->reserved = 0;
+ audplay_send_data(audio, 0);
+ }
+
+ frame = audio->out + audio->out_head;
+
+ rc = wait_event_interruptible(audio->write_wait,
+ (audio->out_needed &&
+ audio->out[0].used == 0 &&
+ audio->out[1].used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+
+ if (rc < 0)
+ goto done;
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+
+ if (copy_from_user(frame->data, buf_start, mfield_size)) {
+ rc = -EFAULT;
+ goto done;
+ }
+
+ frame->mfield_sz = mfield_size;
+ audio->out_head ^= 1;
+ frame->used = mfield_size;
+ audplay_send_data(audio, 0);
+done:
+ return rc;
+}
+
+static ssize_t audio_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ struct buffer *frame;
+ size_t xfer;
+ char *cpy_ptr;
+ int rc = 0, eos_condition = AUDWMA_EOS_NONE;
+ unsigned dsize;
+ unsigned short mfield_size = 0;
+
+ MM_DBG("cnt=%d\n", count);
+
+ mutex_lock(&audio->write_lock);
+ while (count > 0) {
+ frame = audio->out + audio->out_head;
+ cpy_ptr = frame->data;
+ dsize = 0;
+ rc = wait_event_interruptible(audio->write_wait,
+ (frame->used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+ if (rc < 0)
+ break;
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ break;
+ }
+ if (audio->mfield) {
+ if (buf == start) {
+ /* Processing beginning of user buffer */
+ if (__get_user(mfield_size,
+ (unsigned short __user *) buf)) {
+ rc = -EFAULT;
+ break;
+ } else if (mfield_size > count) {
+ rc = -EINVAL;
+ break;
+ }
+ MM_DBG("audio_write: mf offset_val %x\n",
+ mfield_size);
+ if (copy_from_user(cpy_ptr, buf, mfield_size)) {
+ rc = -EFAULT;
+ break;
+ }
+ /* Check if EOS flag is set and buffer has
+ * contains just meta field
+ */
+ if (cpy_ptr[AUDWMA_EOS_FLG_OFFSET] &
+ AUDWMA_EOS_FLG_MASK) {
+ MM_DBG("audio_write: EOS SET\n");
+ eos_condition = AUDWMA_EOS_SET;
+ if (mfield_size == count) {
+ buf += mfield_size;
+ break;
+ } else
+ cpy_ptr[AUDWMA_EOS_FLG_OFFSET]
+ &= ~AUDWMA_EOS_FLG_MASK;
+ }
+ cpy_ptr += mfield_size;
+ count -= mfield_size;
+ dsize += mfield_size;
+ buf += mfield_size;
+ } else {
+ mfield_size = 0;
+ MM_DBG("audio_write: continuous buffer\n");
+ }
+ frame->mfield_sz = mfield_size;
+ }
+
+ if (audio->reserved) {
+ MM_DBG("append reserved byte %x\n", audio->rsv_byte);
+ *cpy_ptr = audio->rsv_byte;
+ xfer = (count > ((frame->size - mfield_size) - 1)) ?
+ (frame->size - mfield_size) - 1 : count;
+ cpy_ptr++;
+ dsize += 1;
+ audio->reserved = 0;
+ } else
+ xfer = (count > (frame->size - mfield_size)) ?
+ (frame->size - mfield_size) : count;
+
+ if (copy_from_user(cpy_ptr, buf, xfer)) {
+ rc = -EFAULT;
+ break;
+ }
+
+ dsize += xfer;
+ if (dsize & 1) {
+ audio->rsv_byte = ((char *) frame->data)[dsize - 1];
+ MM_DBG("odd length buf reserve last byte %x\n",
+ audio->rsv_byte);
+ audio->reserved = 1;
+ dsize--;
+ }
+ count -= xfer;
+ buf += xfer;
+
+ if (dsize > 0) {
+ audio->out_head ^= 1;
+ frame->used = dsize;
+ audplay_send_data(audio, 0);
+ }
+ }
+ if (eos_condition == AUDWMA_EOS_SET)
+ rc = audwma_process_eos(audio, start, mfield_size);
+ mutex_unlock(&audio->write_lock);
+ if (!rc) {
+ if (buf > start)
+ return buf - start;
+ }
+ return rc;
+}
+
+static int audio_release(struct inode *inode, struct file *file)
+{
+ struct audio *audio = file->private_data;
+
+ MM_INFO("audio instance 0x%08x freeing\n", (int)audio);
+ mutex_lock(&audio->lock);
+ auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id);
+ audio_disable(audio);
+ audio_flush(audio);
+ audio_flush_pcm_buf(audio);
+ msm_adsp_put(audio->audplay);
+ audpp_adec_free(audio->dec_id);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&audio->suspend_ctl.node);
+#endif
+ audio->event_abort = 1;
+ wake_up(&audio->event_wait);
+ audwma_reset_event_queue(audio);
+ iounmap(audio->data);
+ pmem_kfree(audio->phys);
+ if (audio->read_data) {
+ iounmap(audio->read_data);
+ pmem_kfree(audio->read_phys);
+ }
+ mutex_unlock(&audio->lock);
+#ifdef CONFIG_DEBUG_FS
+ if (audio->dentry)
+ debugfs_remove(audio->dentry);
+#endif
+ kfree(audio);
+ return 0;
+}
+
+static void audwma_post_event(struct audio *audio, int type,
+ union msm_audio_event_payload payload)
+{
+ struct audwma_event *e_node = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+
+ if (!list_empty(&audio->free_event_queue)) {
+ e_node = list_first_entry(&audio->free_event_queue,
+ struct audwma_event, list);
+ list_del(&e_node->list);
+ } else {
+ e_node = kmalloc(sizeof(struct audwma_event), GFP_ATOMIC);
+ if (!e_node) {
+ MM_ERR("No mem to post event %d\n", type);
+ return;
+ }
+ }
+
+ e_node->event_type = type;
+ e_node->payload = payload;
+
+ list_add_tail(&e_node->list, &audio->event_queue);
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+ wake_up(&audio->event_wait);
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void audwma_suspend(struct early_suspend *h)
+{
+ struct audwma_suspend_ctl *ctl =
+ container_of(h, struct audwma_suspend_ctl, node);
+ union msm_audio_event_payload payload;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ audwma_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload);
+}
+
+static void audwma_resume(struct early_suspend *h)
+{
+ struct audwma_suspend_ctl *ctl =
+ container_of(h, struct audwma_suspend_ctl, node);
+ union msm_audio_event_payload payload;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ audwma_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload);
+}
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t audwma_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t audwma_debug_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ const int debug_bufmax = 4096;
+ static char buffer[4096];
+ int n = 0, i;
+ struct audio *audio = file->private_data;
+
+ mutex_lock(&audio->lock);
+ n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "enabled %d\n", audio->enabled);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "stopped %d\n", audio->stopped);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "pcm_feedback %d\n", audio->pcm_feedback);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_buf_sz %d\n", audio->out[0].size);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "pcm_buf_count %d \n", audio->pcm_buf_count);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "pcm_buf_sz %d \n", audio->in[0].size);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "volume %x \n", audio->vol_pan.volume);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "sample rate %d \n", audio->out_sample_rate);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "channel mode %d \n", audio->out_channel_mode);
+ mutex_unlock(&audio->lock);
+ /* Following variables are only useful for debugging when
+ * when playback halts unexpectedly. Thus, no mutual exclusion
+ * enforced
+ */
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "wflush %d\n", audio->wflush);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "rflush %d\n", audio->rflush);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "running %d \n", audio->running);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "dec state %d \n", audio->dec_state);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_needed %d \n", audio->out_needed);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_head %d \n", audio->out_head);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_tail %d \n", audio->out_tail);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out[0].used %d \n", audio->out[0].used);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out[1].used %d \n", audio->out[1].used);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "buffer_refresh %d \n", audio->buf_refresh);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "read_next %d \n", audio->read_next);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "fill_next %d \n", audio->fill_next);
+ for (i = 0; i < audio->pcm_buf_count; i++)
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "in[%d].size %d \n", i, audio->in[i].used);
+ buffer[n] = 0;
+ return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static const struct file_operations audwma_debug_fops = {
+ .read = audwma_debug_read,
+ .open = audwma_debug_open,
+};
+#endif
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct audio *audio = NULL;
+ int rc, dec_attrb, decid, i;
+ unsigned pmem_sz = DMASZ_MAX;
+ struct audwma_event *e_node = NULL;
+#ifdef CONFIG_DEBUG_FS
+ /* 4 bytes represents decoder number, 1 byte for terminate string */
+ char name[sizeof "msm_wma_" + 5];
+#endif
+
+ /* Allocate Mem for audio instance */
+ audio = kzalloc(sizeof(struct audio), GFP_KERNEL);
+ if (!audio) {
+ MM_ERR("no memory to allocate audio instance \n");
+ rc = -ENOMEM;
+ goto done;
+ }
+ MM_INFO("audio instance 0x%08x created\n", (int)audio);
+
+ /* Allocate the decoder */
+ dec_attrb = AUDDEC_DEC_WMA;
+ if ((file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ dec_attrb |= MSM_AUD_MODE_NONTUNNEL;
+ audio->pcm_feedback = NON_TUNNEL_MODE_PLAYBACK;
+ } else if ((file->f_mode & FMODE_WRITE) &&
+ !(file->f_mode & FMODE_READ)) {
+ dec_attrb |= MSM_AUD_MODE_TUNNEL;
+ audio->pcm_feedback = TUNNEL_MODE_PLAYBACK;
+ } else {
+ kfree(audio);
+ rc = -EACCES;
+ goto done;
+ }
+
+ decid = audpp_adec_alloc(dec_attrb, &audio->module_name,
+ &audio->queue_id);
+
+ if (decid < 0) {
+ MM_ERR("No free decoder available, freeing instance 0x%08x\n",
+ (int)audio);
+ rc = -ENODEV;
+ kfree(audio);
+ goto done;
+ }
+ audio->dec_id = decid & MSM_AUD_DECODER_MASK;
+
+ while (pmem_sz >= DMASZ_MIN) {
+ MM_DBG("pmemsz = %d\n", pmem_sz);
+ audio->phys = pmem_kalloc(pmem_sz, PMEM_MEMTYPE_EBI1|
+ PMEM_ALIGNMENT_4K);
+ if (!IS_ERR((void *)audio->phys)) {
+ audio->data = ioremap(audio->phys, pmem_sz);
+ if (!audio->data) {
+ MM_ERR("could not allocate write buffers, \
+ freeing instance 0x%08x\n",
+ (int)audio);
+ rc = -ENOMEM;
+ pmem_kfree(audio->phys);
+ audpp_adec_free(audio->dec_id);
+ kfree(audio);
+ goto done;
+ }
+ MM_DBG("write buf: phy addr 0x%08x kernel addr \
+ 0x%08x\n", audio->phys, (int)audio->data);
+ break;
+ } else if (pmem_sz == DMASZ_MIN) {
+ MM_ERR("could not allocate write buffers, freeing \
+ instance 0x%08x\n", (int)audio);
+ rc = -ENOMEM;
+ audpp_adec_free(audio->dec_id);
+ kfree(audio);
+ goto done;
+ } else
+ pmem_sz >>= 1;
+ }
+ audio->out_dma_sz = pmem_sz;
+
+ rc = msm_adsp_get(audio->module_name, &audio->audplay,
+ &audplay_adsp_ops_wma, audio);
+ if (rc) {
+ MM_ERR("failed to get %s module, freeing instance 0x%08x\n",
+ audio->module_name, (int)audio);
+ goto err;
+ }
+
+ mutex_init(&audio->lock);
+ mutex_init(&audio->write_lock);
+ mutex_init(&audio->read_lock);
+ mutex_init(&audio->get_event_lock);
+ spin_lock_init(&audio->dsp_lock);
+ init_waitqueue_head(&audio->write_wait);
+ init_waitqueue_head(&audio->read_wait);
+ INIT_LIST_HEAD(&audio->free_event_queue);
+ INIT_LIST_HEAD(&audio->event_queue);
+ init_waitqueue_head(&audio->wait);
+ init_waitqueue_head(&audio->event_wait);
+ spin_lock_init(&audio->event_queue_lock);
+ init_waitqueue_head(&audio->avsync_wait);
+
+ audio->out[0].data = audio->data + 0;
+ audio->out[0].addr = audio->phys + 0;
+ audio->out[0].size = audio->out_dma_sz >> 1;
+
+ audio->out[1].data = audio->data + audio->out[0].size;
+ audio->out[1].addr = audio->phys + audio->out[0].size;
+ audio->out[1].size = audio->out[0].size;
+
+ audio->wma_config.armdatareqthr = 1262;
+ audio->wma_config.channelsdecoded = 2;
+ audio->wma_config.wmabytespersec = 6003;
+ audio->wma_config.wmasamplingfreq = 44100;
+ audio->wma_config.wmaencoderopts = 31;
+
+ audio->out_sample_rate = 44100;
+ audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V;
+
+ audio->vol_pan.volume = 0x2000;
+
+ audio_flush(audio);
+
+ file->private_data = audio;
+ audio->opened = 1;
+
+ audio->device_events = AUDDEV_EVT_DEV_RDY
+ |AUDDEV_EVT_DEV_RLS|
+ AUDDEV_EVT_STREAM_VOL_CHG;
+
+ rc = auddev_register_evt_listner(audio->device_events,
+ AUDDEV_CLNT_DEC,
+ audio->dec_id,
+ wma_listner,
+ (void *)audio);
+ if (rc) {
+ MM_ERR("%s: failed to register listner\n", __func__);
+ goto event_err;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ snprintf(name, sizeof name, "msm_wma_%04x", audio->dec_id);
+ audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *) audio,
+ &audwma_debug_fops);
+
+ if (IS_ERR(audio->dentry))
+ MM_DBG("debugfs_create_file failed\n");
+#endif
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+ audio->suspend_ctl.node.resume = audwma_resume;
+ audio->suspend_ctl.node.suspend = audwma_suspend;
+ audio->suspend_ctl.audio = audio;
+ register_early_suspend(&audio->suspend_ctl.node);
+#endif
+ for (i = 0; i < AUDWMA_EVENT_NUM; i++) {
+ e_node = kmalloc(sizeof(struct audwma_event), GFP_KERNEL);
+ if (e_node)
+ list_add_tail(&e_node->list, &audio->free_event_queue);
+ else {
+ MM_ERR("event pkt alloc failed\n");
+ break;
+ }
+ }
+done:
+ return rc;
+event_err:
+ msm_adsp_put(audio->audplay);
+err:
+ iounmap(audio->data);
+ pmem_kfree(audio->phys);
+ audpp_adec_free(audio->dec_id);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_wma_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_release,
+ .read = audio_read,
+ .write = audio_write,
+ .unlocked_ioctl = audio_ioctl,
+ .fsync = audio_fsync,
+};
+
+struct miscdevice audio_wma_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_wma",
+ .fops = &audio_wma_fops,
+};
+
+static int __init audio_init(void)
+{
+ return misc_register(&audio_wma_misc);
+}
+
+device_initcall(audio_init);
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_wmapro.c b/arch/arm/mach-msm/qdsp5v2/audio_wmapro.c
new file mode 100644
index 0000000..e9837be
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_wmapro.c
@@ -0,0 +1,1801 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Based on the mp3 native driver in arch/arm/mach-msm/qdsp5v2/audio_mp3.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ *
+ * All source code in this file is licensed under the following license except
+ * where indicated.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/earlysuspend.h>
+#include <linux/android_pmem.h>
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+
+#include <linux/msm_audio.h>
+#include <linux/slab.h>
+#include <linux/msm_audio_wmapro.h>
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/qdsp5audplaycmdi.h>
+#include <mach/qdsp5v2/qdsp5audplaymsg.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/qdsp5v2/audpp.h>
+#include <mach/debug_mm.h>
+
+/* Size must be power of 2 */
+#define BUFSZ_MAX 4110 /* Includes meta in size */
+#define BUFSZ_MIN 2062 /* Includes meta in size */
+#define DMASZ_MAX (BUFSZ_MAX * 2)
+#define DMASZ_MIN (BUFSZ_MIN * 2)
+
+#define AUDPLAY_INVALID_READ_PTR_OFFSET 0xFFFF
+#define AUDDEC_DEC_WMAPRO 13
+
+#define PCM_BUFSZ_MIN 8216 /* Hold one stereo WMAPRO frame and meta out*/
+#define PCM_BUF_MAX_COUNT 5 /* DSP only accepts 5 buffers at most
+ but support 2 buffers currently */
+#define ROUTING_MODE_FTRT 1
+#define ROUTING_MODE_RT 2
+/* Decoder status received from AUDPPTASK */
+#define AUDPP_DEC_STATUS_SLEEP 0
+#define AUDPP_DEC_STATUS_INIT 1
+#define AUDPP_DEC_STATUS_CFG 2
+#define AUDPP_DEC_STATUS_PLAY 3
+
+#define AUDWMAPRO_METAFIELD_MASK 0xFFFF0000
+#define AUDWMAPRO_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */
+#define AUDWMAPRO_EOS_FLG_MASK 0x01
+#define AUDWMAPRO_EOS_NONE 0x0 /* No EOS detected */
+#define AUDWMAPRO_EOS_SET 0x1 /* EOS set in meta field */
+
+#define AUDWMAPRO_EVENT_NUM 10 /* Default no. of pre-allocated event packets */
+
+struct buffer {
+ void *data;
+ unsigned size;
+ unsigned used; /* Input usage actual DSP produced PCM size */
+ unsigned addr;
+ unsigned short mfield_sz; /*only useful for data has meta field */
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+struct audwmapro_suspend_ctl {
+ struct early_suspend node;
+ struct audio *audio;
+};
+#endif
+
+struct audwmapro_event{
+ struct list_head list;
+ int event_type;
+ union msm_audio_event_payload payload;
+};
+
+struct audio {
+ struct buffer out[2];
+
+ spinlock_t dsp_lock;
+
+ uint8_t out_head;
+ uint8_t out_tail;
+ uint8_t out_needed; /* number of buffers the dsp is waiting for */
+ unsigned out_dma_sz;
+
+ atomic_t out_bytes;
+
+ struct mutex lock;
+ struct mutex write_lock;
+ wait_queue_head_t write_wait;
+
+ /* Host PCM section */
+ struct buffer in[PCM_BUF_MAX_COUNT];
+ struct mutex read_lock;
+ wait_queue_head_t read_wait; /* Wait queue for read */
+ char *read_data; /* pointer to reader buffer */
+ int32_t read_phys; /* physical address of reader buffer */
+ uint8_t read_next; /* index to input buffers to be read next */
+ uint8_t fill_next; /* index to buffer that DSP should be filling */
+ uint8_t pcm_buf_count; /* number of pcm buffer allocated */
+ /* ---- End of Host PCM section */
+
+ struct msm_adsp_module *audplay;
+
+ /* configuration to use on next enable */
+ uint32_t out_sample_rate;
+ uint32_t out_channel_mode;
+
+ struct msm_audio_wmapro_config wmapro_config;
+
+ /* data allocated for various buffers */
+ char *data;
+ int32_t phys; /* physical address of write buffer */
+
+ int mfield; /* meta field embedded in data */
+ int rflush; /* Read flush */
+ int wflush; /* Write flush */
+ int opened;
+ int enabled;
+ int running;
+ int stopped; /* set when stopped, cleared on flush */
+ int pcm_feedback;
+ int buf_refresh;
+ int teos; /* valid only if tunnel mode & no data left for decoder */
+ enum msm_aud_decoder_state dec_state; /* Represents decoder state */
+ int reserved; /* A byte is being reserved */
+ char rsv_byte; /* Handle odd length user data */
+
+ const char *module_name;
+ unsigned queue_id;
+ uint16_t dec_id;
+ uint32_t read_ptr_offset;
+ int16_t source;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct audwmapro_suspend_ctl suspend_ctl;
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dentry;
+#endif
+
+ wait_queue_head_t wait;
+ struct list_head free_event_queue;
+ struct list_head event_queue;
+ wait_queue_head_t event_wait;
+ spinlock_t event_queue_lock;
+ struct mutex get_event_lock;
+ int event_abort;
+ /* AV sync Info */
+ int avsync_flag; /* Flag to indicate feedback from DSP */
+ wait_queue_head_t avsync_wait;/* Wait queue for AV Sync Message */
+ /* flags, 48 bits sample/bytes counter per channel */
+ uint16_t avsync[AUDPP_AVSYNC_CH_COUNT * AUDPP_AVSYNC_NUM_WORDS + 1];
+
+ uint32_t device_events;
+
+ int eq_enable;
+ int eq_needs_commit;
+ struct audpp_cmd_cfg_object_params_eqalizer eq;
+ struct audpp_cmd_cfg_object_params_volume vol_pan;
+};
+
+static int auddec_dsp_config(struct audio *audio, int enable);
+static void audpp_cmd_cfg_adec_params(struct audio *audio);
+static void audpp_cmd_cfg_routing_mode(struct audio *audio);
+static void audplay_send_data(struct audio *audio, unsigned needed);
+static void audplay_config_hostpcm(struct audio *audio);
+static void audplay_buffer_refresh(struct audio *audio);
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg);
+static void audwmapro_post_event(struct audio *audio, int type,
+ union msm_audio_event_payload payload);
+
+/* must be called with audio->lock held */
+static int audio_enable(struct audio *audio)
+{
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ if (audio->enabled)
+ return 0;
+
+ audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+ audio->out_tail = 0;
+ audio->out_needed = 0;
+
+ if (msm_adsp_enable(audio->audplay)) {
+ MM_ERR("msm_adsp_enable(audplay) failed\n");
+ return -ENODEV;
+ }
+
+ if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) {
+ MM_ERR("audpp_enable() failed\n");
+ msm_adsp_disable(audio->audplay);
+ return -ENODEV;
+ }
+
+ audio->enabled = 1;
+ return 0;
+}
+
+static void wmapro_listner(u32 evt_id, union auddev_evt_data *evt_payload,
+ void *private_data)
+{
+ struct audio *audio = (struct audio *) private_data;
+ switch (evt_id) {
+ case AUDDEV_EVT_DEV_RDY:
+ MM_DBG(":AUDDEV_EVT_DEV_RDY\n");
+ audio->source |= (0x1 << evt_payload->routing_id);
+ if (audio->running == 1 && audio->enabled == 1)
+ audpp_route_stream(audio->dec_id, audio->source);
+ break;
+ case AUDDEV_EVT_DEV_RLS:
+ MM_DBG(":AUDDEV_EVT_DEV_RLS\n");
+ audio->source &= ~(0x1 << evt_payload->routing_id);
+ if (audio->running == 1 && audio->enabled == 1)
+ audpp_route_stream(audio->dec_id, audio->source);
+ break;
+ case AUDDEV_EVT_STREAM_VOL_CHG:
+ audio->vol_pan.volume = evt_payload->session_vol;
+ MM_DBG(":AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n",
+ audio->vol_pan.volume);
+ if (audio->running)
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ break;
+ default:
+ MM_ERR(":ERROR:wrong event\n");
+ break;
+ }
+}
+
+/* must be called with audio->lock held */
+static int audio_disable(struct audio *audio)
+{
+ int rc = 0;
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ if (audio->enabled) {
+ audio->enabled = 0;
+ audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+ auddec_dsp_config(audio, 0);
+ rc = wait_event_interruptible_timeout(audio->wait,
+ audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+ msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE)
+ rc = -EFAULT;
+ else
+ rc = 0;
+ wake_up(&audio->write_wait);
+ wake_up(&audio->read_wait);
+ msm_adsp_disable(audio->audplay);
+ audpp_disable(audio->dec_id, audio);
+ audio->out_needed = 0;
+ }
+ return rc;
+}
+
+/* ------------------- dsp --------------------- */
+static void audio_update_pcm_buf_entry(struct audio *audio,
+ uint32_t *payload)
+{
+ uint8_t index;
+ unsigned long flags;
+
+ if (audio->rflush)
+ return;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ for (index = 0; index < payload[1]; index++) {
+ if (audio->in[audio->fill_next].addr ==
+ payload[2 + index * 2]) {
+ MM_DBG("audio_update_pcm_buf_entry: \
+ in[%d] ready\n", audio->fill_next);
+ audio->in[audio->fill_next].used =
+ payload[3 + index * 2];
+ if ((++audio->fill_next) == audio->pcm_buf_count)
+ audio->fill_next = 0;
+ } else {
+ MM_ERR("audio_update_pcm_buf_entry: \
+ expected=%x ret=%x\n",
+ audio->in[audio->fill_next].addr,
+ payload[1 + index * 2]);
+ break;
+ }
+ }
+ if (audio->in[audio->fill_next].used == 0) {
+ audplay_buffer_refresh(audio);
+ } else {
+ MM_DBG("read cannot keep up\n");
+ audio->buf_refresh = 1;
+ }
+ wake_up(&audio->read_wait);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+static void audplay_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent) (void *ptr, size_t len))
+{
+ struct audio *audio = data;
+ uint32_t msg[28];
+
+ getevent(msg, sizeof(msg));
+
+ MM_DBG("msg_id=%x\n", id);
+
+ switch (id) {
+ case AUDPLAY_MSG_DEC_NEEDS_DATA:
+ audplay_send_data(audio, 1);
+ break;
+
+ case AUDPLAY_MSG_BUFFER_UPDATE:
+ audio_update_pcm_buf_entry(audio, msg);
+ break;
+
+ case ADSP_MESSAGE_ID:
+ MM_DBG("Received ADSP event: module enable(audplaytask)\n");
+ break;
+
+ default:
+ MM_ERR("unexpected message from decoder \n");
+ break;
+ }
+}
+
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg)
+{
+ struct audio *audio = private;
+
+ switch (id) {
+ case AUDPP_MSG_STATUS_MSG:{
+ unsigned status = msg[1];
+
+ switch (status) {
+ case AUDPP_DEC_STATUS_SLEEP: {
+ uint16_t reason = msg[2];
+ MM_DBG("decoder status:sleep reason = \
+ 0x%04x\n", reason);
+ if ((reason == AUDPP_MSG_REASON_MEM)
+ || (reason ==
+ AUDPP_MSG_REASON_NODECODER)) {
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_FAILURE;
+ wake_up(&audio->wait);
+ } else if (reason == AUDPP_MSG_REASON_NONE) {
+ /* decoder is in disable state */
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_CLOSE;
+ wake_up(&audio->wait);
+ }
+ break;
+ }
+ case AUDPP_DEC_STATUS_INIT:
+ MM_DBG("decoder status: init\n");
+ if (audio->pcm_feedback)
+ audpp_cmd_cfg_routing_mode(audio);
+ else
+ audpp_cmd_cfg_adec_params(audio);
+ break;
+
+ case AUDPP_DEC_STATUS_CFG:
+ MM_DBG("decoder status: cfg\n");
+ break;
+ case AUDPP_DEC_STATUS_PLAY:
+ MM_DBG("decoder status: play \n");
+ audpp_route_stream(audio->dec_id,
+ audio->source);
+ if (audio->pcm_feedback) {
+ audplay_config_hostpcm(audio);
+ audplay_buffer_refresh(audio);
+ }
+ audio->dec_state =
+ MSM_AUD_DECODER_STATE_SUCCESS;
+ wake_up(&audio->wait);
+ break;
+ default:
+ MM_ERR("unknown decoder status\n");
+ }
+ break;
+ }
+ case AUDPP_MSG_CFG_MSG:
+ if (msg[0] == AUDPP_MSG_ENA_ENA) {
+ MM_DBG("CFG_MSG ENABLE\n");
+ auddec_dsp_config(audio, 1);
+ audio->out_needed = 0;
+ audio->running = 1;
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ audpp_dsp_set_eq(audio->dec_id, audio->eq_enable,
+ &audio->eq, POPP);
+ } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+ MM_DBG("CFG_MSG DISABLE\n");
+ audio->running = 0;
+ } else {
+ MM_DBG("CFG_MSG %d?\n", msg[0]);
+ }
+ break;
+ case AUDPP_MSG_ROUTING_ACK:
+ MM_DBG("ROUTING_ACK mode=%d\n", msg[1]);
+ audpp_cmd_cfg_adec_params(audio);
+ break;
+
+ case AUDPP_MSG_FLUSH_ACK:
+ MM_DBG("FLUSH_ACK\n");
+ audio->wflush = 0;
+ audio->rflush = 0;
+ wake_up(&audio->write_wait);
+ if (audio->pcm_feedback)
+ audplay_buffer_refresh(audio);
+ break;
+
+ case AUDPP_MSG_PCMDMAMISSED:
+ MM_DBG("PCMDMAMISSED\n");
+ audio->teos = 1;
+ wake_up(&audio->write_wait);
+ break;
+
+ case AUDPP_MSG_AVSYNC_MSG:
+ MM_DBG("AUDPP_MSG_AVSYNC_MSG\n");
+ memcpy(&audio->avsync[0], msg, sizeof(audio->avsync));
+ audio->avsync_flag = 1;
+ wake_up(&audio->avsync_wait);
+ break;
+
+ default:
+ MM_ERR("UNKNOWN (%d)\n", id);
+ }
+
+}
+
+static struct msm_adsp_ops audplay_adsp_ops_wmapro = {
+ .event = audplay_dsp_event,
+};
+
+#define audplay_send_queue0(audio, cmd, len) \
+ msm_adsp_write(audio->audplay, audio->queue_id, \
+ cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+ struct audpp_cmd_cfg_dec_type cfg_dec_cmd;
+
+ memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd));
+
+ cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+ if (enable)
+ cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_WMAPRO;
+ else
+ cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_DIS_DEC_V;
+ cfg_dec_cmd.dm_mode = 0x0;
+ cfg_dec_cmd.stream_id = audio->dec_id;
+ return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd));
+}
+
+static void audpp_cmd_cfg_adec_params(struct audio *audio)
+{
+ struct audpp_cmd_cfg_adec_params_wmapro cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+ cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_WMAPRO_LEN;
+ cmd.common.dec_id = audio->dec_id;
+ cmd.common.input_sampling_frequency = audio->out_sample_rate;
+
+ /*
+ * Test done for sample with the following configuration
+ * armdatareqthr = 1262
+ * channelsdecoded = 1(MONO)/2(STEREO)
+ * wmaprobytespersec = Tested with 6003 Bytes per sec
+ * wmaprosamplingfreq = 44100
+ * wmaproencoderopts = 31
+ */
+
+ cmd.armdatareqthr = audio->wmapro_config.armdatareqthr;
+ cmd.numchannels = audio->wmapro_config.numchannels;
+ cmd.validbitspersample = audio->wmapro_config.validbitspersample;
+ cmd.formattag = audio->wmapro_config.formattag;
+ cmd.samplingrate = audio->wmapro_config.samplingrate;
+ cmd.avgbytespersecond = audio->wmapro_config.avgbytespersecond;
+ cmd.asfpacketlength = audio->wmapro_config.asfpacketlength;
+ cmd.channelmask = audio->wmapro_config.channelmask;
+ cmd.encodeopt = audio->wmapro_config.encodeopt;
+ cmd.advancedencodeopt = audio->wmapro_config.advancedencodeopt;
+ cmd.advancedencodeopt2 = audio->wmapro_config.advancedencodeopt2;
+
+ audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_routing_mode(struct audio *audio)
+{
+ struct audpp_cmd_routing_mode cmd;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_ROUTING_MODE;
+ cmd.object_number = audio->dec_id;
+ if (audio->pcm_feedback)
+ cmd.routing_mode = ROUTING_MODE_FTRT;
+ else
+ cmd.routing_mode = ROUTING_MODE_RT;
+
+ audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static void audplay_buffer_refresh(struct audio *audio)
+{
+ struct audplay_cmd_buffer_refresh refresh_cmd;
+
+ refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH;
+ refresh_cmd.num_buffers = 1;
+ refresh_cmd.buf0_address = audio->in[audio->fill_next].addr;
+ refresh_cmd.buf0_length = audio->in[audio->fill_next].size;
+ refresh_cmd.buf_read_count = 0;
+
+ MM_DBG("buf0_addr=%x buf0_len=%d\n",
+ refresh_cmd.buf0_address,
+ refresh_cmd.buf0_length);
+
+ (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd));
+}
+
+static void audplay_config_hostpcm(struct audio *audio)
+{
+ struct audplay_cmd_hpcm_buf_cfg cfg_cmd;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG;
+ cfg_cmd.max_buffers = audio->pcm_buf_count;
+ cfg_cmd.byte_swap = 0;
+ cfg_cmd.hostpcm_config = (0x8000) | (0x4000);
+ cfg_cmd.feedback_frequency = 1;
+ cfg_cmd.partition_number = 0;
+
+ (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd));
+}
+
+
+static int audplay_dsp_send_data_avail(struct audio *audio,
+ unsigned idx, unsigned len)
+{
+ struct audplay_cmd_bitstream_data_avail_nt2 cmd;
+
+ cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2;
+ if (audio->mfield)
+ cmd.decoder_id = AUDWMAPRO_METAFIELD_MASK |
+ (audio->out[idx].mfield_sz >> 1);
+ else
+ cmd.decoder_id = audio->dec_id;
+ cmd.buf_ptr = audio->out[idx].addr;
+ cmd.buf_size = len/2;
+ cmd.partition_number = 0;
+ return audplay_send_queue0(audio, &cmd, sizeof(cmd));
+}
+
+static void audplay_send_data(struct audio *audio, unsigned needed)
+{
+ struct buffer *frame;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (!audio->running)
+ goto done;
+
+ if (audio->wflush) {
+ audio->out_needed = 1;
+ goto done;
+ }
+
+ if (needed && !audio->wflush) {
+ /* We were called from the callback because the DSP
+ * requested more data. Note that the DSP does want
+ * more data, and if a buffer was in-flight, mark it
+ * as available (since the DSP must now be done with
+ * it).
+ */
+ audio->out_needed = 1;
+ frame = audio->out + audio->out_tail;
+ if (frame->used == 0xffffffff) {
+ MM_DBG("frame %d free\n", audio->out_tail);
+ frame->used = 0;
+ audio->out_tail ^= 1;
+ wake_up(&audio->write_wait);
+ }
+ }
+
+ if (audio->out_needed) {
+ /* If the DSP currently wants data and we have a
+ * buffer available, we will send it and reset
+ * the needed flag. We'll mark the buffer as in-flight
+ * so that it won't be recycled until the next buffer
+ * is requested
+ */
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ frame = audio->out + audio->out_tail;
+ if (frame->used) {
+ BUG_ON(frame->used == 0xffffffff);
+ MM_DBG("frame %d busy\n", audio->out_tail);
+ audplay_dsp_send_data_avail(audio, audio->out_tail,
+ frame->used);
+ frame->used = 0xffffffff;
+ audio->out_needed = 0;
+ }
+ }
+done:
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+/* ------------------- device --------------------- */
+
+static void audio_flush(struct audio *audio)
+{
+ audio->out[0].used = 0;
+ audio->out[1].used = 0;
+ audio->out_head = 0;
+ audio->out_tail = 0;
+ audio->reserved = 0;
+ atomic_set(&audio->out_bytes, 0);
+}
+
+static void audio_flush_pcm_buf(struct audio *audio)
+{
+ uint8_t index;
+
+ for (index = 0; index < PCM_BUF_MAX_COUNT; index++)
+ audio->in[index].used = 0;
+ audio->buf_refresh = 0;
+ audio->read_next = 0;
+ audio->fill_next = 0;
+}
+
+static void audio_ioport_reset(struct audio *audio)
+{
+ /* Make sure read/write thread are free from
+ * sleep and knowing that system is not able
+ * to process io request at the moment
+ */
+ wake_up(&audio->write_wait);
+ mutex_lock(&audio->write_lock);
+ audio_flush(audio);
+ mutex_unlock(&audio->write_lock);
+ wake_up(&audio->read_wait);
+ mutex_lock(&audio->read_lock);
+ audio_flush_pcm_buf(audio);
+ mutex_unlock(&audio->read_lock);
+ audio->avsync_flag = 1;
+ wake_up(&audio->avsync_wait);
+}
+
+static int audwmapro_events_pending(struct audio *audio)
+{
+ unsigned long flags;
+ int empty;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ empty = !list_empty(&audio->event_queue);
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+ return empty || audio->event_abort;
+}
+
+static void audwmapro_reset_event_queue(struct audio *audio)
+{
+ unsigned long flags;
+ struct audwmapro_event *drv_evt;
+ struct list_head *ptr, *next;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ list_for_each_safe(ptr, next, &audio->event_queue) {
+ drv_evt = list_first_entry(&audio->event_queue,
+ struct audwmapro_event, list);
+ list_del(&drv_evt->list);
+ kfree(drv_evt);
+ }
+ list_for_each_safe(ptr, next, &audio->free_event_queue) {
+ drv_evt = list_first_entry(&audio->free_event_queue,
+ struct audwmapro_event, list);
+ list_del(&drv_evt->list);
+ kfree(drv_evt);
+ }
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+ return;
+}
+
+static long audwmapro_process_event_req(struct audio *audio, void __user *arg)
+{
+ long rc;
+ struct msm_audio_event usr_evt;
+ struct audwmapro_event *drv_evt = NULL;
+ int timeout;
+ unsigned long flags;
+
+ if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event)))
+ return -EFAULT;
+
+ timeout = (int) usr_evt.timeout_ms;
+
+ if (timeout > 0) {
+ rc = wait_event_interruptible_timeout(audio->event_wait,
+ audwmapro_events_pending(audio),
+ msecs_to_jiffies(timeout));
+ if (rc == 0)
+ return -ETIMEDOUT;
+ } else {
+ rc = wait_event_interruptible(
+ audio->event_wait, audwmapro_events_pending(audio));
+ }
+
+ if (rc < 0)
+ return rc;
+
+ if (audio->event_abort) {
+ audio->event_abort = 0;
+ return -ENODEV;
+ }
+
+ rc = 0;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ if (!list_empty(&audio->event_queue)) {
+ drv_evt = list_first_entry(&audio->event_queue,
+ struct audwmapro_event, list);
+ list_del(&drv_evt->list);
+ }
+
+ if (drv_evt) {
+ usr_evt.event_type = drv_evt->event_type;
+ usr_evt.event_payload = drv_evt->payload;
+ list_add_tail(&drv_evt->list, &audio->free_event_queue);
+ } else
+ rc = -1;
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+ if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt)))
+ rc = -EFAULT;
+
+ return rc;
+}
+
+static int audio_enable_eq(struct audio *audio, int enable)
+{
+ if (audio->eq_enable == enable && !audio->eq_needs_commit)
+ return 0;
+
+ audio->eq_enable = enable;
+
+ if (audio->running) {
+ audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq, POPP);
+ audio->eq_needs_commit = 0;
+ }
+ return 0;
+}
+
+static int audio_get_avsync_data(struct audio *audio,
+ struct msm_audio_stats *stats)
+{
+ int rc = -EINVAL;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) {
+ /* av_sync sample count */
+ stats->sample_count = (audio->avsync[2] << 16) |
+ (audio->avsync[3]);
+
+ /* av_sync byte_count */
+ stats->byte_count = (audio->avsync[5] << 16) |
+ (audio->avsync[6]);
+
+ audio->avsync_flag = 0;
+ rc = 0;
+ }
+ local_irq_restore(flags);
+ return rc;
+
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct audio *audio = file->private_data;
+ int rc = -EINVAL;
+ unsigned long flags = 0;
+ uint16_t enable_mask;
+ int enable;
+ int prev_state;
+
+ MM_DBG("cmd = %d\n", cmd);
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+
+ audio->avsync_flag = 0;
+ memset(&stats, 0, sizeof(stats));
+ if (audpp_query_avsync(audio->dec_id) < 0)
+ return rc;
+
+ rc = wait_event_interruptible_timeout(audio->avsync_wait,
+ (audio->avsync_flag == 1),
+ msecs_to_jiffies(AUDPP_AVSYNC_EVENT_TIMEOUT));
+
+ if (rc < 0)
+ return rc;
+ else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) {
+ if (audio_get_avsync_data(audio, &stats) < 0)
+ return rc;
+
+ if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ } else
+ return -EAGAIN;
+ }
+
+ switch (cmd) {
+ case AUDIO_ENABLE_AUDPP:
+ if (copy_from_user(&enable_mask, (void *) arg,
+ sizeof(enable_mask))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ enable = (enable_mask & EQ_ENABLE) ? 1 : 0;
+ audio_enable_eq(audio, enable);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ rc = 0;
+ break;
+ case AUDIO_SET_VOLUME:
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->vol_pan.volume = arg;
+ if (audio->running)
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ rc = 0;
+ break;
+
+ case AUDIO_SET_PAN:
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->vol_pan.pan = arg;
+ if (audio->running)
+ audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+ POPP);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ rc = 0;
+ break;
+
+ case AUDIO_SET_EQ:
+ prev_state = audio->eq_enable;
+ audio->eq_enable = 0;
+ if (copy_from_user(&audio->eq.num_bands, (void *) arg,
+ sizeof(audio->eq) -
+ (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) {
+ rc = -EFAULT;
+ break;
+ }
+ audio->eq_enable = prev_state;
+ audio->eq_needs_commit = 1;
+ rc = 0;
+ break;
+ }
+
+ if (-EINVAL != rc)
+ return rc;
+
+ if (cmd == AUDIO_GET_EVENT) {
+ MM_DBG("AUDIO_GET_EVENT\n");
+ if (mutex_trylock(&audio->get_event_lock)) {
+ rc = audwmapro_process_event_req(audio,
+ (void __user *) arg);
+ mutex_unlock(&audio->get_event_lock);
+ } else
+ rc = -EBUSY;
+ return rc;
+ }
+
+ if (cmd == AUDIO_ABORT_GET_EVENT) {
+ audio->event_abort = 1;
+ wake_up(&audio->event_wait);
+ return 0;
+ }
+
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START:
+ MM_DBG("AUDIO_START\n");
+ rc = audio_enable(audio);
+ if (!rc) {
+ rc = wait_event_interruptible_timeout(audio->wait,
+ audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+ msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+ MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc);
+
+ if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS)
+ rc = -ENODEV;
+ else
+ rc = 0;
+ }
+ break;
+ case AUDIO_STOP:
+ MM_DBG("AUDIO_STOP\n");
+ rc = audio_disable(audio);
+ audio->stopped = 1;
+ audio_ioport_reset(audio);
+ audio->stopped = 0;
+ break;
+ case AUDIO_FLUSH:
+ MM_DBG("AUDIO_FLUSH\n");
+ audio->rflush = 1;
+ audio->wflush = 1;
+ audio_ioport_reset(audio);
+ if (audio->running) {
+ audpp_flush(audio->dec_id);
+ rc = wait_event_interruptible(audio->write_wait,
+ !audio->wflush);
+ if (rc < 0) {
+ MM_ERR("AUDIO_FLUSH interrupted\n");
+ rc = -EINTR;
+ }
+ } else {
+ audio->rflush = 0;
+ audio->wflush = 0;
+ }
+ break;
+ case AUDIO_SET_CONFIG: {
+ struct msm_audio_config config;
+ if (copy_from_user(&config, (void *) arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (config.channel_count == 1) {
+ config.channel_count = AUDPP_CMD_PCM_INTF_MONO_V;
+ } else if (config.channel_count == 2) {
+ config.channel_count = AUDPP_CMD_PCM_INTF_STEREO_V;
+ } else {
+ rc = -EINVAL;
+ break;
+ }
+ audio->mfield = config.meta_field;
+ audio->out_sample_rate = config.sample_rate;
+ audio->out_channel_mode = config.channel_count;
+ rc = 0;
+ break;
+ }
+ case AUDIO_GET_CONFIG: {
+ struct msm_audio_config config;
+ config.buffer_size = (audio->out_dma_sz >> 1);
+ config.buffer_count = 2;
+ config.sample_rate = audio->out_sample_rate;
+ if (audio->out_channel_mode == AUDPP_CMD_PCM_INTF_MONO_V)
+ config.channel_count = 1;
+ else
+ config.channel_count = 2;
+ config.meta_field = 0;
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+ config.unused[2] = 0;
+ if (copy_to_user((void *) arg, &config, sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+
+ break;
+ }
+ case AUDIO_GET_WMAPRO_CONFIG:{
+ if (copy_to_user((void *)arg, &audio->wmapro_config,
+ sizeof(audio->wmapro_config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ case AUDIO_SET_WMAPRO_CONFIG:{
+ struct msm_audio_wmapro_config usr_config;
+
+ if (copy_from_user
+ (&usr_config, (void *)arg,
+ sizeof(usr_config))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ audio->wmapro_config = usr_config;
+
+ /* Need to swap the first and last words of advancedencodeopt2
+ * as DSP cannot read 32-bit variable at a time. Need to be
+ * split into two 16-bit and swap them as required by DSP */
+
+ audio->wmapro_config.advancedencodeopt2 =
+ ((audio->wmapro_config.advancedencodeopt2 & 0xFFFF0000)
+ >> 16) | ((audio->wmapro_config.advancedencodeopt2
+ << 16) & 0xFFFF0000);
+ rc = 0;
+ break;
+ }
+ case AUDIO_GET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+ config.pcm_feedback = audio->pcm_feedback;
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+ config.buffer_size = PCM_BUFSZ_MIN;
+ if (copy_to_user((void *)arg, &config,
+ sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ case AUDIO_SET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+ if (copy_from_user
+ (&config, (void *)arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (config.pcm_feedback != audio->pcm_feedback) {
+ MM_ERR("Not sufficient permission to"
+ "change the playback mode\n");
+ rc = -EACCES;
+ break;
+ }
+ if ((config.buffer_count > PCM_BUF_MAX_COUNT) ||
+ (config.buffer_count == 1))
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+
+ if (config.buffer_size < PCM_BUFSZ_MIN)
+ config.buffer_size = PCM_BUFSZ_MIN;
+
+ /* Check if pcm feedback is required */
+ if ((config.pcm_feedback) && (!audio->read_data)) {
+ MM_DBG("allocate PCM buffer %d\n",
+ config.buffer_count *
+ config.buffer_size);
+ audio->read_phys = pmem_kalloc(
+ config.buffer_size *
+ config.buffer_count,
+ PMEM_MEMTYPE_EBI1|
+ PMEM_ALIGNMENT_4K);
+ if (IS_ERR((void *)audio->read_phys)) {
+ rc = -ENOMEM;
+ break;
+ }
+ audio->read_data = ioremap(audio->read_phys,
+ config.buffer_size *
+ config.buffer_count);
+ if (!audio->read_data) {
+ MM_ERR("read buf alloc fail\n");
+ rc = -ENOMEM;
+ pmem_kfree(audio->read_phys);
+ } else {
+ uint8_t index;
+ uint32_t offset = 0;
+ audio->pcm_feedback = 1;
+ audio->buf_refresh = 0;
+ audio->pcm_buf_count =
+ config.buffer_count;
+ audio->read_next = 0;
+ audio->fill_next = 0;
+
+ for (index = 0;
+ index < config.buffer_count;
+ index++) {
+ audio->in[index].data =
+ audio->read_data + offset;
+ audio->in[index].addr =
+ audio->read_phys + offset;
+ audio->in[index].size =
+ config.buffer_size;
+ audio->in[index].used = 0;
+ offset += config.buffer_size;
+ }
+ MM_DBG("read buf: phy addr \
+ 0x%08x kernel addr 0x%08x\n",
+ audio->read_phys,
+ (int)audio->read_data);
+ rc = 0;
+ }
+ } else {
+ rc = 0;
+ }
+ break;
+ }
+ case AUDIO_PAUSE:
+ MM_DBG("AUDIO_PAUSE %ld\n", arg);
+ rc = audpp_pause(audio->dec_id, (int) arg);
+ break;
+ case AUDIO_GET_SESSION_ID:
+ if (copy_to_user((void *) arg, &audio->dec_id,
+ sizeof(unsigned short)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+/* Only useful in tunnel-mode */
+static int audio_fsync(struct file *file, int datasync)
+{
+ struct audio *audio = file->private_data;
+ struct buffer *frame;
+ int rc = 0;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+
+ if (!audio->running || audio->pcm_feedback) {
+ rc = -EINVAL;
+ goto done_nolock;
+ }
+
+ mutex_lock(&audio->write_lock);
+
+ rc = wait_event_interruptible(audio->write_wait,
+ (!audio->out[0].used &&
+ !audio->out[1].used &&
+ audio->out_needed) || audio->wflush);
+
+ if (rc < 0)
+ goto done;
+ else if (audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+
+ if (audio->reserved) {
+ MM_DBG("send reserved byte\n");
+ frame = audio->out + audio->out_tail;
+ ((char *) frame->data)[0] = audio->rsv_byte;
+ ((char *) frame->data)[1] = 0;
+ frame->used = 2;
+ audplay_send_data(audio, 0);
+
+ rc = wait_event_interruptible(audio->write_wait,
+ (!audio->out[0].used &&
+ !audio->out[1].used &&
+ audio->out_needed) || audio->wflush);
+
+ if (rc < 0)
+ goto done;
+ else if (audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+ }
+
+ /* pcm dmamiss message is sent continously
+ * when decoder is starved so no race
+ * condition concern
+ */
+ audio->teos = 0;
+
+ rc = wait_event_interruptible(audio->write_wait,
+ audio->teos || audio->wflush);
+
+ if (audio->wflush)
+ rc = -EBUSY;
+
+done:
+ mutex_unlock(&audio->write_lock);
+done_nolock:
+ return rc;
+}
+
+static ssize_t audio_read(struct file *file, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ int rc = 0;
+
+ if (!audio->pcm_feedback)
+ return 0; /* PCM feedback is not enabled. Nothing to read */
+
+ mutex_lock(&audio->read_lock);
+ MM_DBG("%d \n", count);
+ while (count > 0) {
+ rc = wait_event_interruptible(audio->read_wait,
+ (audio->in[audio->read_next].used > 0) ||
+ (audio->stopped) || (audio->rflush));
+
+ if (rc < 0)
+ break;
+
+ if (audio->stopped || audio->rflush) {
+ rc = -EBUSY;
+ break;
+ }
+
+ if (count < audio->in[audio->read_next].used) {
+ /* Read must happen in frame boundary. Since driver
+ does not know frame size, read count must be greater
+ or equal to size of PCM samples */
+ MM_DBG("audio_read: no partial frame done reading\n");
+ break;
+ } else {
+ MM_DBG("audio_read: read from in[%d]\n",
+ audio->read_next);
+ if (copy_to_user
+ (buf, audio->in[audio->read_next].data,
+ audio->in[audio->read_next].used)) {
+ MM_ERR("invalid addr %x \n", (unsigned int)buf);
+ rc = -EFAULT;
+ break;
+ }
+ count -= audio->in[audio->read_next].used;
+ buf += audio->in[audio->read_next].used;
+ audio->in[audio->read_next].used = 0;
+ if ((++audio->read_next) == audio->pcm_buf_count)
+ audio->read_next = 0;
+ break; /* Force to exit while loop
+ * to prevent output thread
+ * sleep too long if data is
+ * not ready at this moment.
+ */
+ }
+ }
+
+ /* don't feed output buffer to HW decoder during flushing
+ * buffer refresh command will be sent once flush completes
+ * send buf refresh command here can confuse HW decoder
+ */
+ if (audio->buf_refresh && !audio->rflush) {
+ audio->buf_refresh = 0;
+ MM_DBG("kick start pcm feedback again\n");
+ audplay_buffer_refresh(audio);
+ }
+
+ mutex_unlock(&audio->read_lock);
+
+ if (buf > start)
+ rc = buf - start;
+
+ MM_DBG("read %d bytes\n", rc);
+ return rc;
+}
+
+static int audwmapro_process_eos(struct audio *audio,
+ const char __user *buf_start, unsigned short mfield_size)
+{
+ int rc = 0;
+ struct buffer *frame;
+ char *buf_ptr;
+
+ if (audio->reserved) {
+ MM_DBG("flush reserve byte\n");
+ frame = audio->out + audio->out_head;
+ buf_ptr = frame->data;
+ rc = wait_event_interruptible(audio->write_wait,
+ (frame->used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+ if (rc < 0)
+ goto done;
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+
+ buf_ptr[0] = audio->rsv_byte;
+ buf_ptr[1] = 0;
+ audio->out_head ^= 1;
+ frame->mfield_sz = 0;
+ frame->used = 2;
+ audio->reserved = 0;
+ audplay_send_data(audio, 0);
+ }
+
+ frame = audio->out + audio->out_head;
+
+ rc = wait_event_interruptible(audio->write_wait,
+ (audio->out_needed &&
+ audio->out[0].used == 0 &&
+ audio->out[1].used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+
+ if (rc < 0)
+ goto done;
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ goto done;
+ }
+
+ if (copy_from_user(frame->data, buf_start, mfield_size)) {
+ rc = -EFAULT;
+ goto done;
+ }
+
+ frame->mfield_sz = mfield_size;
+ audio->out_head ^= 1;
+ frame->used = mfield_size;
+ audplay_send_data(audio, 0);
+done:
+ return rc;
+}
+
+static ssize_t audio_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ struct buffer *frame;
+ size_t xfer;
+ char *cpy_ptr;
+ int rc = 0, eos_condition = AUDWMAPRO_EOS_NONE;
+ unsigned dsize;
+ unsigned short mfield_size = 0;
+
+ MM_DBG("cnt=%d\n", count);
+
+ mutex_lock(&audio->write_lock);
+ while (count > 0) {
+ frame = audio->out + audio->out_head;
+ cpy_ptr = frame->data;
+ dsize = 0;
+ rc = wait_event_interruptible(audio->write_wait,
+ (frame->used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+ if (rc < 0)
+ break;
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ break;
+ }
+ if (audio->mfield) {
+ if (buf == start) {
+ /* Processing beginning of user buffer */
+ if (__get_user(mfield_size,
+ (unsigned short __user *) buf)) {
+ rc = -EFAULT;
+ break;
+ } else if (mfield_size > count) {
+ rc = -EINVAL;
+ break;
+ }
+ MM_DBG("audio_write: mf offset_val %x\n",
+ mfield_size);
+ if (copy_from_user(cpy_ptr, buf, mfield_size)) {
+ rc = -EFAULT;
+ break;
+ }
+ /* Check if EOS flag is set and buffer has
+ * contains just meta field
+ */
+ if (cpy_ptr[AUDWMAPRO_EOS_FLG_OFFSET] &
+ AUDWMAPRO_EOS_FLG_MASK) {
+ MM_DBG("audio_write: EOS SET\n");
+ eos_condition = AUDWMAPRO_EOS_SET;
+ if (mfield_size == count) {
+ buf += mfield_size;
+ break;
+ } else
+ cpy_ptr[AUDWMAPRO_EOS_FLG_OFFSET]
+ &= ~AUDWMAPRO_EOS_FLG_MASK;
+ }
+ cpy_ptr += mfield_size;
+ count -= mfield_size;
+ dsize += mfield_size;
+ buf += mfield_size;
+ } else {
+ mfield_size = 0;
+ MM_DBG("audio_write: continuous buffer\n");
+ }
+ frame->mfield_sz = mfield_size;
+ }
+
+ if (audio->reserved) {
+ MM_DBG("append reserved byte %x\n", audio->rsv_byte);
+ *cpy_ptr = audio->rsv_byte;
+ xfer = (count > ((frame->size - mfield_size) - 1)) ?
+ (frame->size - mfield_size) - 1 : count;
+ cpy_ptr++;
+ dsize += 1;
+ audio->reserved = 0;
+ } else
+ xfer = (count > (frame->size - mfield_size)) ?
+ (frame->size - mfield_size) : count;
+
+ if (copy_from_user(cpy_ptr, buf, xfer)) {
+ rc = -EFAULT;
+ break;
+ }
+
+ dsize += xfer;
+ if (dsize & 1) {
+ audio->rsv_byte = ((char *) frame->data)[dsize - 1];
+ MM_DBG("odd length buf reserve last byte %x\n",
+ audio->rsv_byte);
+ audio->reserved = 1;
+ dsize--;
+ }
+ count -= xfer;
+ buf += xfer;
+
+ if (dsize > 0) {
+ audio->out_head ^= 1;
+ frame->used = dsize;
+ audplay_send_data(audio, 0);
+ }
+ }
+ if (eos_condition == AUDWMAPRO_EOS_SET)
+ rc = audwmapro_process_eos(audio, start, mfield_size);
+ mutex_unlock(&audio->write_lock);
+ if (!rc) {
+ if (buf > start)
+ return buf - start;
+ }
+ return rc;
+}
+
+static int audio_release(struct inode *inode, struct file *file)
+{
+ struct audio *audio = file->private_data;
+
+ MM_INFO("audio instance 0x%08x freeing\n", (int)audio);
+ mutex_lock(&audio->lock);
+ auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id);
+ audio_disable(audio);
+ audio_flush(audio);
+ audio_flush_pcm_buf(audio);
+ msm_adsp_put(audio->audplay);
+ audpp_adec_free(audio->dec_id);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&audio->suspend_ctl.node);
+#endif
+ audio->event_abort = 1;
+ wake_up(&audio->event_wait);
+ audwmapro_reset_event_queue(audio);
+ iounmap(audio->data);
+ pmem_kfree(audio->phys);
+ if (audio->read_data) {
+ iounmap(audio->read_data);
+ pmem_kfree(audio->read_phys);
+ }
+ mutex_unlock(&audio->lock);
+#ifdef CONFIG_DEBUG_FS
+ if (audio->dentry)
+ debugfs_remove(audio->dentry);
+#endif
+ kfree(audio);
+ return 0;
+}
+
+static void audwmapro_post_event(struct audio *audio, int type,
+ union msm_audio_event_payload payload)
+{
+ struct audwmapro_event *e_node = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+
+ if (!list_empty(&audio->free_event_queue)) {
+ e_node = list_first_entry(&audio->free_event_queue,
+ struct audwmapro_event, list);
+ list_del(&e_node->list);
+ } else {
+ e_node = kmalloc(sizeof(struct audwmapro_event), GFP_ATOMIC);
+ if (!e_node) {
+ MM_ERR("No mem to post event %d\n", type);
+ return;
+ }
+ }
+
+ e_node->event_type = type;
+ e_node->payload = payload;
+
+ list_add_tail(&e_node->list, &audio->event_queue);
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+ wake_up(&audio->event_wait);
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void audwmapro_suspend(struct early_suspend *h)
+{
+ struct audwmapro_suspend_ctl *ctl =
+ container_of(h, struct audwmapro_suspend_ctl, node);
+ union msm_audio_event_payload payload;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ audwmapro_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload);
+}
+
+static void audwmapro_resume(struct early_suspend *h)
+{
+ struct audwmapro_suspend_ctl *ctl =
+ container_of(h, struct audwmapro_suspend_ctl, node);
+ union msm_audio_event_payload payload;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+ audwmapro_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload);
+}
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t audwmapro_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t audwmapro_debug_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ const int debug_bufmax = 4096;
+ static char buffer[4096];
+ int n = 0, i;
+ struct audio *audio = file->private_data;
+
+ mutex_lock(&audio->lock);
+ n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "enabled %d\n", audio->enabled);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "stopped %d\n", audio->stopped);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "pcm_feedback %d\n", audio->pcm_feedback);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_buf_sz %d\n", audio->out[0].size);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "pcm_buf_count %d \n", audio->pcm_buf_count);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "pcm_buf_sz %d \n", audio->in[0].size);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "volume %x \n", audio->vol_pan.volume);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "sample rate %d \n", audio->out_sample_rate);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "channel mode %d \n", audio->out_channel_mode);
+ mutex_unlock(&audio->lock);
+ /* Following variables are only useful for debugging when
+ * when playback halts unexpectedly. Thus, no mutual exclusion
+ * enforced
+ */
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "wflush %d\n", audio->wflush);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "rflush %d\n", audio->rflush);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "running %d \n", audio->running);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "dec state %d \n", audio->dec_state);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_needed %d \n", audio->out_needed);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_head %d \n", audio->out_head);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out_tail %d \n", audio->out_tail);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out[0].used %d \n", audio->out[0].used);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "out[1].used %d \n", audio->out[1].used);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "buffer_refresh %d \n", audio->buf_refresh);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "read_next %d \n", audio->read_next);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "fill_next %d \n", audio->fill_next);
+ for (i = 0; i < audio->pcm_buf_count; i++)
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "in[%d].size %d \n", i, audio->in[i].used);
+ buffer[n] = 0;
+ return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static const struct file_operations audwmapro_debug_fops = {
+ .read = audwmapro_debug_read,
+ .open = audwmapro_debug_open,
+};
+#endif
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct audio *audio = NULL;
+ int rc, dec_attrb, decid, i;
+ unsigned pmem_sz = DMASZ_MAX;
+ struct audwmapro_event *e_node = NULL;
+#ifdef CONFIG_DEBUG_FS
+ /* 4 bytes represents decoder number, 1 byte for terminate string */
+ char name[sizeof "msm_wmapro_" + 5];
+#endif
+
+ /* Allocate Mem for audio instance */
+ audio = kzalloc(sizeof(struct audio), GFP_KERNEL);
+ if (!audio) {
+ MM_ERR("no memory to allocate audio instance \n");
+ rc = -ENOMEM;
+ goto done;
+ }
+ MM_INFO("audio instance 0x%08x created\n", (int)audio);
+
+ /* Allocate the decoder */
+ dec_attrb = AUDDEC_DEC_WMAPRO;
+ if ((file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ dec_attrb |= MSM_AUD_MODE_NONTUNNEL;
+ audio->pcm_feedback = NON_TUNNEL_MODE_PLAYBACK;
+ } else if ((file->f_mode & FMODE_WRITE) &&
+ !(file->f_mode & FMODE_READ)) {
+ dec_attrb |= MSM_AUD_MODE_TUNNEL;
+ audio->pcm_feedback = TUNNEL_MODE_PLAYBACK;
+ } else {
+ kfree(audio);
+ rc = -EACCES;
+ goto done;
+ }
+
+ decid = audpp_adec_alloc(dec_attrb, &audio->module_name,
+ &audio->queue_id);
+
+ if (decid < 0) {
+ MM_ERR("No free decoder available, freeing instance 0x%08x\n",
+ (int)audio);
+ rc = -ENODEV;
+ kfree(audio);
+ goto done;
+ }
+ audio->dec_id = decid & MSM_AUD_DECODER_MASK;
+
+ while (pmem_sz >= DMASZ_MIN) {
+ MM_DBG("pmemsz = %d\n", pmem_sz);
+ audio->phys = pmem_kalloc(pmem_sz, PMEM_MEMTYPE_EBI1|
+ PMEM_ALIGNMENT_4K);
+ if (!IS_ERR((void *)audio->phys)) {
+ audio->data = ioremap(audio->phys, pmem_sz);
+ if (!audio->data) {
+ MM_ERR("could not allocate write buffers, \
+ freeing instance 0x%08x\n",
+ (int)audio);
+ rc = -ENOMEM;
+ pmem_kfree(audio->phys);
+ audpp_adec_free(audio->dec_id);
+ kfree(audio);
+ goto done;
+ }
+ MM_DBG("write buf: phy addr 0x%08x kernel addr \
+ 0x%08x\n", audio->phys, (int)audio->data);
+ break;
+ } else if (pmem_sz == DMASZ_MIN) {
+ MM_ERR("could not allocate write buffers, freeing \
+ instance 0x%08x\n", (int)audio);
+ rc = -ENOMEM;
+ audpp_adec_free(audio->dec_id);
+ kfree(audio);
+ goto done;
+ } else
+ pmem_sz >>= 1;
+ }
+ audio->out_dma_sz = pmem_sz;
+
+ rc = msm_adsp_get(audio->module_name, &audio->audplay,
+ &audplay_adsp_ops_wmapro, audio);
+ if (rc) {
+ MM_ERR("failed to get %s module, freeing instance 0x%08x\n",
+ audio->module_name, (int)audio);
+ goto err;
+ }
+
+ mutex_init(&audio->lock);
+ mutex_init(&audio->write_lock);
+ mutex_init(&audio->read_lock);
+ mutex_init(&audio->get_event_lock);
+ spin_lock_init(&audio->dsp_lock);
+ init_waitqueue_head(&audio->write_wait);
+ init_waitqueue_head(&audio->read_wait);
+ INIT_LIST_HEAD(&audio->free_event_queue);
+ INIT_LIST_HEAD(&audio->event_queue);
+ init_waitqueue_head(&audio->wait);
+ init_waitqueue_head(&audio->event_wait);
+ spin_lock_init(&audio->event_queue_lock);
+ init_waitqueue_head(&audio->avsync_wait);
+
+ audio->out[0].data = audio->data + 0;
+ audio->out[0].addr = audio->phys + 0;
+ audio->out[0].size = audio->out_dma_sz >> 1;
+
+ audio->out[1].data = audio->data + audio->out[0].size;
+ audio->out[1].addr = audio->phys + audio->out[0].size;
+ audio->out[1].size = audio->out[0].size;
+
+ /*audio->wmapro_config.armdatareqthr = 1268;
+ audio->wmapro_config.numchannels = 2;
+ audio->wmapro_config.avgbytespersecond = 6003;
+ audio->wmapro_config.samplingrate = 44100;
+ audio->wmapro_config.encodeopt = 224;
+ audio->wmapro_config.validbitspersample = 16;
+ audio->wmapro_config.formattag = 354;
+ audio->wmapro_config.asfpacketlength = 2230;
+ audio->wmapro_config.channelmask = 3;
+ audio->wmapro_config.advancedencodeopt = 32834;
+ audio->wmapro_config.advancedencodeopt2 = 0;*/
+
+ audio->out_sample_rate = 44100;
+ audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V;
+
+ audio->vol_pan.volume = 0x2000;
+
+ audio_flush(audio);
+
+ file->private_data = audio;
+ audio->opened = 1;
+ audio->device_events = AUDDEV_EVT_DEV_RDY
+ |AUDDEV_EVT_DEV_RLS|
+ AUDDEV_EVT_STREAM_VOL_CHG;
+
+ rc = auddev_register_evt_listner(audio->device_events,
+ AUDDEV_CLNT_DEC,
+ audio->dec_id,
+ wmapro_listner,
+ (void *)audio);
+ if (rc) {
+ MM_ERR("%s: failed to register listner\n", __func__);
+ goto event_err;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ snprintf(name, sizeof name, "msm_wmapro_%04x", audio->dec_id);
+ audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *) audio,
+ &audwmapro_debug_fops);
+
+ if (IS_ERR(audio->dentry))
+ MM_DBG("debugfs_create_file failed\n");
+#endif
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+ audio->suspend_ctl.node.resume = audwmapro_resume;
+ audio->suspend_ctl.node.suspend = audwmapro_suspend;
+ audio->suspend_ctl.audio = audio;
+ register_early_suspend(&audio->suspend_ctl.node);
+#endif
+ for (i = 0; i < AUDWMAPRO_EVENT_NUM; i++) {
+ e_node = kmalloc(sizeof(struct audwmapro_event), GFP_KERNEL);
+ if (e_node)
+ list_add_tail(&e_node->list, &audio->free_event_queue);
+ else {
+ MM_ERR("event pkt alloc failed\n");
+ break;
+ }
+ }
+done:
+ return rc;
+event_err:
+ msm_adsp_put(audio->audplay);
+err:
+ iounmap(audio->data);
+ pmem_kfree(audio->phys);
+ audpp_adec_free(audio->dec_id);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_wmapro_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_release,
+ .read = audio_read,
+ .write = audio_write,
+ .unlocked_ioctl = audio_ioctl,
+ .fsync = audio_fsync,
+};
+
+struct miscdevice audio_wmapro_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_wmapro",
+ .fops = &audio_wmapro_fops,
+};
+
+static int __init audio_init(void)
+{
+ return misc_register(&audio_wmapro_misc);
+}
+
+device_initcall(audio_init);
diff --git a/arch/arm/mach-msm/qdsp5v2/audpp.c b/arch/arm/mach-msm/qdsp5v2/audpp.c
new file mode 100644
index 0000000..be274c7
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audpp.c
@@ -0,0 +1,1133 @@
+/* arch/arm/mach-msm/qdsp5/audpp.c
+ *
+ * common code to deal with the AUDPP dsp task (audio postproc)
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/platform_device.h>
+#include <linux/wakelock.h>
+
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/board.h>
+#include <mach/msm_adsp.h>
+#include <mach/qdsp5v2/audio_acdbi.h>
+#include <mach/qdsp5v2/qdsp5audppcmdi.h>
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/audpp.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+
+#include "../qdsp5/evlog.h"
+#include <mach/debug_mm.h>
+
+enum {
+ EV_NULL,
+ EV_ENABLE,
+ EV_DISABLE,
+ EV_EVENT,
+ EV_DATA,
+};
+
+static const char *dsp_log_strings[] = {
+ "NULL",
+ "ENABLE",
+ "DISABLE",
+ "EVENT",
+ "DATA",
+};
+
+DECLARE_LOG(dsp_log, 64, dsp_log_strings);
+
+static int __init _dsp_log_init(void)
+{
+ return ev_log_init(&dsp_log);
+}
+
+module_init(_dsp_log_init);
+#define LOG(id, arg) ev_log_write(&dsp_log, id, arg)
+
+static DEFINE_MUTEX(audpp_lock);
+static DEFINE_MUTEX(audpp_dec_lock);
+static struct wake_lock audpp_wake_lock;
+
+#define CH_COUNT 5
+#define AUDPP_CLNT_MAX_COUNT 6
+
+#define AUDPP_CMD_CFG_OBJ_UPDATE 0x8000
+#define AUDPP_CMD_EQ_FLAG_DIS 0x0000
+#define AUDPP_CMD_EQ_FLAG_ENA -1
+#define AUDPP_CMD_IIR_FLAG_DIS 0x0000
+#define AUDPP_CMD_IIR_FLAG_ENA -1
+#define AUDPP_CMD_STF_FLAG_ENA -1
+#define AUDPP_CMD_STF_FLAG_DIS 0x0000
+
+#define MAX_EVENT_CALLBACK_CLIENTS 1
+
+#define AUDPP_CONCURRENCY_DEFAULT 0 /* Set default to LPA mode */
+#define AUDPP_MAX_DECODER_CNT 5
+#define AUDPP_CODEC_MASK 0x000000FF
+#define AUDPP_MODE_MASK 0x00000F00
+#define AUDPP_OP_MASK 0xF0000000
+
+struct audpp_decoder_info {
+ unsigned int codec;
+ pid_t pid;
+};
+
+struct audpp_state {
+ struct msm_adsp_module *mod;
+ audpp_event_func func[AUDPP_CLNT_MAX_COUNT];
+ void *private[AUDPP_CLNT_MAX_COUNT];
+ struct mutex *lock;
+ unsigned open_count;
+ unsigned enabled;
+
+ /* Related to decoder allocation */
+ struct mutex *lock_dec;
+ struct msm_adspdec_database *dec_database;
+ struct audpp_decoder_info dec_info_table[AUDPP_MAX_DECODER_CNT];
+ unsigned dec_inuse;
+ unsigned long concurrency;
+
+ struct audpp_event_callback *cb_tbl[MAX_EVENT_CALLBACK_CLIENTS];
+
+ /* Related to decoder instances */
+ uint8_t op_mode; /* Specifies Turbo/Non Turbo mode */
+ uint8_t decoder_count; /* No. of decoders active running */
+ uint8_t codec_max_instances; /* Max codecs allowed currently */
+ uint8_t codec_cnt[MSM_MAX_DEC_CNT]; /* Nr of each codec
+ type enabled */
+
+ wait_queue_head_t event_wait;
+};
+
+struct audpp_state the_audpp_state = {
+ .lock = &audpp_lock,
+ .lock_dec = &audpp_dec_lock,
+};
+
+static inline void prevent_suspend(void)
+{
+ wake_lock(&audpp_wake_lock);
+}
+static inline void allow_suspend(void)
+{
+ wake_unlock(&audpp_wake_lock);
+}
+
+int audpp_send_queue1(void *cmd, unsigned len)
+{
+ return msm_adsp_write(the_audpp_state.mod,
+ QDSP_uPAudPPCmd1Queue, cmd, len);
+}
+EXPORT_SYMBOL(audpp_send_queue1);
+
+int audpp_send_queue2(void *cmd, unsigned len)
+{
+ return msm_adsp_write(the_audpp_state.mod,
+ QDSP_uPAudPPCmd2Queue, cmd, len);
+}
+EXPORT_SYMBOL(audpp_send_queue2);
+
+int audpp_send_queue3(void *cmd, unsigned len)
+{
+ return msm_adsp_write(the_audpp_state.mod,
+ QDSP_uPAudPPCmd3Queue, cmd, len);
+}
+EXPORT_SYMBOL(audpp_send_queue3);
+
+static int audpp_dsp_config(int enable)
+{
+ struct audpp_cmd_cfg cmd;
+
+ cmd.cmd_id = AUDPP_CMD_CFG;
+ cmd.cfg = enable ? AUDPP_CMD_CFG_ENABLE : AUDPP_CMD_CFG_SLEEP;
+
+ return audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+void audpp_route_stream(unsigned short dec_id, unsigned short mixer_mask)
+{
+ struct audpp_cmd_cfg_dev_mixer_params mixer_params_cmd;
+
+ memset(&mixer_params_cmd, 0, sizeof(mixer_params_cmd));
+
+ mixer_params_cmd.cmd_id = AUDPP_CMD_CFG_DEV_MIXER;
+ mixer_params_cmd.stream_id = dec_id;
+ mixer_params_cmd.mixer_cmd = mixer_mask;
+ audpp_send_queue1(&mixer_params_cmd, sizeof(mixer_params_cmd));
+
+}
+EXPORT_SYMBOL(audpp_route_stream);
+
+int is_audpp_enable(void)
+{
+ struct audpp_state *audpp = &the_audpp_state;
+
+ return audpp->enabled;
+}
+EXPORT_SYMBOL(is_audpp_enable);
+
+int audpp_register_event_callback(struct audpp_event_callback *ecb)
+{
+ struct audpp_state *audpp = &the_audpp_state;
+ int i;
+
+ for (i = 0; i < MAX_EVENT_CALLBACK_CLIENTS; ++i) {
+ if (NULL == audpp->cb_tbl[i]) {
+ audpp->cb_tbl[i] = ecb;
+ return 0;
+ }
+ }
+ return -1;
+}
+EXPORT_SYMBOL(audpp_register_event_callback);
+
+
+int audpp_unregister_event_callback(struct audpp_event_callback *ecb)
+{
+ struct audpp_state *audpp = &the_audpp_state;
+ int i;
+
+ for (i = 0; i < MAX_EVENT_CALLBACK_CLIENTS; ++i) {
+ if (ecb == audpp->cb_tbl[i]) {
+ audpp->cb_tbl[i] = NULL;
+ return 0;
+ }
+ }
+ return -1;
+}
+EXPORT_SYMBOL(audpp_unregister_event_callback);
+
+static void audpp_broadcast(struct audpp_state *audpp, unsigned id,
+ uint16_t *msg)
+{
+ unsigned n;
+ for (n = 0; n < AUDPP_CLNT_MAX_COUNT; n++) {
+ if (audpp->func[n])
+ audpp->func[n] (audpp->private[n], id, msg);
+ }
+
+ for (n = 0; n < MAX_EVENT_CALLBACK_CLIENTS; ++n)
+ if (audpp->cb_tbl[n] && audpp->cb_tbl[n]->fn)
+ audpp->cb_tbl[n]->fn(audpp->cb_tbl[n]->private, id,
+ msg);
+}
+
+static void audpp_notify_clnt(struct audpp_state *audpp, unsigned clnt_id,
+ unsigned id, uint16_t *msg)
+{
+ if (clnt_id < AUDPP_CLNT_MAX_COUNT && audpp->func[clnt_id])
+ audpp->func[clnt_id] (audpp->private[clnt_id], id, msg);
+}
+
+static void audpp_handle_pcmdmamiss(struct audpp_state *audpp,
+ uint16_t bit_mask)
+{
+ uint8_t b_index;
+
+ for (b_index = 0; b_index < AUDPP_CLNT_MAX_COUNT; b_index++) {
+ if (bit_mask & (0x1 << b_index))
+ if (audpp->func[b_index])
+ audpp->func[b_index] (audpp->private[b_index],
+ AUDPP_MSG_PCMDMAMISSED,
+ &bit_mask);
+ }
+}
+
+static void audpp_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent) (void *ptr, size_t len))
+{
+ struct audpp_state *audpp = data;
+ uint16_t msg[8];
+
+ getevent(msg, sizeof(msg));
+
+ LOG(EV_EVENT, (id << 16) | msg[0]);
+ LOG(EV_DATA, (msg[1] << 16) | msg[2]);
+
+ switch (id) {
+ case AUDPP_MSG_STATUS_MSG:{
+ unsigned cid = msg[0];
+ MM_DBG("status %d %d %d\n", cid, msg[1], msg[2]);
+
+ if ((cid < 5) && audpp->func[cid])
+ audpp->func[cid] (audpp->private[cid], id, msg);
+ break;
+ }
+ case AUDPP_MSG_HOST_PCM_INTF_MSG:
+ if (audpp->func[5])
+ audpp->func[5] (audpp->private[5], id, msg);
+ break;
+ case AUDPP_MSG_PCMDMAMISSED:
+ audpp_handle_pcmdmamiss(audpp, msg[0]);
+ break;
+ case AUDPP_MSG_CFG_MSG:
+ if (msg[0] == AUDPP_MSG_ENA_ENA) {
+ MM_INFO("ENABLE\n");
+ audpp->enabled = 1;
+ audpp_broadcast(audpp, id, msg);
+ } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+ MM_INFO("DISABLE\n");
+ audpp->enabled = 0;
+ wake_up(&audpp->event_wait);
+ audpp_broadcast(audpp, id, msg);
+ } else {
+ MM_ERR("invalid config msg %d\n", msg[0]);
+ }
+ break;
+ case AUDPP_MSG_ROUTING_ACK:
+ audpp_notify_clnt(audpp, msg[0], id, msg);
+ break;
+ case AUDPP_MSG_FLUSH_ACK:
+ audpp_notify_clnt(audpp, msg[0], id, msg);
+ break;
+ case ADSP_MESSAGE_ID:
+ MM_DBG("Received ADSP event: module enable/disable \
+ (audpptask)");
+ break;
+ case AUDPP_MSG_AVSYNC_MSG:
+ audpp_notify_clnt(audpp, msg[0], id, msg);
+ break;
+#ifdef CONFIG_DEBUG_FS
+ case AUDPP_MSG_FEAT_QUERY_DM_DONE:
+ MM_INFO(" RTC ACK --> %x %x %x %x %x %x %x %x\n", msg[0],\
+ msg[1], msg[2], msg[3], msg[4], \
+ msg[5], msg[6], msg[7]);
+ acdb_rtc_set_err(msg[3]);
+ break;
+#endif
+ default:
+ MM_INFO("unhandled msg id %x\n", id);
+ }
+}
+
+static struct msm_adsp_ops adsp_ops = {
+ .event = audpp_dsp_event,
+};
+
+static void audpp_fake_event(struct audpp_state *audpp, int id,
+ unsigned event, unsigned arg)
+{
+ uint16_t msg[1];
+ msg[0] = arg;
+ audpp->func[id] (audpp->private[id], event, msg);
+}
+
+int audpp_enable(int id, audpp_event_func func, void *private)
+{
+ struct audpp_state *audpp = &the_audpp_state;
+ int res = 0;
+
+ if (id < -1 || id > 4)
+ return -EINVAL;
+
+ if (id == -1)
+ id = 5;
+
+ mutex_lock(audpp->lock);
+ if (audpp->func[id]) {
+ res = -EBUSY;
+ goto out;
+ }
+
+ audpp->func[id] = func;
+ audpp->private[id] = private;
+
+ LOG(EV_ENABLE, 1);
+ if (audpp->open_count++ == 0) {
+ MM_DBG("enable\n");
+ res = msm_adsp_get("AUDPPTASK", &audpp->mod, &adsp_ops, audpp);
+ if (res < 0) {
+ MM_ERR("audpp: cannot open AUDPPTASK\n");
+ audpp->open_count = 0;
+ audpp->func[id] = NULL;
+ audpp->private[id] = NULL;
+ goto out;
+ }
+ LOG(EV_ENABLE, 2);
+ prevent_suspend();
+ msm_adsp_enable(audpp->mod);
+ audpp_dsp_config(1);
+ } else {
+ unsigned long flags;
+ local_irq_save(flags);
+ if (audpp->enabled)
+ audpp_fake_event(audpp, id,
+ AUDPP_MSG_CFG_MSG, AUDPP_MSG_ENA_ENA);
+ local_irq_restore(flags);
+ }
+
+ res = 0;
+out:
+ mutex_unlock(audpp->lock);
+ return res;
+}
+EXPORT_SYMBOL(audpp_enable);
+
+void audpp_disable(int id, void *private)
+{
+ struct audpp_state *audpp = &the_audpp_state;
+ unsigned long flags;
+ int rc;
+
+ if (id < -1 || id > 4)
+ return;
+
+ if (id == -1)
+ id = 5;
+
+ mutex_lock(audpp->lock);
+ LOG(EV_DISABLE, 1);
+ if (!audpp->func[id])
+ goto out;
+ if (audpp->private[id] != private)
+ goto out;
+
+ local_irq_save(flags);
+ audpp_fake_event(audpp, id, AUDPP_MSG_CFG_MSG, AUDPP_MSG_ENA_DIS);
+ audpp->func[id] = NULL;
+ audpp->private[id] = NULL;
+ local_irq_restore(flags);
+
+ if (--audpp->open_count == 0) {
+ MM_DBG("disable\n");
+ LOG(EV_DISABLE, 2);
+ audpp_dsp_config(0);
+ rc = wait_event_interruptible(audpp->event_wait,
+ (audpp->enabled == 0));
+ if (audpp->enabled == 0)
+ MM_INFO("Received CFG_MSG_DISABLE from ADSP\n");
+ else
+ MM_ERR("Didn't receive CFG_MSG DISABLE \
+ message from ADSP\n");
+ msm_adsp_disable(audpp->mod);
+ msm_adsp_put(audpp->mod);
+ audpp->mod = NULL;
+ allow_suspend();
+ }
+out:
+ mutex_unlock(audpp->lock);
+}
+EXPORT_SYMBOL(audpp_disable);
+
+#define BAD_ID(id) ((id < 0) || (id >= CH_COUNT))
+
+int audpp_restore_avsync(int id, uint16_t *avsync)
+{
+ struct audpp_cmd_avsync cmd;
+
+ if (BAD_ID(id))
+ return -1;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_AVSYNC;
+ cmd.stream_id = id;
+ cmd.interrupt_interval = 0; /* Setting it to Zero as there won't be
+ periodic update */
+ cmd.sample_counter_dlsw = avsync[3];
+ cmd.sample_counter_dmsw = avsync[2];
+ cmd.sample_counter_msw = avsync[1];
+ cmd.byte_counter_dlsw = avsync[6];
+ cmd.byte_counter_dmsw = avsync[5];
+ cmd.byte_counter_msw = avsync[4];
+
+ return audpp_send_queue1(&cmd, sizeof(cmd));
+}
+EXPORT_SYMBOL(audpp_restore_avsync);
+
+int audpp_query_avsync(int id)
+{
+ struct audpp_cmd_query_avsync cmd;
+
+ if (BAD_ID(id))
+ return -EINVAL;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_QUERY_AVSYNC;
+ cmd.stream_id = id;
+ return audpp_send_queue1(&cmd, sizeof(cmd));
+
+}
+EXPORT_SYMBOL(audpp_query_avsync);
+
+int audpp_set_volume_and_pan(unsigned id, unsigned volume, int pan,
+ enum obj_type objtype)
+{
+ /* cmd, obj_cfg[7], cmd_type, volume, pan */
+ uint16_t cmd[7];
+
+ if (objtype) {
+ if (id > 5) {
+ MM_ERR("Wrong POPP decoder id: %d\n", id);
+ return -EINVAL;
+ }
+ } else {
+ if (id > 3) {
+ MM_ERR("Wrong COPP decoder id: %d\n", id);
+ return -EINVAL;
+ }
+ }
+
+ memset(cmd, 0, sizeof(cmd));
+ cmd[0] = AUDPP_CMD_CFG_OBJECT_PARAMS;
+ if (objtype)
+ cmd[1] = AUDPP_CMD_POPP_STREAM;
+ else
+ cmd[1] = AUDPP_CMD_COPP_STREAM;
+ cmd[2] = id;
+ cmd[3] = AUDPP_CMD_CFG_OBJ_UPDATE;
+ cmd[4] = AUDPP_CMD_VOLUME_PAN;
+ cmd[5] = volume;
+ cmd[6] = pan;
+
+ return audpp_send_queue3(cmd, sizeof(cmd));
+}
+EXPORT_SYMBOL(audpp_set_volume_and_pan);
+
+/* Implementation of COPP features */
+int audpp_dsp_set_mbadrc(unsigned id, unsigned enable,
+ struct audpp_cmd_cfg_object_params_mbadrc *mbadrc,
+ enum obj_type objtype)
+{
+ if (objtype) {
+ if (id > 5) {
+ MM_ERR("Wrong POPP decoder id: %d\n", id);
+ return -EINVAL;
+ }
+ } else {
+ if (id > 3) {
+ MM_ERR("Wrong COPP decoder id: %d\n", id);
+ return -EINVAL;
+ }
+ }
+
+ mbadrc->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS;
+ if (objtype)
+ mbadrc->common.stream = AUDPP_CMD_POPP_STREAM;
+ else
+ mbadrc->common.stream = AUDPP_CMD_COPP_STREAM;
+
+ mbadrc->common.stream_id = id;
+ mbadrc->common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE;
+ mbadrc->common.command_type = AUDPP_CMD_MBADRC;
+
+ if (enable)
+ mbadrc->enable = AUDPP_CMD_ADRC_FLAG_ENA;
+ else
+ mbadrc->enable = AUDPP_CMD_ADRC_FLAG_DIS;
+
+ return audpp_send_queue3(mbadrc,
+ sizeof(struct audpp_cmd_cfg_object_params_mbadrc));
+}
+EXPORT_SYMBOL(audpp_dsp_set_mbadrc);
+
+int audpp_dsp_set_qconcert_plus(unsigned id, unsigned enable,
+ struct audpp_cmd_cfg_object_params_qconcert *qconcert_plus,
+ enum obj_type objtype)
+{
+ if (objtype) {
+ if (id > 5) {
+ MM_ERR("Wrong POPP decoder id: %d\n", id);
+ return -EINVAL;
+ }
+ } else {
+ if (id > 3) {
+ MM_ERR("Wrong COPP decoder id: %d\n", id);
+ return -EINVAL;
+ }
+ }
+
+ qconcert_plus->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS;
+ if (objtype)
+ qconcert_plus->common.stream = AUDPP_CMD_POPP_STREAM;
+ else
+ qconcert_plus->common.stream = AUDPP_CMD_COPP_STREAM;
+
+ qconcert_plus->common.stream_id = id;
+ qconcert_plus->common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE;
+ qconcert_plus->common.command_type = AUDPP_CMD_QCONCERT;
+
+ if (enable)
+ qconcert_plus->enable_flag = AUDPP_CMD_ADRC_FLAG_ENA;
+ else
+ qconcert_plus->enable_flag = AUDPP_CMD_ADRC_FLAG_DIS;
+
+ return audpp_send_queue3(qconcert_plus,
+ sizeof(struct audpp_cmd_cfg_object_params_qconcert));
+}
+EXPORT_SYMBOL(audpp_dsp_set_qconcert_plus);
+
+int audpp_dsp_set_rx_iir(unsigned id, unsigned enable,
+ struct audpp_cmd_cfg_object_params_pcm *iir,
+ enum obj_type objtype)
+{
+
+ if (objtype) {
+ if (id > 5) {
+ MM_ERR("Wrong POPP decoder id: %d\n", id);
+ return -EINVAL;
+ }
+ } else {
+ if (id > 3) {
+ MM_ERR("Wrong COPP decoder id: %d\n", id);
+ return -EINVAL;
+ }
+ }
+
+ iir->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS;
+ if (objtype)
+ iir->common.stream = AUDPP_CMD_POPP_STREAM;
+ else
+ iir->common.stream = AUDPP_CMD_COPP_STREAM;
+
+ iir->common.stream_id = id;
+ iir->common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE;
+ iir->common.command_type = AUDPP_CMD_IIR_TUNING_FILTER;
+
+ if (enable)
+ iir->active_flag = AUDPP_CMD_IIR_FLAG_ENA;
+ else
+ iir->active_flag = AUDPP_CMD_IIR_FLAG_DIS;
+
+ return audpp_send_queue3(iir,
+ sizeof(struct audpp_cmd_cfg_object_params_pcm));
+}
+EXPORT_SYMBOL(audpp_dsp_set_rx_iir);
+
+int audpp_dsp_set_gain_rx(unsigned id,
+ struct audpp_cmd_cfg_cal_gain *calib_gain_rx,
+ enum obj_type objtype)
+{
+ if (objtype) {
+ return -EINVAL;
+ } else {
+ if (id > 3) {
+ MM_ERR("Wrong COPP decoder id: %d\n", id);
+ return -EINVAL;
+ }
+ }
+ calib_gain_rx->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS;
+ calib_gain_rx->common.stream = AUDPP_CMD_COPP_STREAM;
+
+ calib_gain_rx->common.stream_id = id;
+ calib_gain_rx->common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE;
+ calib_gain_rx->common.command_type = AUDPP_CMD_CALIB_GAIN_RX;
+
+ return audpp_send_queue3(calib_gain_rx,
+ sizeof(struct audpp_cmd_cfg_cal_gain));
+}
+EXPORT_SYMBOL(audpp_dsp_set_gain_rx);
+
+int audpp_dsp_set_pbe(unsigned id, unsigned enable,
+ struct audpp_cmd_cfg_pbe *pbe_block,
+ enum obj_type objtype)
+{
+ if (objtype) {
+ if (id > 5) {
+ MM_ERR("Wrong POPP decoder id: %d\n", id);
+ return -EINVAL;
+ }
+ } else {
+ if (id > 3) {
+ MM_ERR("Wrong COPP decoder id: %d\n", id);
+ return -EINVAL;
+ }
+ }
+
+ pbe_block->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS;
+ if (objtype)
+ pbe_block->common.stream = AUDPP_CMD_POPP_STREAM;
+ else
+ pbe_block->common.stream = AUDPP_CMD_COPP_STREAM;
+
+ pbe_block->common.stream_id = id;
+ pbe_block->common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE;
+ pbe_block->common.command_type = AUDPP_CMD_PBE;
+
+ if (enable)
+ pbe_block->pbe_enable = AUDPP_CMD_PBE_FLAG_ENA;
+ else
+ pbe_block->pbe_enable = AUDPP_CMD_PBE_FLAG_DIS;
+
+ return audpp_send_queue3(pbe_block,
+ sizeof(struct audpp_cmd_cfg_pbe));
+}
+EXPORT_SYMBOL(audpp_dsp_set_pbe);
+
+int audpp_dsp_set_spa(unsigned id,
+ struct audpp_cmd_cfg_object_params_spectram *spa,
+ enum obj_type objtype){
+ struct audpp_cmd_cfg_object_params_spectram cmd;
+
+ if (objtype) {
+ if (id > 5) {
+ MM_ERR("Wrong POPP decoder id: %d\n", id);
+ return -EINVAL;
+ }
+ } else {
+ if (id > 3) {
+ MM_ERR("Wrong COPP decoder id: %d\n", id);
+ return -EINVAL;
+ }
+ }
+
+ memset(&cmd, 0, sizeof(cmd));
+ if (objtype)
+ cmd.common.stream = AUDPP_CMD_POPP_STREAM;
+ else
+ cmd.common.stream = AUDPP_CMD_COPP_STREAM;
+
+ cmd.common.stream_id = id;
+ cmd.common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE;
+ cmd.common.command_type = AUDPP_CMD_SPECTROGRAM;
+ cmd.sample_interval = spa->sample_interval;
+ cmd.num_coeff = spa->num_coeff;
+ return audpp_send_queue3(&cmd, sizeof(cmd));
+
+}
+EXPORT_SYMBOL(audpp_dsp_set_spa);
+
+int audpp_dsp_set_stf(unsigned id, unsigned enable,
+ struct audpp_cmd_cfg_object_params_sidechain *stf,
+ enum obj_type objtype){
+ if (objtype) {
+ if (id > 5) {
+ MM_ERR("Wrong POPP decoder id: %d\n", id);
+ return -EINVAL;
+ }
+ } else {
+ if (id > 3) {
+ MM_ERR("Wrong COPP decoder id: %d\n", id);
+ return -EINVAL;
+ }
+ }
+
+ stf->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS;
+ if (objtype)
+ stf->common.stream = AUDPP_CMD_POPP_STREAM;
+ else
+ stf->common.stream = AUDPP_CMD_COPP_STREAM;
+
+ stf->common.stream_id = id;
+ stf->common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE;
+ stf->common.command_type = AUDPP_CMD_SIDECHAIN_TUNING_FILTER;
+
+ if (enable)
+ stf->active_flag = AUDPP_CMD_STF_FLAG_ENA;
+ else
+ stf->active_flag = AUDPP_CMD_STF_FLAG_DIS;
+ return audpp_send_queue3(stf,
+ sizeof(struct audpp_cmd_cfg_object_params_sidechain));
+}
+EXPORT_SYMBOL(audpp_dsp_set_stf);
+
+/* Implementation Of COPP + POPP */
+int audpp_dsp_set_eq(unsigned id, unsigned enable,
+ struct audpp_cmd_cfg_object_params_eqalizer *eq,
+ enum obj_type objtype)
+{
+ struct audpp_cmd_cfg_object_params_eqalizer cmd;
+
+ if (objtype) {
+ if (id > 5) {
+ MM_ERR("Wrong POPP decoder id: %d\n", id);
+ return -EINVAL;
+ }
+ } else {
+ if (id > 3) {
+ MM_ERR("Wrong COPP decoder id: %d\n", id);
+ return -EINVAL;
+ }
+ }
+
+ memset(&cmd, 0, sizeof(cmd));
+ if (objtype)
+ cmd.common.stream = AUDPP_CMD_POPP_STREAM;
+ else
+ cmd.common.stream = AUDPP_CMD_COPP_STREAM;
+
+ cmd.common.stream_id = id;
+ cmd.common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE;
+ cmd.common.command_type = AUDPP_CMD_EQUALIZER;
+ if (enable) {
+ cmd.eq_flag = AUDPP_CMD_EQ_FLAG_ENA;
+ cmd.num_bands = eq->num_bands;
+ memcpy(&cmd.eq_coeff, &eq->eq_coeff, sizeof(eq->eq_coeff));
+ } else
+ cmd.eq_flag = AUDPP_CMD_EQ_FLAG_DIS;
+
+ return audpp_send_queue3(&cmd, sizeof(cmd));
+}
+EXPORT_SYMBOL(audpp_dsp_set_eq);
+
+int audpp_dsp_set_vol_pan(unsigned id,
+ struct audpp_cmd_cfg_object_params_volume *vol_pan,
+ enum obj_type objtype)
+{
+ struct audpp_cmd_cfg_object_params_volume cmd;
+
+ if (objtype) {
+ if (id > 5) {
+ MM_ERR("Wrong POPP decoder id: %d\n", id);
+ return -EINVAL;
+ }
+ } else {
+ if (id > AUDPP_MAX_COPP_DEVICES) {
+ MM_ERR("Wrong COPP decoder id: %d\n", id);
+ return -EINVAL;
+ }
+ }
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS;
+ if (objtype)
+ cmd.common.stream = AUDPP_CMD_POPP_STREAM;
+ else
+ cmd.common.stream = AUDPP_CMD_COPP_STREAM;
+
+ cmd.common.stream_id = id;
+ cmd.common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE;
+ cmd.common.command_type = AUDPP_CMD_VOLUME_PAN;
+
+ cmd.volume = vol_pan->volume;
+ cmd.pan = vol_pan->pan;
+
+ return audpp_send_queue3(&cmd, sizeof(cmd));
+}
+EXPORT_SYMBOL(audpp_dsp_set_vol_pan);
+
+int audpp_pause(unsigned id, int pause)
+{
+ /* pause 1 = pause 0 = resume */
+ u16 pause_cmd[AUDPP_CMD_DEC_CTRL_LEN / sizeof(unsigned short)];
+
+ if (id >= CH_COUNT)
+ return -EINVAL;
+
+ memset(pause_cmd, 0, sizeof(pause_cmd));
+
+ pause_cmd[0] = AUDPP_CMD_DEC_CTRL;
+ pause_cmd[1] = id;
+ if (pause == 1)
+ pause_cmd[2] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_PAUSE_V;
+ else if (pause == 0)
+ pause_cmd[2] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_RESUME_V;
+ else
+ return -EINVAL;
+
+ return audpp_send_queue1(pause_cmd, sizeof(pause_cmd));
+}
+EXPORT_SYMBOL(audpp_pause);
+
+int audpp_flush(unsigned id)
+{
+ u16 flush_cmd[AUDPP_CMD_DEC_CTRL_LEN / sizeof(unsigned short)];
+
+ if (id >= CH_COUNT)
+ return -EINVAL;
+
+ memset(flush_cmd, 0, sizeof(flush_cmd));
+
+ flush_cmd[0] = AUDPP_CMD_DEC_CTRL;
+ flush_cmd[1] = id;
+ flush_cmd[2] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_FLUSH_V;
+
+ return audpp_send_queue1(flush_cmd, sizeof(flush_cmd));
+}
+EXPORT_SYMBOL(audpp_flush);
+
+/* dec_attrb = 7:0, 0 - No Decoder, else supported decoder *
+ * like mp3, aac, wma etc ... *
+ * = 15:8, bit[8] = 1 - Tunnel, bit[9] = 1 - NonTunnel *
+ * = 31:16, reserved */
+int audpp_adec_alloc(unsigned dec_attrb, const char **module_name,
+ unsigned *queueid)
+{
+ struct audpp_state *audpp = &the_audpp_state;
+ int decid = -1, idx, lidx, mode, codec;
+ int codecs_supported, min_codecs_supported;
+ unsigned int *concurrency_entry;
+ u8 max_instance, codec_type;
+
+ struct dec_instance_table *dec_instance_list;
+ dec_instance_list = (struct dec_instance_table *)
+ (audpp->dec_database->dec_instance_list);
+
+ mutex_lock(audpp->lock_dec);
+ /* Represents in bit mask */
+ mode = ((dec_attrb & AUDPP_MODE_MASK) << 16);
+ codec = (1 << (dec_attrb & AUDPP_CODEC_MASK));
+ codec_type = (dec_attrb & AUDPP_CODEC_MASK);
+
+ /* Find whether same/different codec instances are running */
+ audpp->decoder_count++;
+ audpp->codec_cnt[codec_type]++;
+ max_instance = 0;
+
+ /*if different instance of codec*/
+ if (audpp->codec_cnt[codec_type] < audpp->decoder_count) {
+ max_instance = audpp->codec_max_instances;
+ /* Get the maximum no. of instances that can be supported */
+ for (idx = 0; idx < MSM_MAX_DEC_CNT; idx++) {
+ if (audpp->codec_cnt[idx]) {
+ if ((dec_instance_list +
+ audpp->op_mode * MSM_MAX_DEC_CNT +
+ idx)->
+ max_instances_diff_dec <
+ max_instance) {
+ max_instance =
+ (dec_instance_list +
+ audpp->op_mode *
+ MSM_MAX_DEC_CNT
+ + idx)->
+ max_instances_diff_dec;
+ }
+ }
+ }
+ /* if different codec type, should not cross maximum other
+ supported */
+ if (audpp->decoder_count > (max_instance + 1)) {
+ MM_ERR("Can not support, already reached max\n");
+ audpp->decoder_count--;
+ audpp->codec_cnt[codec_type]--;
+ goto done;
+ }
+ audpp->codec_max_instances = max_instance;
+ MM_DBG("different codec running\n");
+ } else {
+ max_instance = (dec_instance_list + audpp->op_mode *
+ MSM_MAX_DEC_CNT +
+ codec_type)->
+ max_instances_same_dec;
+ /* if same codec type, should not cross maximum supported */
+ if (audpp->decoder_count > max_instance) {
+ MM_ERR("Can not support, already reached max\n");
+ audpp->decoder_count--;
+ audpp->codec_cnt[codec_type]--;
+ goto done;
+ }
+ audpp->codec_max_instances = max_instance;
+ MM_DBG("same codec running\n");
+ }
+
+ /* Point to Last entry of the row */
+ concurrency_entry = ((audpp->dec_database->dec_concurrency_table +
+ ((audpp->concurrency + 1) *
+ (audpp->dec_database->num_dec))) - 1);
+
+ lidx = audpp->dec_database->num_dec;
+ min_codecs_supported = sizeof(unsigned int) * 8;
+
+ MM_DBG("mode = 0x%08x codec = 0x%08x\n", mode, codec);
+
+ for (idx = lidx; idx > 0; idx--, concurrency_entry--) {
+ if (!(audpp->dec_inuse & (1 << (idx - 1)))) {
+ if (((mode & *concurrency_entry) == mode) &&
+ (codec & *concurrency_entry)) {
+ /* Check supports minimum number codecs */
+ codecs_supported =
+ audpp->dec_database->dec_info_list[idx -
+ 1].
+ nr_codec_support;
+ if (codecs_supported < min_codecs_supported) {
+ lidx = idx - 1;
+ min_codecs_supported = codecs_supported;
+ }
+ }
+ }
+ }
+
+ if (lidx < audpp->dec_database->num_dec) {
+ audpp->dec_inuse |= (1 << lidx);
+ *module_name =
+ audpp->dec_database->dec_info_list[lidx].module_name;
+ *queueid =
+ audpp->dec_database->dec_info_list[lidx].module_queueid;
+ decid = audpp->dec_database->dec_info_list[lidx].module_decid;
+ audpp->dec_info_table[lidx].codec =
+ (dec_attrb & AUDPP_CODEC_MASK);
+ audpp->dec_info_table[lidx].pid = current->pid;
+ /* point to row to get supported operation */
+ concurrency_entry =
+ ((audpp->dec_database->dec_concurrency_table +
+ ((audpp->concurrency) * (audpp->dec_database->num_dec))) +
+ lidx);
+ decid |= ((*concurrency_entry & AUDPP_OP_MASK) >> 12);
+ MM_INFO("decid =0x%08x module_name=%s, queueid=%d \n", decid,
+ *module_name, *queueid);
+ }
+done:
+ mutex_unlock(audpp->lock_dec);
+ return decid;
+
+}
+EXPORT_SYMBOL(audpp_adec_alloc);
+
+void audpp_adec_free(int decid)
+{
+ struct audpp_state *audpp = &the_audpp_state;
+ int idx;
+ mutex_lock(audpp->lock_dec);
+ for (idx = audpp->dec_database->num_dec; idx > 0; idx--) {
+ if (audpp->dec_database->dec_info_list[idx - 1].module_decid ==
+ decid) {
+ audpp->decoder_count--;
+ audpp->\
+ codec_cnt[audpp->dec_info_table[idx - 1].codec]--;
+ audpp->dec_inuse &= ~(1 << (idx - 1));
+ audpp->dec_info_table[idx - 1].codec = -1;
+ audpp->dec_info_table[idx - 1].pid = 0;
+ MM_INFO("free decid =%d \n", decid);
+ break;
+ }
+ }
+ mutex_unlock(audpp->lock_dec);
+ return;
+
+}
+EXPORT_SYMBOL(audpp_adec_free);
+
+static ssize_t concurrency_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct audpp_state *audpp = &the_audpp_state;
+ int rc;
+ mutex_lock(audpp->lock_dec);
+ rc = sprintf(buf, "%ld\n", audpp->concurrency);
+ mutex_unlock(audpp->lock_dec);
+ return rc;
+}
+
+static ssize_t concurrency_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct audpp_state *audpp = &the_audpp_state;
+ unsigned long concurrency;
+ int rc = -1;
+ mutex_lock(audpp->lock_dec);
+ if (audpp->dec_inuse) {
+ MM_ERR("Can not change profile, while playback in progress\n");
+ goto done;
+ }
+ rc = strict_strtoul(buf, 10, &concurrency);
+ if (!rc &&
+ (concurrency < audpp->dec_database->num_concurrency_support)) {
+ audpp->concurrency = concurrency;
+ MM_DBG("Concurrency case %ld\n", audpp->concurrency);
+ rc = count;
+ } else {
+ MM_ERR("Not a valid Concurrency case\n");
+ rc = -EINVAL;
+ }
+done:
+ mutex_unlock(audpp->lock_dec);
+ return rc;
+}
+
+static ssize_t decoder_info_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static struct device_attribute dev_attr_decoder[AUDPP_MAX_DECODER_CNT] = {
+ __ATTR(decoder0, S_IRUGO, decoder_info_show, NULL),
+ __ATTR(decoder1, S_IRUGO, decoder_info_show, NULL),
+ __ATTR(decoder2, S_IRUGO, decoder_info_show, NULL),
+ __ATTR(decoder3, S_IRUGO, decoder_info_show, NULL),
+ __ATTR(decoder4, S_IRUGO, decoder_info_show, NULL),
+};
+
+static ssize_t decoder_info_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int cpy_sz = 0;
+ struct audpp_state *audpp = &the_audpp_state;
+ const ptrdiff_t off = attr - dev_attr_decoder; /* decoder number */
+ mutex_lock(audpp->lock_dec);
+ cpy_sz += scnprintf(buf + cpy_sz, PAGE_SIZE - cpy_sz, "%d:",
+ audpp->dec_info_table[off].codec);
+ cpy_sz += scnprintf(buf + cpy_sz, PAGE_SIZE - cpy_sz, "%d\n",
+ audpp->dec_info_table[off].pid);
+ mutex_unlock(audpp->lock_dec);
+ return cpy_sz;
+}
+
+static DEVICE_ATTR(concurrency, S_IWUSR | S_IRUGO, concurrency_show,
+ concurrency_store);
+static int audpp_probe(struct platform_device *pdev)
+{
+ int rc, idx;
+ struct audpp_state *audpp = &the_audpp_state;
+ audpp->concurrency = AUDPP_CONCURRENCY_DEFAULT;
+ audpp->dec_database =
+ (struct msm_adspdec_database *)pdev->dev.platform_data;
+
+ MM_INFO("Number of decoder supported %d\n",
+ audpp->dec_database->num_dec);
+ MM_INFO("Number of concurrency supported %d\n",
+ audpp->dec_database->num_concurrency_support);
+ init_waitqueue_head(&audpp->event_wait);
+ for (idx = 0; idx < audpp->dec_database->num_dec; idx++) {
+ audpp->dec_info_table[idx].codec = -1;
+ audpp->dec_info_table[idx].pid = 0;
+ MM_INFO("module_name:%s\n",
+ audpp->dec_database->dec_info_list[idx].module_name);
+ MM_INFO("queueid:%d\n",
+ audpp->dec_database->dec_info_list[idx].module_queueid);
+ MM_INFO("decid:%d\n",
+ audpp->dec_database->dec_info_list[idx].module_decid);
+ MM_INFO("nr_codec_support:%d\n",
+ audpp->dec_database->dec_info_list[idx].
+ nr_codec_support);
+ }
+
+ wake_lock_init(&audpp_wake_lock, WAKE_LOCK_SUSPEND, "audpp");
+ for (idx = 0; idx < audpp->dec_database->num_dec; idx++) {
+ rc = device_create_file(&pdev->dev, &dev_attr_decoder[idx]);
+ if (rc)
+ goto err;
+ }
+ rc = device_create_file(&pdev->dev, &dev_attr_concurrency);
+ audpp->op_mode = 0; /* Consider as non turbo mode */
+ if (rc)
+ goto err;
+ else
+ goto done;
+err:
+ while (idx--)
+ device_remove_file(&pdev->dev, &dev_attr_decoder[idx]);
+done:
+ return rc;
+}
+
+static struct platform_driver audpp_plat_driver = {
+ .probe = audpp_probe,
+ .driver = {
+ .name = "msm_adspdec",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init audpp_init(void)
+{
+ return platform_driver_register(&audpp_plat_driver);
+}
+
+device_initcall(audpp_init);
diff --git a/arch/arm/mach-msm/qdsp5v2/audpreproc.c b/arch/arm/mach-msm/qdsp5v2/audpreproc.c
new file mode 100644
index 0000000..09611a5
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audpreproc.c
@@ -0,0 +1,523 @@
+/*
+ * Common code to deal with the AUDPREPROC dsp task (audio preprocessing)
+ *
+ * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Based on the audpp layer in arch/arm/mach-msm/qdsp5/audpp.c
+ *
+ * Copyright (C) 2008 Google, 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/wakelock.h>
+#include <mach/msm_adsp.h>
+#include <mach/qdsp5v2/audio_acdbi.h>
+#include <mach/qdsp5v2/audpreproc.h>
+#include <mach/debug_mm.h>
+#include <mach/qdsp5v2/qdsp5audpreprocmsg.h>
+
+static DEFINE_MUTEX(audpreproc_lock);
+static struct wake_lock audpre_wake_lock;
+static struct wake_lock audpre_idle_wake_lock;
+
+struct msm_adspenc_info {
+ const char *module_name;
+ unsigned module_queueids;
+ int module_encid; /* streamid */
+ int enc_formats; /* supported formats */
+ int nr_codec_support; /* number of codec suported */
+};
+
+#define ENC_MODULE_INFO(name, queueids, encid, formats, nr_codec) \
+ {.module_name = name, .module_queueids = queueids, \
+ .module_encid = encid, .enc_formats = formats, \
+ .nr_codec_support = nr_codec }
+
+#define MAX_EVENT_CALLBACK_CLIENTS 1
+
+#define ENC0_FORMAT ((1<<MSM_ADSP_ENC_CODEC_WAV)| \
+ (1<<MSM_ADSP_ENC_CODEC_SBC) | (1<<MSM_ADSP_ENC_CODEC_EXT_WAV))
+
+#define ENC1_FORMAT ((1<<MSM_ADSP_ENC_CODEC_WAV)| \
+ (1<<MSM_ADSP_ENC_CODEC_AAC) | (1<<MSM_ADSP_ENC_CODEC_AMRNB) | \
+ (1<<MSM_ADSP_ENC_CODEC_EVRC) | (1<<MSM_ADSP_ENC_CODEC_QCELP) | \
+ (1<<MSM_ADSP_ENC_CODEC_EXT_WAV))
+
+#define ENC2_FORMAT ((1<<MSM_ADSP_ENC_CODEC_WAV) | \
+ (1<<MSM_ADSP_ENC_CODEC_EXT_WAV))
+
+struct msm_adspenc_database {
+ unsigned num_enc;
+ struct msm_adspenc_info *enc_info_list;
+};
+
+static struct msm_adspenc_info enc_info_list[] = {
+ ENC_MODULE_INFO("AUDREC0TASK", \
+ ((QDSP_uPAudRec0BitStreamQueue << 16)| \
+ QDSP_uPAudRec0CmdQueue), 0, \
+ (ENC0_FORMAT | (1 << MSM_ADSP_ENC_MODE_TUNNEL)), 3),
+
+ ENC_MODULE_INFO("AUDREC1TASK", \
+ ((QDSP_uPAudRec1BitStreamQueue << 16)| \
+ QDSP_uPAudRec1CmdQueue), 1, \
+ (ENC1_FORMAT | (1 << MSM_ADSP_ENC_MODE_TUNNEL) | \
+ (1 << MSM_ADSP_ENC_MODE_NON_TUNNEL)), 6),
+
+ ENC_MODULE_INFO("AUDREC2TASK", \
+ ((QDSP_uPAudRec2BitStreamQueue << 16)| \
+ QDSP_uPAudRec2CmdQueue), 2, \
+ (ENC2_FORMAT | (1 << MSM_ADSP_ENC_MODE_TUNNEL)), 2),
+
+};
+
+static struct msm_adspenc_database msm_enc_database = {
+ .num_enc = ARRAY_SIZE(enc_info_list),
+ .enc_info_list = enc_info_list,
+};
+
+
+static struct audrec_session_info
+ session_info[MAX_ENC_COUNT] = { {0, 0}, {0, 0}, {0, 0} };
+
+struct audpreproc_state {
+ struct msm_adsp_module *mod;
+ audpreproc_event_func func[MAX_ENC_COUNT];
+ void *private[MAX_ENC_COUNT];
+ struct mutex *lock;
+ unsigned open_count;
+ unsigned enc_inuse;
+ struct audpreproc_event_callback *cb_tbl[MAX_EVENT_CALLBACK_CLIENTS];
+};
+
+static struct audpreproc_state the_audpreproc_state = {
+ .lock = &audpreproc_lock,
+};
+
+static inline void prevent_suspend(void)
+{
+ wake_lock(&audpre_wake_lock);
+ wake_lock(&audpre_idle_wake_lock);
+}
+static inline void allow_suspend(void)
+{
+ wake_unlock(&audpre_wake_lock);
+ wake_unlock(&audpre_idle_wake_lock);
+}
+
+/* DSP preproc event handler */
+static void audpreproc_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent)(void *ptr, size_t len))
+{
+ struct audpreproc_state *audpreproc = data;
+ int n = 0;
+
+ switch (id) {
+ case AUDPREPROC_CMD_CFG_DONE_MSG: {
+ struct audpreproc_cmd_cfg_done_msg cfg_done_msg;
+
+ getevent(&cfg_done_msg, AUDPREPROC_CMD_CFG_DONE_MSG_LEN);
+ MM_DBG("AUDPREPROC_CMD_CFG_DONE_MSG: stream id %d preproc \
+ type %x\n", cfg_done_msg.stream_id, \
+ cfg_done_msg.aud_preproc_type);
+ if ((cfg_done_msg.stream_id < MAX_ENC_COUNT) &&
+ audpreproc->func[cfg_done_msg.stream_id])
+ audpreproc->func[cfg_done_msg.stream_id](
+ audpreproc->private[cfg_done_msg.stream_id], id,
+ &cfg_done_msg);
+ break;
+ }
+ case AUDPREPROC_ERROR_MSG: {
+ struct audpreproc_err_msg err_msg;
+
+ getevent(&err_msg, AUDPREPROC_ERROR_MSG_LEN);
+ MM_DBG("AUDPREPROC_ERROR_MSG: stream id %d err idx %d\n",
+ err_msg.stream_id, err_msg.aud_preproc_err_idx);
+ if ((err_msg.stream_id < MAX_ENC_COUNT) &&
+ audpreproc->func[err_msg.stream_id])
+ audpreproc->func[err_msg.stream_id](
+ audpreproc->private[err_msg.stream_id], id,
+ &err_msg);
+ break;
+ }
+ case AUDPREPROC_CMD_ENC_CFG_DONE_MSG: {
+ struct audpreproc_cmd_enc_cfg_done_msg enc_cfg_msg;
+
+ getevent(&enc_cfg_msg, AUDPREPROC_CMD_ENC_CFG_DONE_MSG_LEN);
+ MM_DBG("AUDPREPROC_CMD_ENC_CFG_DONE_MSG: stream id %d enc type \
+ %d\n", enc_cfg_msg.stream_id, enc_cfg_msg.rec_enc_type);
+ if ((enc_cfg_msg.stream_id < MAX_ENC_COUNT) &&
+ audpreproc->func[enc_cfg_msg.stream_id])
+ audpreproc->func[enc_cfg_msg.stream_id](
+ audpreproc->private[enc_cfg_msg.stream_id], id,
+ &enc_cfg_msg);
+ for (n = 0; n < MAX_EVENT_CALLBACK_CLIENTS; ++n) {
+ if (audpreproc->cb_tbl[n] &&
+ audpreproc->cb_tbl[n]->fn) {
+ audpreproc->cb_tbl[n]->fn( \
+ audpreproc->cb_tbl[n]->private, \
+ id, (void *) &enc_cfg_msg);
+ }
+ }
+ break;
+ }
+ case AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG: {
+ struct audpreproc_cmd_enc_param_cfg_done_msg enc_param_msg;
+
+ getevent(&enc_param_msg,
+ AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG_LEN);
+ MM_DBG("AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG: stream id %d\n",
+ enc_param_msg.stream_id);
+ if ((enc_param_msg.stream_id < MAX_ENC_COUNT) &&
+ audpreproc->func[enc_param_msg.stream_id])
+ audpreproc->func[enc_param_msg.stream_id](
+ audpreproc->private[enc_param_msg.stream_id], id,
+ &enc_param_msg);
+ break;
+ }
+ case AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG: {
+ struct audpreproc_afe_cmd_audio_record_cfg_done
+ record_cfg_done;
+ getevent(&record_cfg_done,
+ AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG_LEN);
+ MM_DBG("AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG: \
+ stream id %d\n", record_cfg_done.stream_id);
+ if ((record_cfg_done.stream_id < MAX_ENC_COUNT) &&
+ audpreproc->func[record_cfg_done.stream_id])
+ audpreproc->func[record_cfg_done.stream_id](
+ audpreproc->private[record_cfg_done.stream_id], id,
+ &record_cfg_done);
+ break;
+ }
+ case AUDPREPROC_CMD_ROUTING_MODE_DONE_MSG: {
+ struct audpreproc_cmd_routing_mode_done routing_mode_done;
+
+ getevent(&routing_mode_done,
+ AUDPREPROC_CMD_ROUTING_MODE_DONE_MSG_LEN);
+ MM_DBG("AUDPREPROC_CMD_ROUTING_MODE_DONE_MSG: \
+ stream id %d\n", routing_mode_done.stream_id);
+ if ((routing_mode_done.stream_id < MAX_ENC_COUNT) &&
+ audpreproc->func[routing_mode_done.stream_id])
+ audpreproc->func[routing_mode_done.stream_id](
+ audpreproc->private[routing_mode_done.stream_id], id,
+ &routing_mode_done);
+ break;
+ }
+#ifdef CONFIG_DEBUG_FS
+ case AUDPREPROC_MSG_FEAT_QUERY_DM_DONE:
+ {
+ uint16_t msg[3];
+ getevent(msg, sizeof(msg));
+ MM_INFO("RTC ACK --> %x %x %x\n", msg[0], msg[1], msg[2]);
+ acdb_rtc_set_err(msg[2]);
+ }
+ break;
+#endif
+ case ADSP_MESSAGE_ID: {
+ MM_DBG("Received ADSP event:module audpreproctask\n");
+ break;
+ }
+ default:
+ MM_ERR("Unknown Event %d\n", id);
+ }
+ return;
+}
+
+static struct msm_adsp_ops adsp_ops = {
+ .event = audpreproc_dsp_event,
+};
+
+/* EXPORTED API's */
+int audpreproc_enable(int enc_id, audpreproc_event_func func, void *private)
+{
+ struct audpreproc_state *audpreproc = &the_audpreproc_state;
+ int res = 0;
+
+ if (enc_id < 0 || enc_id > (MAX_ENC_COUNT - 1))
+ return -EINVAL;
+
+ mutex_lock(audpreproc->lock);
+ if (audpreproc->func[enc_id]) {
+ res = -EBUSY;
+ goto out;
+ }
+
+ audpreproc->func[enc_id] = func;
+ audpreproc->private[enc_id] = private;
+
+ /* First client to enable preproc task */
+ if (audpreproc->open_count++ == 0) {
+ MM_DBG("Get AUDPREPROCTASK\n");
+ res = msm_adsp_get("AUDPREPROCTASK", &audpreproc->mod,
+ &adsp_ops, audpreproc);
+ if (res < 0) {
+ MM_ERR("Can not get AUDPREPROCTASK\n");
+ audpreproc->open_count = 0;
+ audpreproc->func[enc_id] = NULL;
+ audpreproc->private[enc_id] = NULL;
+ goto out;
+ }
+ prevent_suspend();
+ if (msm_adsp_enable(audpreproc->mod)) {
+ MM_ERR("Can not enable AUDPREPROCTASK\n");
+ audpreproc->open_count = 0;
+ audpreproc->func[enc_id] = NULL;
+ audpreproc->private[enc_id] = NULL;
+ msm_adsp_put(audpreproc->mod);
+ audpreproc->mod = NULL;
+ res = -ENODEV;
+ allow_suspend();
+ goto out;
+ }
+ }
+ res = 0;
+out:
+ mutex_unlock(audpreproc->lock);
+ return res;
+}
+EXPORT_SYMBOL(audpreproc_enable);
+
+int audpreproc_update_audrec_info(
+ struct audrec_session_info *audrec_session_info)
+{
+ if (!audrec_session_info) {
+ MM_ERR("error in audrec session info address\n");
+ return -EINVAL;
+ }
+ if (audrec_session_info->session_id < MAX_ENC_COUNT) {
+ memcpy(&session_info[audrec_session_info->session_id],
+ audrec_session_info,
+ sizeof(struct audrec_session_info));
+ return 0;
+ }
+ return -EINVAL;
+}
+EXPORT_SYMBOL(audpreproc_update_audrec_info);
+
+void audpreproc_disable(int enc_id, void *private)
+{
+ struct audpreproc_state *audpreproc = &the_audpreproc_state;
+
+ if (enc_id < 0 || enc_id > (MAX_ENC_COUNT - 1))
+ return;
+
+ mutex_lock(audpreproc->lock);
+ if (!audpreproc->func[enc_id])
+ goto out;
+ if (audpreproc->private[enc_id] != private)
+ goto out;
+
+ audpreproc->func[enc_id] = NULL;
+ audpreproc->private[enc_id] = NULL;
+
+ /* Last client then disable preproc task */
+ if (--audpreproc->open_count == 0) {
+ msm_adsp_disable(audpreproc->mod);
+ MM_DBG("Put AUDPREPROCTASK\n");
+ msm_adsp_put(audpreproc->mod);
+ audpreproc->mod = NULL;
+ allow_suspend();
+ }
+out:
+ mutex_unlock(audpreproc->lock);
+ return;
+}
+EXPORT_SYMBOL(audpreproc_disable);
+
+
+int audpreproc_register_event_callback(struct audpreproc_event_callback *ecb)
+{
+ struct audpreproc_state *audpreproc = &the_audpreproc_state;
+ int i;
+
+ for (i = 0; i < MAX_EVENT_CALLBACK_CLIENTS; ++i) {
+ if (NULL == audpreproc->cb_tbl[i]) {
+ audpreproc->cb_tbl[i] = ecb;
+ return 0;
+ }
+ }
+ return -1;
+}
+EXPORT_SYMBOL(audpreproc_register_event_callback);
+
+int audpreproc_unregister_event_callback(struct audpreproc_event_callback *ecb)
+{
+ struct audpreproc_state *audpreproc = &the_audpreproc_state;
+ int i;
+
+ for (i = 0; i < MAX_EVENT_CALLBACK_CLIENTS; ++i) {
+ if (ecb == audpreproc->cb_tbl[i]) {
+ audpreproc->cb_tbl[i] = NULL;
+ return 0;
+ }
+ }
+ return -1;
+}
+EXPORT_SYMBOL(audpreproc_unregister_event_callback);
+
+
+/* enc_type = supported encode format *
+ * like pcm, aac, sbc, evrc, qcelp, amrnb etc ... *
+ */
+int audpreproc_aenc_alloc(unsigned enc_type, const char **module_name,
+ unsigned *queue_ids)
+{
+ struct audpreproc_state *audpreproc = &the_audpreproc_state;
+ int encid = -1, idx, lidx, mode, codec;
+ int codecs_supported, min_codecs_supported;
+ static int wakelock_init;
+
+ mutex_lock(audpreproc->lock);
+ /* Represents in bit mask */
+ mode = ((enc_type & AUDPREPROC_MODE_MASK) << 16);
+ codec = (1 << (enc_type & AUDPREPROC_CODEC_MASK));
+
+ lidx = msm_enc_database.num_enc;
+ min_codecs_supported = sizeof(unsigned int) * 8;
+ MM_DBG("mode = 0x%08x codec = 0x%08x\n", mode, codec);
+
+ for (idx = lidx-1; idx >= 0; idx--) {
+ /* encoder free and supports the format */
+ if (!(audpreproc->enc_inuse & (1 << (idx))) &&
+ ((mode & msm_enc_database.enc_info_list[idx].enc_formats)
+ == mode) && ((codec &
+ msm_enc_database.enc_info_list[idx].enc_formats)
+ == codec)){
+ /* Check supports minimum number codecs */
+ codecs_supported =
+ msm_enc_database.enc_info_list[idx].nr_codec_support;
+ if (codecs_supported < min_codecs_supported) {
+ lidx = idx;
+ min_codecs_supported = codecs_supported;
+ }
+ }
+ }
+
+ if (lidx < msm_enc_database.num_enc) {
+ audpreproc->enc_inuse |= (1 << lidx);
+ *module_name =
+ msm_enc_database.enc_info_list[lidx].module_name;
+ *queue_ids =
+ msm_enc_database.enc_info_list[lidx].module_queueids;
+ encid = msm_enc_database.enc_info_list[lidx].module_encid;
+ }
+
+ if (!wakelock_init) {
+ wake_lock_init(&audpre_wake_lock, WAKE_LOCK_SUSPEND, "audpre");
+ wake_lock_init(&audpre_idle_wake_lock, WAKE_LOCK_IDLE,
+ "audpre_idle");
+ wakelock_init = 1;
+ }
+
+ mutex_unlock(audpreproc->lock);
+ return encid;
+}
+EXPORT_SYMBOL(audpreproc_aenc_alloc);
+
+void audpreproc_aenc_free(int enc_id)
+{
+ struct audpreproc_state *audpreproc = &the_audpreproc_state;
+ int idx;
+
+ mutex_lock(audpreproc->lock);
+ for (idx = 0; idx < msm_enc_database.num_enc; idx++) {
+ if (msm_enc_database.enc_info_list[idx].module_encid ==
+ enc_id) {
+ audpreproc->enc_inuse &= ~(1 << idx);
+ break;
+ }
+ }
+ mutex_unlock(audpreproc->lock);
+ return;
+
+}
+EXPORT_SYMBOL(audpreproc_aenc_free);
+
+int audpreproc_send_preproccmdqueue(void *cmd, unsigned len)
+{
+ return msm_adsp_write(the_audpreproc_state.mod,
+ QDSP_uPAudPreProcCmdQueue, cmd, len);
+}
+EXPORT_SYMBOL(audpreproc_send_preproccmdqueue);
+
+int audpreproc_send_audreccmdqueue(void *cmd, unsigned len)
+{
+ return msm_adsp_write(the_audpreproc_state.mod,
+ QDSP_uPAudPreProcAudRecCmdQueue, cmd, len);
+}
+EXPORT_SYMBOL(audpreproc_send_audreccmdqueue);
+
+int audpreproc_send_audrec2cmdqueue(void *cmd, unsigned len)
+{
+ return msm_adsp_write(the_audpreproc_state.mod,
+ QDSP_uPAudRec2CmdQueue, cmd, len);
+}
+EXPORT_SYMBOL(audpreproc_send_audrec2cmdqueue);
+
+int audpreproc_dsp_set_agc(struct audpreproc_cmd_cfg_agc_params *agc,
+ unsigned len)
+{
+ return msm_adsp_write(the_audpreproc_state.mod,
+ QDSP_uPAudPreProcCmdQueue, agc, len);
+}
+EXPORT_SYMBOL(audpreproc_dsp_set_agc);
+
+int audpreproc_dsp_set_agc2(struct audpreproc_cmd_cfg_agc_params_2 *agc2,
+ unsigned len)
+{
+ return msm_adsp_write(the_audpreproc_state.mod,
+ QDSP_uPAudPreProcCmdQueue, agc2, len);
+}
+EXPORT_SYMBOL(audpreproc_dsp_set_agc2);
+
+int audpreproc_dsp_set_ns(struct audpreproc_cmd_cfg_ns_params *ns,
+ unsigned len)
+{
+ return msm_adsp_write(the_audpreproc_state.mod,
+ QDSP_uPAudPreProcCmdQueue, ns, len);
+}
+EXPORT_SYMBOL(audpreproc_dsp_set_ns);
+
+int audpreproc_dsp_set_iir(
+struct audpreproc_cmd_cfg_iir_tuning_filter_params *iir, unsigned len)
+{
+ return msm_adsp_write(the_audpreproc_state.mod,
+ QDSP_uPAudPreProcCmdQueue, iir, len);
+}
+EXPORT_SYMBOL(audpreproc_dsp_set_iir);
+
+int audpreproc_dsp_set_gain_tx(
+ struct audpreproc_cmd_cfg_cal_gain *calib_gain_tx, unsigned len)
+{
+ return msm_adsp_write(the_audpreproc_state.mod,
+ QDSP_uPAudPreProcCmdQueue, calib_gain_tx, len);
+}
+EXPORT_SYMBOL(audpreproc_dsp_set_gain_tx);
+
+void get_audrec_session_info(int id, struct audrec_session_info *info)
+{
+ if (id >= MAX_ENC_COUNT) {
+ MM_ERR("invalid session id = %d\n", id);
+ return;
+ }
+ memcpy(info, &session_info[id], sizeof(struct audrec_session_info));
+}
+EXPORT_SYMBOL(get_audrec_session_info);
+
+int audpreproc_dsp_set_lvnv(
+ struct audpreproc_cmd_cfg_lvnv_param *preproc_lvnv, unsigned len)
+{
+ return msm_adsp_write(the_audpreproc_state.mod,
+ QDSP_uPAudPreProcCmdQueue, preproc_lvnv, len);
+}
+EXPORT_SYMBOL(audpreproc_dsp_set_lvnv);
+
diff --git a/arch/arm/mach-msm/qdsp5v2/aux_pcm.c b/arch/arm/mach-msm/qdsp5v2/aux_pcm.c
new file mode 100644
index 0000000..712766d
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/aux_pcm.c
@@ -0,0 +1,280 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <mach/qdsp5v2/aux_pcm.h>
+#include <mach/gpio.h>
+#include <linux/delay.h>
+#include <mach/debug_mm.h>
+
+/*----------------------------------------------------------------------------
+ * Preprocessor Definitions and Constants
+ * -------------------------------------------------------------------------*/
+
+/* define offset of registers here, may put them into platform data */
+#define AUX_CODEC_CTL_OFFSET 0x00
+#define PCM_PATH_CTL_OFFSET 0x04
+#define AUX_CODEC_CTL_OUT_OFFSET 0x08
+
+/* define some bit values in PCM_PATH_CTL register */
+#define PCM_PATH_CTL__ADSP_CTL_EN_BMSK 0x8
+
+/* mask and shift */
+#define AUX_CODEC_CTL_ADSP_CODEC_CTL_EN_BMSK 0x800
+#define AUX_CODEC_CTL_PCM_SYNC_LONG_BMSK 0x400
+#define AUX_CODEC_CTL_PCM_SYNC_SHORT_BMSK 0x200
+#define AUX_CODEC_CTL_I2S_SAMPLE_CLK_SRC_BMSK 0x80
+#define AUX_CODEC_CTL_I2S_SAMPLE_CLK_MODE_BMSK 0x40
+#define AUX_CODEC_CTL_I2S_RX_MODE_BMSK 0x20
+#define AUX_CODEC_CTL_I2S_CLK_MODE_BMSK 0x10
+#define AUX_CODEC_CTL_AUX_PCM_MODE_BMSK 0x0b
+#define AUX_CODEC_CTL_AUX_CODEC_MODE_BMSK 0x02
+
+/* AUX PCM MODE */
+#define MASTER_PRIM_PCM_SHORT 0
+#define MASTER_AUX_PCM_LONG 1
+#define SLAVE_PRIM_PCM_SHORT 2
+
+struct aux_pcm_state {
+ void __iomem *aux_pcm_base; /* configure aux pcm through Scorpion */
+ int dout;
+ int din;
+ int syncout;
+ int clkin_a;
+};
+
+static struct aux_pcm_state the_aux_pcm_state;
+
+static void __iomem *get_base_addr(struct aux_pcm_state *aux_pcm)
+{
+ return aux_pcm->aux_pcm_base;
+}
+
+/* Set who control aux pcm : adsp or MSM */
+void aux_codec_adsp_codec_ctl_en(bool msm_adsp_en)
+{
+ void __iomem *baddr = get_base_addr(&the_aux_pcm_state);
+ uint32_t val;
+
+ if (!IS_ERR(baddr)) {
+ val = readl(baddr + AUX_CODEC_CTL_OFFSET);
+ if (msm_adsp_en) { /* adsp */
+ writel(
+ ((val & ~AUX_CODEC_CTL_ADSP_CODEC_CTL_EN_BMSK) |
+ AUX_CODEC_CTL__ADSP_CODEC_CTL_EN__ADSP_V),
+ baddr + AUX_CODEC_CTL_OFFSET);
+ } else { /* MSM */
+ writel(
+ ((val & ~AUX_CODEC_CTL_ADSP_CODEC_CTL_EN_BMSK) |
+ AUX_CODEC_CTL__ADSP_CODEC_CTL_EN__MSM_V),
+ baddr + AUX_CODEC_CTL_OFFSET);
+ }
+ }
+ mb();
+}
+
+/* Set who control aux pcm path: adsp or MSM */
+void aux_codec_pcm_path_ctl_en(bool msm_adsp_en)
+{
+ void __iomem *baddr = get_base_addr(&the_aux_pcm_state);
+ uint32_t val;
+
+ if (!IS_ERR(baddr)) {
+ val = readl(baddr + PCM_PATH_CTL_OFFSET);
+ if (msm_adsp_en) { /* adsp */
+ writel(
+ ((val & ~PCM_PATH_CTL__ADSP_CTL_EN_BMSK) |
+ PCM_PATH_CTL__ADSP_CTL_EN__ADSP_V),
+ baddr + PCM_PATH_CTL_OFFSET);
+ } else { /* MSM */
+ writel(
+ ((val & ~PCM_PATH_CTL__ADSP_CTL_EN_BMSK) |
+ PCM_PATH_CTL__ADSP_CTL_EN__MSM_V),
+ baddr + PCM_PATH_CTL_OFFSET);
+ }
+ }
+ mb();
+ return;
+}
+EXPORT_SYMBOL(aux_codec_pcm_path_ctl_en);
+
+int aux_pcm_gpios_request(void)
+{
+ int rc = 0;
+
+ MM_DBG("aux_pcm_gpios_request\n");
+ rc = gpio_request(the_aux_pcm_state.dout, "AUX PCM DOUT");
+ if (rc) {
+ MM_ERR("GPIO request for AUX PCM DOUT failed\n");
+ return rc;
+ }
+
+ rc = gpio_request(the_aux_pcm_state.din, "AUX PCM DIN");
+ if (rc) {
+ MM_ERR("GPIO request for AUX PCM DIN failed\n");
+ gpio_free(the_aux_pcm_state.dout);
+ return rc;
+ }
+
+ rc = gpio_request(the_aux_pcm_state.syncout, "AUX PCM SYNC OUT");
+ if (rc) {
+ MM_ERR("GPIO request for AUX PCM SYNC OUT failed\n");
+ gpio_free(the_aux_pcm_state.dout);
+ gpio_free(the_aux_pcm_state.din);
+ return rc;
+ }
+
+ rc = gpio_request(the_aux_pcm_state.clkin_a, "AUX PCM CLKIN A");
+ if (rc) {
+ MM_ERR("GPIO request for AUX PCM CLKIN A failed\n");
+ gpio_free(the_aux_pcm_state.dout);
+ gpio_free(the_aux_pcm_state.din);
+ gpio_free(the_aux_pcm_state.syncout);
+ return rc;
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(aux_pcm_gpios_request);
+
+
+void aux_pcm_gpios_free(void)
+{
+ MM_DBG(" aux_pcm_gpios_free \n");
+
+ /*
+ * Feed silence frames before close to prevent buzzing sound in BT at
+ * call end. This fix is applicable only to Marimba BT.
+ */
+ gpio_tlmm_config(GPIO_CFG(the_aux_pcm_state.dout, 0, GPIO_CFG_OUTPUT,
+ GPIO_CFG_NO_PULL, GPIO_CFG_2MA), GPIO_CFG_ENABLE);
+ gpio_set_value(the_aux_pcm_state.dout, 0);
+ msleep(20);
+ gpio_tlmm_config(GPIO_CFG(the_aux_pcm_state.dout, 1, GPIO_CFG_OUTPUT,
+ GPIO_CFG_NO_PULL, GPIO_CFG_2MA), GPIO_CFG_ENABLE);
+
+ gpio_free(the_aux_pcm_state.dout);
+ gpio_free(the_aux_pcm_state.din);
+ gpio_free(the_aux_pcm_state.syncout);
+ gpio_free(the_aux_pcm_state.clkin_a);
+}
+EXPORT_SYMBOL(aux_pcm_gpios_free);
+
+
+static int get_aux_pcm_gpios(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct resource *res;
+
+ /* Claim all of the GPIOs. */
+ res = platform_get_resource_byname(pdev, IORESOURCE_IO,
+ "aux_pcm_dout");
+ if (!res) {
+ MM_ERR("%s: failed to get gpio AUX PCM DOUT\n", __func__);
+ return -ENODEV;
+ }
+
+ the_aux_pcm_state.dout = res->start;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_IO,
+ "aux_pcm_din");
+ if (!res) {
+ MM_ERR("%s: failed to get gpio AUX PCM DIN\n", __func__);
+ return -ENODEV;
+ }
+
+ the_aux_pcm_state.din = res->start;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_IO,
+ "aux_pcm_syncout");
+ if (!res) {
+ MM_ERR("%s: failed to get gpio AUX PCM SYNC OUT\n", __func__);
+ return -ENODEV;
+ }
+
+ the_aux_pcm_state.syncout = res->start;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_IO,
+ "aux_pcm_clkin_a");
+ if (!res) {
+ MM_ERR("%s: failed to get gpio AUX PCM CLKIN A\n", __func__);
+ return -ENODEV;
+ }
+
+ the_aux_pcm_state.clkin_a = res->start;
+
+ return rc;
+}
+static int aux_pcm_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct resource *mem_src;
+
+ MM_DBG("aux_pcm_probe \n");
+ mem_src = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "aux_codec_reg_addr");
+ if (!mem_src) {
+ rc = -ENODEV;
+ goto done;
+ }
+
+ the_aux_pcm_state.aux_pcm_base = ioremap(mem_src->start,
+ (mem_src->end - mem_src->start) + 1);
+ if (!the_aux_pcm_state.aux_pcm_base) {
+ rc = -ENOMEM;
+ goto done;
+ }
+ rc = get_aux_pcm_gpios(pdev);
+ if (rc) {
+ MM_ERR("GPIO configuration failed\n");
+ rc = -ENODEV;
+ }
+
+done: return rc;
+
+}
+
+static int aux_pcm_remove(struct platform_device *pdev)
+{
+ iounmap(the_aux_pcm_state.aux_pcm_base);
+ return 0;
+}
+
+static struct platform_driver aux_pcm_driver = {
+ .probe = aux_pcm_probe,
+ .remove = aux_pcm_remove,
+ .driver = {
+ .name = "msm_aux_pcm",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init aux_pcm_init(void)
+{
+
+ return platform_driver_register(&aux_pcm_driver);
+}
+
+static void __exit aux_pcm_exit(void)
+{
+ platform_driver_unregister(&aux_pcm_driver);
+}
+
+module_init(aux_pcm_init);
+module_exit(aux_pcm_exit);
+
+MODULE_DESCRIPTION("MSM AUX PCM driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/lpa.c b/arch/arm/mach-msm/qdsp5v2/lpa.c
new file mode 100644
index 0000000..c4e0fee
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/lpa.c
@@ -0,0 +1,608 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <mach/qdsp5v2/lpa.h>
+#include <mach/qdsp5v2/lpa_hw.h>
+#include <mach/qdsp5v2/msm_lpa.h>
+#include <mach/debug_mm.h>
+
+#define LPA_REG_WRITEL(drv, val, reg) writel(val, drv->baseaddr + reg)
+#define LPA_REG_READL(drv, reg) readl(drv->baseaddr + reg)
+
+/* bit 2:0 is reserved because watermarks have to be 64-bit aligned */
+#define LLB_WATERMARK_VAL_MASK 0x00000003
+
+#define LPA_STATUS_SBUF_EN 0x01
+
+struct lpa_drv {
+ void __iomem *baseaddr;
+ u32 obuf_hlb_size;
+ u32 dsp_proc_id;
+ u32 app_proc_id;
+ struct lpa_mem_config nosb_config;
+ struct lpa_mem_config sb_config;
+ u32 status;
+ u32 watermark_bytes;
+ u32 watermark_aheadtime;
+ u32 sample_boundary;
+};
+
+struct lpa_state {
+ struct lpa_drv lpa_drv; /* One instance for now */
+ u32 assigned;
+ struct mutex lpa_lock;
+};
+
+struct lpa_state the_lpa_state;
+
+static void lpa_enable_codec(struct lpa_drv *lpa, bool enable)
+{
+ u32 val;
+
+ val = LPA_REG_READL(lpa, LPA_OBUF_CODEC);
+ val = enable ? (val | LPA_OBUF_CODEC_CODEC_INTF_EN_BMSK) :
+ (val & ~LPA_OBUF_CODEC_CODEC_INTF_EN_BMSK);
+ val |= LPA_OBUF_CODEC_LOAD_BMSK;
+ LPA_REG_WRITEL(lpa, val, LPA_OBUF_CODEC);
+ mb();
+}
+
+static void lpa_reset(struct lpa_drv *lpa)
+{
+ u32 status;
+ struct clk *adsp_clk;
+ /* Need to make sure not disable clock while other device is enabled */
+ adsp_clk = clk_get(NULL, "adsp_clk");
+ if (!adsp_clk) {
+ MM_ERR("failed to get adsp clk\n");
+ goto error;
+ }
+ clk_enable(adsp_clk);
+ lpa_enable_codec(lpa, 0);
+ LPA_REG_WRITEL(lpa, (LPA_OBUF_RESETS_MISR_RESET |
+ LPA_OBUF_RESETS_OVERALL_RESET), LPA_OBUF_RESETS);
+ do {
+ status = LPA_REG_READL(lpa, LPA_OBUF_STATUS);
+ } while (!(status & LPA_OBUF_STATUS_RESET_DONE));
+
+ LPA_REG_WRITEL(lpa, LPA_OBUF_ACK_RESET_DONE_BMSK, LPA_OBUF_ACK);
+ mb();
+ clk_disable(adsp_clk);
+ clk_put(adsp_clk);
+error:
+ return;
+}
+
+static void lpa_config_hlb_addr(struct lpa_drv *lpa)
+{
+ u32 val, min_addr = 0, max_addr = min_addr + lpa->obuf_hlb_size;
+
+ val = (min_addr & LPA_OBUF_HLB_MIN_ADDR_SEG_BMSK) |
+ LPA_OBUF_HLB_MIN_ADDR_LOAD_BMSK;
+ LPA_REG_WRITEL(lpa, val, LPA_OBUF_HLB_MIN_ADDR);
+ val = max_addr & LPA_OBUF_HLB_MAX_ADDR_SEG_BMSK;
+ LPA_REG_WRITEL(lpa, val, LPA_OBUF_HLB_MAX_ADDR);
+}
+
+static void lpa_powerup_mem_bank(struct lpa_drv *lpa,
+ struct lpa_mem_bank_select *bank)
+{
+ u32 status, val;
+
+ status = LPA_REG_READL(lpa, LPA_OBUF_MEMORY_CONTROL);
+ val = ((*((u32 *) bank)) << LPA_OBUF_MEM_CTL_PWRUP_SHFT) &
+ LPA_OBUF_MEM_CTL_PWRUP_BMSK;
+ val |= status;
+ LPA_REG_WRITEL(lpa, val, LPA_OBUF_MEMORY_CONTROL);
+}
+
+static void lpa_enable_interrupt(struct lpa_drv *lpa, u32 proc_id)
+{
+ u32 val;
+
+ proc_id &= LPA_OBUF_INTR_EN_BMSK;
+ val = 0x1 << proc_id;
+ LPA_REG_WRITEL(lpa, val, LPA_OBUF_INTR_ENABLE);
+}
+
+static void lpa_config_llb_addr(struct lpa_drv *lpa, u32 min_addr, u32 max_addr)
+{
+ u32 val;
+
+ val = (min_addr & LPA_OBUF_LLB_MIN_ADDR_SEG_BMSK) |
+ LPA_OBUF_LLB_MIN_ADDR_LOAD_BMSK;
+ LPA_REG_WRITEL(lpa, val, LPA_OBUF_LLB_MIN_ADDR);
+ val = max_addr & LPA_OBUF_LLB_MAX_ADDR_SEG_BMSK;
+ LPA_REG_WRITEL(lpa, val, LPA_OBUF_LLB_MAX_ADDR);
+}
+
+static void lpa_config_sb_addr(struct lpa_drv *lpa, u32 min_addr, u32 max_addr)
+{
+ u32 val;
+
+ val = (min_addr & LPA_OBUF_SB_MIN_ADDR_SEG_BMSK) |
+ LPA_OBUF_SB_MIN_ADDR_LOAD_BMSK;
+ LPA_REG_WRITEL(lpa, val, LPA_OBUF_SB_MIN_ADDR);
+ val = max_addr & LPA_OBUF_SB_MAX_ADDR_SEG_BMSK;
+ LPA_REG_WRITEL(lpa, val, LPA_OBUF_SB_MAX_ADDR);
+}
+
+static void lpa_switch_sb(struct lpa_drv *lpa)
+{
+ if (lpa->status & LPA_STATUS_SBUF_EN) {
+ lpa_config_llb_addr(lpa, lpa->sb_config.llb_min_addr,
+ lpa->sb_config.llb_max_addr);
+ lpa_config_sb_addr(lpa, lpa->sb_config.sb_min_addr,
+ lpa->sb_config.sb_max_addr);
+ } else {
+ lpa_config_llb_addr(lpa, lpa->nosb_config.llb_min_addr,
+ lpa->nosb_config.llb_max_addr);
+ lpa_config_sb_addr(lpa, lpa->nosb_config.sb_min_addr,
+ lpa->nosb_config.sb_max_addr);
+ }
+}
+
+static u8 lpa_req_wmark_id(struct lpa_drv *lpa)
+{
+ return (u8) (LPA_REG_READL(lpa, LPA_OBUF_WMARK_ASSIGN) &
+ LPA_OBUF_WMARK_ASSIGN_BMSK);
+}
+
+static void lpa_enable_llb_wmark(struct lpa_drv *lpa, u32 wmark_ctrl,
+ u32 wmark_id, u32 cpu_id)
+{
+ u32 val;
+
+ wmark_id = (wmark_id > 3) ? 0 : wmark_id;
+ val = LPA_REG_READL(lpa, LPA_OBUF_WMARK_n_LLB_ADDR(wmark_id));
+ val &= ~LPA_OBUF_LLB_WMARK_CTRL_BMSK;
+ val &= ~LPA_OBUF_LLB_WMARK_MAP_BMSK;
+ val |= (wmark_ctrl << LPA_OBUF_LLB_WMARK_CTRL_SHFT) &
+ LPA_OBUF_LLB_WMARK_CTRL_BMSK;
+ val |= (cpu_id << LPA_OBUF_LLB_WMARK_MAP_SHFT) &
+ LPA_OBUF_LLB_WMARK_MAP_BMSK;
+ LPA_REG_WRITEL(lpa, val, LPA_OBUF_WMARK_n_LLB_ADDR(wmark_id));
+}
+
+static void lpa_enable_sb_wmark(struct lpa_drv *lpa, u32 wmark_ctrl,
+ u32 cpu_id)
+{
+ u32 val;
+
+ val = LPA_REG_READL(lpa, LPA_OBUF_WMARK_SB);
+ val &= ~LPA_OBUF_SB_WMARK_CTRL_BMSK;
+ val &= ~LPA_OBUF_SB_WMARK_MAP_BMSK;
+ val |= (wmark_ctrl << LPA_OBUF_SB_WMARK_CTRL_SHFT) &
+ LPA_OBUF_SB_WMARK_CTRL_BMSK;
+ val |= (cpu_id << LPA_OBUF_SB_WMARK_MAP_SHFT) &
+ LPA_OBUF_SB_WMARK_MAP_BMSK;
+ LPA_REG_WRITEL(lpa, val, LPA_OBUF_WMARK_SB);
+}
+static void lpa_enable_hlb_wmark(struct lpa_drv *lpa, u32 wmark_ctrl,
+ u32 cpu_id)
+{
+ u32 val;
+
+ val = LPA_REG_READL(lpa, LPA_OBUF_WMARK_HLB);
+ val &= ~LPA_OBUF_HLB_WMARK_CTRL_BMSK;
+ val &= ~LPA_OBUF_HLB_WMARK_MAP_BMSK;
+ val |= (wmark_ctrl << LPA_OBUF_HLB_WMARK_CTRL_SHFT) &
+ LPA_OBUF_HLB_WMARK_CTRL_BMSK;
+ val |= (cpu_id << LPA_OBUF_HLB_WMARK_MAP_SHFT) &
+ LPA_OBUF_HLB_WMARK_MAP_BMSK;
+ LPA_REG_WRITEL(lpa, val, LPA_OBUF_WMARK_HLB);
+}
+
+static void lpa_enable_utc(struct lpa_drv *lpa, bool enable, u32 cpu_id)
+{
+ u32 val;
+
+ val = (cpu_id << LPA_OBUF_UTC_CONFIG_MAP_SHFT) &
+ LPA_OBUF_UTC_CONFIG_MAP_BMSK;
+ enable = (enable ? 1 : 0);
+ val = (enable << LPA_OBUF_UTC_CONFIG_EN_SHFT) &
+ LPA_OBUF_UTC_CONFIG_EN_BMSK;
+ LPA_REG_WRITEL(lpa, val, LPA_OBUF_UTC_CONFIG);
+}
+
+static void lpa_enable_mixing(struct lpa_drv *lpa, bool enable)
+{
+ u32 val;
+
+ val = LPA_REG_READL(lpa, LPA_OBUF_CONTROL);
+ val = (enable ? val | LPA_OBUF_CONTROL_LLB_EN_BMSK :
+ val & ~LPA_OBUF_CONTROL_LLB_EN_BMSK);
+ LPA_REG_WRITEL(lpa, val, LPA_OBUF_CONTROL);
+}
+
+static void lpa_enable_mixer_saturation(struct lpa_drv *lpa, u32 buf_id,
+ bool enable)
+{
+ u32 val;
+
+ val = LPA_REG_READL(lpa, LPA_OBUF_CONTROL);
+
+ switch (buf_id) {
+ case LPA_BUF_ID_LLB:
+ val = enable ? (val | LPA_OBUF_CONTROL_LLB_SAT_EN_BMSK) :
+ (val & ~LPA_OBUF_CONTROL_LLB_SAT_EN_BMSK);
+ break;
+
+ case LPA_BUF_ID_SB:
+ val = enable ? (val | LPA_OBUF_CONTROL_SB_SAT_EN_BMSK) :
+ (val & ~LPA_OBUF_CONTROL_SB_SAT_EN_BMSK);
+ break;
+ }
+
+ LPA_REG_WRITEL(lpa, val, LPA_OBUF_CONTROL);
+}
+
+static void lpa_enable_obuf(struct lpa_drv *lpa, u32 buf_id, bool enable)
+{
+ u32 val;
+
+ val = LPA_REG_READL(lpa, LPA_OBUF_CONTROL);
+
+ switch (buf_id) {
+ case LPA_BUF_ID_HLB:
+ val = enable ? (val | LPA_OBUF_CONTROL_HLB_EN_BMSK) :
+ (val & ~LPA_OBUF_CONTROL_HLB_EN_BMSK);
+ break;
+
+ case LPA_BUF_ID_LLB:
+ val = enable ? (val | LPA_OBUF_CONTROL_LLB_EN_BMSK) :
+ (val & ~LPA_OBUF_CONTROL_LLB_EN_BMSK);
+ break;
+
+ case LPA_BUF_ID_SB:
+ val = enable ? (val | LPA_OBUF_CONTROL_SB_EN_BMSK) :
+ (val & ~LPA_OBUF_CONTROL_SB_EN_BMSK);
+ break;
+ }
+ LPA_REG_WRITEL(lpa, val, LPA_OBUF_CONTROL);
+}
+
+struct lpa_drv *lpa_get(void)
+{
+ struct lpa_mem_bank_select mem_bank;
+ struct lpa_drv *ret_lpa = &the_lpa_state.lpa_drv;
+
+ mutex_lock(&the_lpa_state.lpa_lock);
+ if (the_lpa_state.assigned) {
+ MM_ERR("LPA HW accupied\n");
+ ret_lpa = ERR_PTR(-EBUSY);
+ goto error;
+ }
+ /* perform initialization */
+ lpa_reset(ret_lpa);
+ /* Config adec param */
+ /* Initialize LLB/SB min/max address */
+ lpa_switch_sb(ret_lpa);
+ /* Config HLB minx/max address */
+ lpa_config_hlb_addr(ret_lpa);
+
+ /* Power up all memory bank for now */
+ mem_bank.b0 = 1;
+ mem_bank.b1 = 1;
+ mem_bank.b2 = 1;
+ mem_bank.b3 = 1;
+ mem_bank.b4 = 1;
+ mem_bank.b5 = 1;
+ mem_bank.b6 = 1;
+ mem_bank.b7 = 1;
+ mem_bank.b8 = 1;
+ mem_bank.b9 = 1;
+ mem_bank.b10 = 1;
+ mem_bank.llb = 1;
+ lpa_powerup_mem_bank(ret_lpa, &mem_bank);
+
+ while
+ (lpa_req_wmark_id(ret_lpa) != LPA_OBUF_WMARK_ASSIGN_DONE);
+
+ lpa_enable_llb_wmark(ret_lpa, LPA_WMARK_CTL_DISABLED, 0,
+ ret_lpa->dsp_proc_id);
+ lpa_enable_llb_wmark(ret_lpa, LPA_WMARK_CTL_DISABLED, 1,
+ ret_lpa->dsp_proc_id);
+ lpa_enable_llb_wmark(ret_lpa, LPA_WMARK_CTL_DISABLED, 2,
+ ret_lpa->app_proc_id);
+ lpa_enable_llb_wmark(ret_lpa, LPA_WMARK_CTL_DISABLED, 3,
+ ret_lpa->app_proc_id);
+ lpa_enable_hlb_wmark(ret_lpa, LPA_WMARK_CTL_DISABLED,
+ ret_lpa->dsp_proc_id);
+ lpa_enable_sb_wmark(ret_lpa, LPA_WMARK_CTL_DISABLED,
+ ret_lpa->dsp_proc_id);
+ lpa_enable_utc(ret_lpa, 0, LPA_OBUF_UTC_CONFIG_NO_INTR);
+
+ lpa_enable_mixing(ret_lpa, 1);
+ lpa_enable_mixer_saturation(ret_lpa, LPA_BUF_ID_LLB, 1);
+
+ lpa_enable_obuf(ret_lpa, LPA_BUF_ID_HLB, 0);
+ lpa_enable_obuf(ret_lpa, LPA_BUF_ID_LLB, 1);
+ if (ret_lpa->status & LPA_STATUS_SBUF_EN) {
+ lpa_enable_mixer_saturation(ret_lpa, LPA_BUF_ID_SB, 1);
+ lpa_enable_obuf(ret_lpa, LPA_BUF_ID_SB, 1);
+ }
+
+ lpa_enable_interrupt(ret_lpa, ret_lpa->dsp_proc_id);
+ mb();
+ the_lpa_state.assigned++;
+error:
+ mutex_unlock(&the_lpa_state.lpa_lock);
+ return ret_lpa;
+}
+EXPORT_SYMBOL(lpa_get);
+
+void lpa_put(struct lpa_drv *lpa)
+{
+
+ mutex_lock(&the_lpa_state.lpa_lock);
+ if (!lpa || &the_lpa_state.lpa_drv != lpa) {
+ MM_ERR("invalid arg\n");
+ goto error;
+ }
+ /* Deinitialize */
+ the_lpa_state.assigned--;
+error:
+ mutex_unlock(&the_lpa_state.lpa_lock);
+}
+EXPORT_SYMBOL(lpa_put);
+
+int lpa_cmd_codec_config(struct lpa_drv *lpa,
+ struct lpa_codec_config *config_ptr)
+{
+ u32 sample_rate;
+ u32 num_channels;
+ u32 width;
+ u32 val = 0;
+
+ if (!lpa || !config_ptr) {
+ MM_ERR("invalid parameters\n");
+ return -EINVAL;
+ }
+
+ switch (config_ptr->num_channels) {
+ case 8:
+ num_channels = LPA_NUM_CHAN_7P1;
+ break;
+ case 6:
+ num_channels = LPA_NUM_CHAN_5P1;
+ break;
+ case 4:
+ num_channels = LPA_NUM_CHAN_4_CHANNEL;
+ break;
+ case 2:
+ num_channels = LPA_NUM_CHAN_STEREO;
+ break;
+ case 1:
+ num_channels = LPA_NUM_CHAN_MONO;
+ break;
+ default:
+ MM_ERR("unsupported number of channel\n");
+ goto error;
+ }
+ val |= (num_channels << LPA_OBUF_CODEC_NUM_CHAN_SHFT) &
+ LPA_OBUF_CODEC_NUM_CHAN_BMSK;
+
+ switch (config_ptr->sample_rate) {
+ case 96000:
+ sample_rate = LPA_SAMPLE_RATE_96KHZ;
+ break;
+ case 64000:
+ sample_rate = LPA_SAMPLE_RATE_64KHZ;
+ break;
+ case 48000:
+ sample_rate = LPA_SAMPLE_RATE_48KHZ;
+ break;
+ case 44100:
+ sample_rate = LPA_SAMPLE_RATE_44P1KHZ;
+ break;
+ case 32000:
+ sample_rate = LPA_SAMPLE_RATE_32KHZ;
+ break;
+ case 22050:
+ sample_rate = LPA_SAMPLE_RATE_22P05KHZ;
+ break;
+ case 16000:
+ sample_rate = LPA_SAMPLE_RATE_16KHZ;
+ break;
+ case 11025:
+ sample_rate = LPA_SAMPLE_RATE_11P025KHZ;
+ break;
+ case 8000:
+ sample_rate = LPA_SAMPLE_RATE_8KHZ;
+ break;
+ default:
+ MM_ERR("unsupported sample rate \n");
+ goto error;
+ }
+ val |= (sample_rate << LPA_OBUF_CODEC_SAMP_SHFT) &
+ LPA_OBUF_CODEC_SAMP_BMSK;
+ switch (config_ptr->sample_width) {
+ case 32:
+ width = LPA_BITS_PER_CHAN_32BITS;
+ break;
+ case 24:
+ width = LPA_BITS_PER_CHAN_24BITS;
+ break;
+ case 16:
+ width = LPA_BITS_PER_CHAN_16BITS;
+ break;
+ default:
+ MM_ERR("unsupported sample width \n");
+ goto error;
+ }
+ val |= (width << LPA_OBUF_CODEC_BITS_PER_CHAN_SHFT) &
+ LPA_OBUF_CODEC_BITS_PER_CHAN_BMSK;
+
+ val |= LPA_OBUF_CODEC_LOAD_BMSK;
+ val |= (config_ptr->output_interface << LPA_OBUF_CODEC_INTF_SHFT) &
+ LPA_OBUF_CODEC_INTF_BMSK;
+
+ LPA_REG_WRITEL(lpa, val, LPA_OBUF_CODEC);
+ mb();
+
+ return 0;
+error:
+ return -EINVAL;
+}
+EXPORT_SYMBOL(lpa_cmd_codec_config);
+
+static int lpa_check_llb_clear(struct lpa_drv *lpa)
+{
+ u32 val;
+ val = LPA_REG_READL(lpa, LPA_OBUF_STATUS);
+
+ return !(val & LPA_OBUF_STATUS_LLB_CLR_BMSK);
+}
+
+static void lpa_clear_llb(struct lpa_drv *lpa)
+{
+ u32 val;
+
+ val = LPA_REG_READL(lpa, LPA_OBUF_CONTROL);
+ LPA_REG_WRITEL(lpa, (val | LPA_OBUF_CONTROL_LLB_CLR_CMD_BMSK),
+ LPA_OBUF_CONTROL);
+ lpa_enable_obuf(lpa, LPA_BUF_ID_LLB, 0);
+
+ while (!lpa_check_llb_clear(lpa))
+ udelay(100);
+ LPA_REG_WRITEL(lpa, val, LPA_OBUF_CONTROL);
+}
+
+int lpa_cmd_enable_codec(struct lpa_drv *lpa, bool enable)
+{
+ u32 val;
+ struct lpa_mem_bank_select mem_bank;
+
+ MM_DBG(" %s\n", (enable ? "enable" : "disable"));
+
+ if (!lpa)
+ return -EINVAL;
+
+ val = LPA_REG_READL(lpa, LPA_OBUF_CODEC);
+
+ if (enable) {
+ if (val & LPA_OBUF_CODEC_CODEC_INTF_EN_BMSK)
+ return -EBUSY;
+ /* Power up all memory bank for now */
+ mem_bank.b0 = 1;
+ mem_bank.b1 = 1;
+ mem_bank.b2 = 1;
+ mem_bank.b3 = 1;
+ mem_bank.b4 = 1;
+ mem_bank.b5 = 1;
+ mem_bank.b6 = 1;
+ mem_bank.b7 = 1;
+ mem_bank.b8 = 1;
+ mem_bank.b9 = 1;
+ mem_bank.b10 = 1;
+ mem_bank.llb = 1;
+ lpa_powerup_mem_bank(lpa, &mem_bank);
+
+ /*clear LLB*/
+ lpa_clear_llb(lpa);
+
+ lpa_enable_codec(lpa, 1);
+ MM_DBG("LPA codec is enabled\n");
+ } else {
+ if (val & LPA_OBUF_CODEC_CODEC_INTF_EN_BMSK) {
+ lpa_enable_codec(lpa, 0);
+ MM_DBG("LPA codec is disabled\n");
+ } else
+ MM_ERR("LPA codec is already disable\n");
+ }
+ mb();
+ return 0;
+}
+EXPORT_SYMBOL(lpa_cmd_enable_codec);
+
+static int lpa_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct resource *mem_src;
+ struct msm_lpa_platform_data *pdata;
+
+ MM_INFO("lpa probe\n");
+
+ if (!pdev || !pdev->dev.platform_data) {
+ MM_ERR("no plaform data\n");
+ rc = -ENODEV;
+ goto error;
+ }
+
+ mem_src = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpa");
+ if (!mem_src) {
+ MM_ERR("LPA base address undefined\n");
+ rc = -ENODEV;
+ goto error;
+ }
+
+ pdata = pdev->dev.platform_data;
+ the_lpa_state.lpa_drv.baseaddr = ioremap(mem_src->start,
+ (mem_src->end - mem_src->start) + 1);
+ if (!the_lpa_state.lpa_drv.baseaddr) {
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ the_lpa_state.lpa_drv.obuf_hlb_size = pdata->obuf_hlb_size;
+ the_lpa_state.lpa_drv.dsp_proc_id = pdata->dsp_proc_id;
+ the_lpa_state.lpa_drv.app_proc_id = pdata->app_proc_id;
+ the_lpa_state.lpa_drv.nosb_config = pdata->nosb_config;
+ the_lpa_state.lpa_drv.sb_config = pdata->sb_config;
+ /* default to enable summing buffer */
+ the_lpa_state.lpa_drv.status = LPA_STATUS_SBUF_EN;
+
+error:
+ return rc;
+
+}
+
+static int lpa_remove(struct platform_device *pdev)
+{
+ iounmap(the_lpa_state.lpa_drv.baseaddr);
+ return 0;
+}
+
+static struct platform_driver lpa_driver = {
+ .probe = lpa_probe,
+ .remove = lpa_remove,
+ .driver = {
+ .name = "lpa",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init lpa_init(void)
+{
+ the_lpa_state.assigned = 0;
+ mutex_init(&the_lpa_state.lpa_lock);
+ return platform_driver_register(&lpa_driver);
+}
+
+static void __exit lpa_exit(void)
+{
+ platform_driver_unregister(&lpa_driver);
+}
+
+module_init(lpa_init);
+module_exit(lpa_exit);
+
+MODULE_DESCRIPTION("MSM LPA driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/mi2s.c b/arch/arm/mach-msm/qdsp5v2/mi2s.c
new file mode 100644
index 0000000..e38f164
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/mi2s.c
@@ -0,0 +1,885 @@
+/* Copyright (c) 2009,2011 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+
+#include <mach/qdsp5v2/mi2s.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+
+#define DEBUG
+#ifdef DEBUG
+#define dprintk(format, arg...) \
+printk(KERN_DEBUG format, ## arg)
+#else
+#define dprintk(format, arg...) do {} while (0)
+#endif
+
+/*----------------------------------------------------------------------------
+ * Preprocessor Definitions and Constants
+ * -------------------------------------------------------------------------*/
+
+/* Device Types */
+#define HDMI 0
+#define CODEC_RX 1
+#define CODEC_TX 2
+
+/* Static offset for now. If different target have different
+ * offset, update to platform data model
+ */
+#define MI2S_RESET_OFFSET 0x0
+#define MI2S_MODE_OFFSET 0x4
+#define MI2S_TX_MODE_OFFSET 0x8
+#define MI2S_RX_MODE_OFFSET 0xc
+
+#define MI2S_SD_N_EN_MASK 0xF0
+#define MI2S_TX_RX_N_MASK 0x0F
+
+#define MI2S_RESET__MI2S_RESET__RESET 0x1
+#define MI2S_RESET__MI2S_RESET__ACTIVE 0x0
+#define MI2S_MODE__MI2S_MASTER__MASTER 0x1
+#define MI2S_MODE__MI2S_MASTER__SLAVE 0x0
+#define MI2S_MODE__MI2S_TX_RX_WORD_TYPE__16_BIT 0x1
+#define MI2S_MODE__MI2S_TX_RX_WORD_TYPE__24_BIT 0x2
+#define MI2S_MODE__MI2S_TX_RX_WORD_TYPE__32_BIT 0x3
+#define MI2S_TX_MODE__MI2S_TX_CODEC_16_MONO_MODE__RAW 0x0
+#define MI2S_TX_MODE__MI2S_TX_CODEC_16_MONO_MODE__PACKED 0x1
+#define MI2S_TX_MODE__MI2S_TX_STEREO_MODE__MONO_SAMPLE 0x0
+#define MI2S_TX_MODE__MI2S_TX_STEREO_MODE__STEREO_SAMPLE 0x1
+#define MI2S_TX_MODE__MI2S_TX_CH_TYPE__2_CHANNEL 0x0
+#define MI2S_TX_MODE__MI2S_TX_CH_TYPE__4_CHANNEL 0x1
+#define MI2S_TX_MODE__MI2S_TX_CH_TYPE__6_CHANNEL 0x2
+#define MI2S_TX_MODE__MI2S_TX_CH_TYPE__8_CHANNEL 0x3
+#define MI2S_TX_MODE__MI2S_TX_DMA_ACK_SYNCH_EN__SYNC_ENABLE 0x1
+#define MI2S_RX_MODE__MI2S_RX_CODEC_16_MONO_MODE__RAW 0x0
+#define MI2S_RX_MODE__MI2S_RX_CODEC_16_MONO_MODE__PACKED 0x1
+#define MI2S_RX_MODE__MI2S_RX_STEREO_MODE__MONO_SAMPLE 0x0
+#define MI2S_RX_MODE__MI2S_RX_STEREO_MODE__STEREO_SAMPLE 0x1
+#define MI2S_RX_MODE__MI2S_RX_CH_TYPE__2_CH 0x0
+#define MI2S_RX_MODE__MI2S_RX_DMA_ACK_SYNCH_EN__SYNC_ENABLE 0x1
+
+#define HWIO_AUDIO1_MI2S_MODE_MI2S_MASTER_BMSK 0x1000
+#define HWIO_AUDIO1_MI2S_MODE_MI2S_MASTER_SHFT 0xC
+#define HWIO_AUDIO1_MI2S_MODE_MI2S_TX_RX_WORD_TYPE_BMSK 0x300
+#define HWIO_AUDIO1_MI2S_MODE_MI2S_TX_RX_WORD_TYPE_SHFT 0x8
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_BMSK 0x4
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_SHFT 0x2
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_P_MONO_BMSK 0x2
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_P_MONO_SHFT 0x1
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_BMSK 0x18
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_SHFT 0x3
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_4_0_CH_MAP_BMSK 0x80
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_4_0_CH_MAP_SHFT 0x7
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_2_0_CH_MAP_BMSK 0x60
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_2_0_CH_MAP_SHFT 0x5
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_DMA_ACK_SYNCH_EN_BMSK 0x1
+#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_I2S_LINE_BMSK 0x60
+#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_I2S_LINE_SHFT 0x5
+#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_BMSK 0x4
+#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_SHFT 0x2
+#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CODEC_P_MONO_BMSK 0x2
+#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CODEC_P_MONO_SHFT 0x1
+#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CH_TYPE_BMSK 0x18
+#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CH_TYPE_SHFT 0x3
+#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_DMA_ACK_SYNCH_EN_BMSK 0x1
+
+/* Max number of channels */
+#define MAX_NUM_CHANNELS_OUT 8
+#define MAX_NUM_CHANNELS_IN 2
+
+/* Num of SD Lines */
+#define MAX_SD_LINES 4
+
+#define MI2S_SD_0_EN_MAP 0x10
+#define MI2S_SD_1_EN_MAP 0x20
+#define MI2S_SD_2_EN_MAP 0x40
+#define MI2S_SD_3_EN_MAP 0x80
+#define MI2S_SD_0_TX_MAP 0x01
+#define MI2S_SD_1_TX_MAP 0x02
+#define MI2S_SD_2_TX_MAP 0x04
+#define MI2S_SD_3_TX_MAP 0x08
+
+struct mi2s_state {
+ void __iomem *mi2s_hdmi_base;
+ void __iomem *mi2s_rx_base;
+ void __iomem *mi2s_tx_base;
+ struct mutex mutex_lock;
+
+};
+
+static struct mi2s_state the_mi2s_state;
+
+static void __iomem *get_base_addr(struct mi2s_state *mi2s, uint8_t dev_id)
+{
+ switch (dev_id) {
+ case HDMI:
+ return mi2s->mi2s_hdmi_base;
+ case CODEC_RX:
+ return mi2s->mi2s_rx_base;
+ case CODEC_TX:
+ return mi2s->mi2s_tx_base;
+ default:
+ break;
+ }
+ return ERR_PTR(-ENODEV);
+}
+
+static void mi2s_reset(struct mi2s_state *mi2s, uint8_t dev_id)
+{
+ void __iomem *baddr = get_base_addr(mi2s, dev_id);
+ if (!IS_ERR(baddr))
+ writel(MI2S_RESET__MI2S_RESET__RESET,
+ baddr + MI2S_RESET_OFFSET);
+}
+
+static void mi2s_release(struct mi2s_state *mi2s, uint8_t dev_id)
+{
+ void __iomem *baddr = get_base_addr(mi2s, dev_id);
+ if (!IS_ERR(baddr))
+ writel(MI2S_RESET__MI2S_RESET__ACTIVE,
+ baddr + MI2S_RESET_OFFSET);
+}
+
+static void mi2s_master(struct mi2s_state *mi2s, uint8_t dev_id, bool master)
+{
+ void __iomem *baddr = get_base_addr(mi2s, dev_id);
+ uint32_t val;
+ if (!IS_ERR(baddr)) {
+ val = readl(baddr + MI2S_MODE_OFFSET);
+ if (master) {
+ writel(
+ ((val & ~HWIO_AUDIO1_MI2S_MODE_MI2S_MASTER_BMSK) |
+ (MI2S_MODE__MI2S_MASTER__MASTER <<
+ HWIO_AUDIO1_MI2S_MODE_MI2S_MASTER_SHFT)),
+ baddr + MI2S_MODE_OFFSET);
+ } else {
+ writel(
+ ((val & ~HWIO_AUDIO1_MI2S_MODE_MI2S_MASTER_BMSK) |
+ (MI2S_MODE__MI2S_MASTER__SLAVE <<
+ HWIO_AUDIO1_MI2S_MODE_MI2S_MASTER_SHFT)),
+ baddr + MI2S_MODE_OFFSET);
+ }
+ }
+}
+
+static void mi2s_set_word_type(struct mi2s_state *mi2s, uint8_t dev_id,
+ uint8_t size)
+{
+ void __iomem *baddr = get_base_addr(mi2s, dev_id);
+ uint32_t val;
+ if (!IS_ERR(baddr)) {
+ val = readl(baddr + MI2S_MODE_OFFSET);
+ switch (size) {
+ case WT_16_BIT:
+ writel(
+ ((val &
+ ~HWIO_AUDIO1_MI2S_MODE_MI2S_TX_RX_WORD_TYPE_BMSK) |
+ (MI2S_MODE__MI2S_TX_RX_WORD_TYPE__16_BIT <<
+ HWIO_AUDIO1_MI2S_MODE_MI2S_TX_RX_WORD_TYPE_SHFT)),
+ baddr + MI2S_MODE_OFFSET);
+ break;
+ case WT_24_BIT:
+ writel(
+ ((val &
+ ~HWIO_AUDIO1_MI2S_MODE_MI2S_TX_RX_WORD_TYPE_BMSK) |
+ (MI2S_MODE__MI2S_TX_RX_WORD_TYPE__24_BIT <<
+ HWIO_AUDIO1_MI2S_MODE_MI2S_TX_RX_WORD_TYPE_SHFT)),
+ baddr + MI2S_MODE_OFFSET);
+ break;
+ case WT_32_BIT:
+ writel(
+ ((val &
+ ~HWIO_AUDIO1_MI2S_MODE_MI2S_TX_RX_WORD_TYPE_BMSK) |
+ (MI2S_MODE__MI2S_TX_RX_WORD_TYPE__32_BIT <<
+ HWIO_AUDIO1_MI2S_MODE_MI2S_TX_RX_WORD_TYPE_SHFT)),
+ baddr + MI2S_MODE_OFFSET);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void mi2s_set_sd(struct mi2s_state *mi2s, uint8_t dev_id, uint8_t sd_map)
+{
+ void __iomem *baddr = get_base_addr(mi2s, dev_id);
+ uint32_t val;
+ if (!IS_ERR(baddr)) {
+ val = readl(baddr + MI2S_MODE_OFFSET) &
+ ~(MI2S_SD_N_EN_MASK | MI2S_TX_RX_N_MASK);
+ writel(val | sd_map, baddr + MI2S_MODE_OFFSET);
+ }
+}
+
+static void mi2s_set_output_num_channels(struct mi2s_state *mi2s,
+ uint8_t dev_id, uint8_t channels)
+{
+ void __iomem *baddr = get_base_addr(mi2s, dev_id);
+ uint32_t val;
+ if (!IS_ERR(baddr)) {
+ val = readl(baddr + MI2S_TX_MODE_OFFSET);
+ if (channels == MI2S_CHAN_MONO_RAW) {
+ val = (val &
+ ~(HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_BMSK |
+ HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_P_MONO_BMSK)) |
+ ((MI2S_TX_MODE__MI2S_TX_STEREO_MODE__MONO_SAMPLE <<
+ HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_SHFT) |
+ (MI2S_TX_MODE__MI2S_TX_CODEC_16_MONO_MODE__RAW <<
+ HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_P_MONO_SHFT));
+ } else if (channels == MI2S_CHAN_MONO_PACKED) {
+ val = (val &
+ ~(HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_BMSK |
+ HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_P_MONO_BMSK)) |
+ ((MI2S_TX_MODE__MI2S_TX_STEREO_MODE__MONO_SAMPLE <<
+ HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_SHFT) |
+ (MI2S_TX_MODE__MI2S_TX_CODEC_16_MONO_MODE__PACKED <<
+ HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_P_MONO_SHFT));
+ } else if (channels == MI2S_CHAN_STEREO) {
+ val = (val &
+ ~(HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_BMSK |
+ HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_BMSK)) |
+ ((MI2S_TX_MODE__MI2S_TX_STEREO_MODE__STEREO_SAMPLE <<
+ HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_SHFT) |
+ (MI2S_TX_MODE__MI2S_TX_CH_TYPE__2_CHANNEL <<
+ HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_SHFT));
+ } else if (channels == MI2S_CHAN_4CHANNELS) {
+ val = (val &
+ ~(HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_BMSK |
+ HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_BMSK)) |
+ ((MI2S_TX_MODE__MI2S_TX_STEREO_MODE__STEREO_SAMPLE <<
+ HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_SHFT) |
+ (MI2S_TX_MODE__MI2S_TX_CH_TYPE__4_CHANNEL <<
+ HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_SHFT));
+ } else if (channels == MI2S_CHAN_6CHANNELS) {
+ val = (val &
+ ~(HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_BMSK |
+ HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_BMSK)) |
+ ((MI2S_TX_MODE__MI2S_TX_STEREO_MODE__STEREO_SAMPLE <<
+ HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_SHFT) |
+ (MI2S_TX_MODE__MI2S_TX_CH_TYPE__6_CHANNEL <<
+ HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_SHFT));
+ } else if (channels == MI2S_CHAN_8CHANNELS) {
+ val = (val &
+ ~(HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_BMSK |
+ HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_BMSK)) |
+ ((MI2S_TX_MODE__MI2S_TX_STEREO_MODE__STEREO_SAMPLE <<
+ HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_SHFT) |
+ (MI2S_TX_MODE__MI2S_TX_CH_TYPE__8_CHANNEL <<
+ HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_SHFT));
+ }
+ writel(val, baddr + MI2S_TX_MODE_OFFSET);
+ }
+}
+
+static void mi2s_set_output_4ch_map(struct mi2s_state *mi2s, uint8_t dev_id,
+ bool high_low)
+{
+ void __iomem *baddr = get_base_addr(mi2s, dev_id);
+ uint32_t val;
+ if (!IS_ERR(baddr)) {
+ val = readl(baddr + MI2S_TX_MODE_OFFSET);
+ val = (val & ~HWIO_AUDIO1_MI2S_TX_MODE_MI2S_4_0_CH_MAP_BMSK) |
+ (high_low <<
+ HWIO_AUDIO1_MI2S_TX_MODE_MI2S_4_0_CH_MAP_SHFT);
+ writel(val, baddr + MI2S_TX_MODE_OFFSET);
+ }
+}
+
+static void mi2s_set_output_2ch_map(struct mi2s_state *mi2s, uint8_t dev_id,
+ uint8_t sd_line)
+{
+ void __iomem *baddr = get_base_addr(mi2s, dev_id);
+ uint32_t val;
+
+ if (!IS_ERR(baddr)) {
+ val = readl(baddr + MI2S_TX_MODE_OFFSET);
+ if (sd_line < 4) {
+ val = (val &
+ ~HWIO_AUDIO1_MI2S_TX_MODE_MI2S_2_0_CH_MAP_BMSK) |
+ (sd_line <<
+ HWIO_AUDIO1_MI2S_TX_MODE_MI2S_2_0_CH_MAP_SHFT);
+ writel(val, baddr + MI2S_TX_MODE_OFFSET);
+ }
+ }
+}
+
+static void mi2s_set_output_clk_synch(struct mi2s_state *mi2s, uint8_t dev_id)
+{
+ void __iomem *baddr = get_base_addr(mi2s, dev_id);
+ uint32_t val;
+
+ if (!IS_ERR(baddr)) {
+ val = readl(baddr + MI2S_TX_MODE_OFFSET);
+ writel(((val &
+ ~HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_DMA_ACK_SYNCH_EN_BMSK) |
+ MI2S_TX_MODE__MI2S_TX_DMA_ACK_SYNCH_EN__SYNC_ENABLE),
+ baddr + MI2S_TX_MODE_OFFSET);
+ }
+}
+
+static void mi2s_set_input_sd_line(struct mi2s_state *mi2s, uint8_t dev_id,
+ uint8_t sd_line)
+{
+ void __iomem *baddr = get_base_addr(mi2s, dev_id);
+ uint32_t val;
+
+ if (!IS_ERR(baddr)) {
+ val = readl(baddr + MI2S_RX_MODE_OFFSET);
+ if (sd_line < 4) {
+ val = (val &
+ ~HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_I2S_LINE_BMSK) |
+ (sd_line <<
+ HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_I2S_LINE_SHFT);
+ writel(val, baddr + MI2S_RX_MODE_OFFSET);
+ }
+ }
+}
+
+static void mi2s_set_input_num_channels(struct mi2s_state *mi2s, uint8_t dev_id,
+ uint8_t channels)
+{
+ void __iomem *baddr = get_base_addr(mi2s, dev_id);
+ uint32_t val;
+
+ if (!IS_ERR(baddr)) {
+ val = readl(baddr + MI2S_RX_MODE_OFFSET);
+ if (channels == MI2S_CHAN_MONO_RAW) {
+ val = (val &
+ ~(HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_BMSK |
+ HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CODEC_P_MONO_BMSK)) |
+ ((MI2S_RX_MODE__MI2S_RX_STEREO_MODE__MONO_SAMPLE <<
+ HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_SHFT) |
+ (MI2S_RX_MODE__MI2S_RX_CODEC_16_MONO_MODE__RAW <<
+ HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CODEC_P_MONO_SHFT));
+ } else if (channels == MI2S_CHAN_MONO_PACKED) {
+ val = (val &
+ ~(HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_BMSK |
+ HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CODEC_P_MONO_BMSK)) |
+ ((MI2S_RX_MODE__MI2S_RX_STEREO_MODE__MONO_SAMPLE <<
+ HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_SHFT) |
+ (MI2S_RX_MODE__MI2S_RX_CODEC_16_MONO_MODE__PACKED <<
+ HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CODEC_P_MONO_SHFT));
+ } else if (channels == MI2S_CHAN_STEREO) {
+
+ if (dev_id == HDMI)
+ val = (val &
+ ~(HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_BMSK |
+ HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CH_TYPE_BMSK)) |
+ ((MI2S_RX_MODE__MI2S_RX_STEREO_MODE__STEREO_SAMPLE <<
+ HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_SHFT) |
+ (MI2S_RX_MODE__MI2S_RX_CH_TYPE__2_CH <<
+ HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CH_TYPE_SHFT));
+
+ else
+ val = (val &
+ ~(HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_BMSK |
+ HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CH_TYPE_BMSK)) |
+ ((MI2S_RX_MODE__MI2S_RX_STEREO_MODE__STEREO_SAMPLE <<
+ HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_SHFT) |
+ (MI2S_RX_MODE__MI2S_RX_CODEC_16_MONO_MODE__PACKED <<
+ HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CODEC_P_MONO_SHFT) |
+ (MI2S_RX_MODE__MI2S_RX_CH_TYPE__2_CH <<
+ HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CH_TYPE_SHFT));
+
+
+ }
+ writel(val, baddr + MI2S_RX_MODE_OFFSET);
+ }
+}
+
+static void mi2s_set_input_clk_synch(struct mi2s_state *mi2s, uint8_t dev_id)
+{
+ void __iomem *baddr = get_base_addr(mi2s, dev_id);
+ uint32_t val;
+
+ if (!IS_ERR(baddr)) {
+ val = readl(baddr + MI2S_RX_MODE_OFFSET);
+ writel(
+ ((val &
+ ~HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_DMA_ACK_SYNCH_EN_BMSK) |
+ MI2S_RX_MODE__MI2S_RX_DMA_ACK_SYNCH_EN__SYNC_ENABLE),
+ baddr + MI2S_RX_MODE_OFFSET);
+ }
+}
+
+
+static u8 num_of_bits_set(u8 sd_line_mask)
+{
+ u8 num_bits_set = 0;
+
+ while (sd_line_mask) {
+
+ if (sd_line_mask & 1)
+ num_bits_set++;
+ sd_line_mask = sd_line_mask >> 1;
+ }
+ return num_bits_set;
+}
+
+
+bool mi2s_set_hdmi_output_path(uint8_t channels, uint8_t size,
+ uint8_t sd_line_mask)
+{
+ bool ret_val = MI2S_TRUE;
+ struct mi2s_state *mi2s = &the_mi2s_state;
+ u8 sd_line, num_of_sd_lines = 0;
+ void __iomem *baddr;
+ uint32_t val;
+
+ pr_debug("%s: channels = %u size = %u sd_line_mask = 0x%x\n", __func__,
+ channels, size, sd_line_mask);
+
+ if ((channels == 0) || (channels > MAX_NUM_CHANNELS_OUT) ||
+ ((channels != 1) && (channels % 2 != 0))) {
+
+ pr_err("%s: invalid number of channels. channels = %u\n",
+ __func__, channels);
+ return MI2S_FALSE;
+ }
+
+ sd_line_mask &= MI2S_SD_LINE_MASK;
+
+ if (!sd_line_mask) {
+ pr_err("%s: Did not set any data lines to use "
+ " sd_line_mask =0x%x\n", __func__, sd_line_mask);
+ return MI2S_FALSE;
+ }
+
+ mutex_lock(&mi2s->mutex_lock);
+ /* Put device in reset */
+ mi2s_reset(mi2s, HDMI);
+
+ mi2s_master(mi2s, HDMI, 1);
+
+ /* Set word type */
+ if (size <= WT_MAX)
+ mi2s_set_word_type(mi2s, HDMI, size);
+ else
+ ret_val = MI2S_FALSE;
+
+ /* Enable clock crossing synchronization of RD DMA ACK */
+ mi2s_set_output_clk_synch(mi2s, HDMI);
+
+ mi2s_set_output_num_channels(mi2s, HDMI, channels);
+
+ num_of_sd_lines = num_of_bits_set(sd_line_mask);
+ /*Second argument to find_first_bit should be maximum number of
+ bit*/
+
+ sd_line = find_first_bit((unsigned long *)&sd_line_mask,
+ sizeof(sd_line_mask) * 8);
+ pr_debug("sd_line = %d\n", sd_line);
+
+ if (channels == 1) {
+
+ if (num_of_sd_lines != 1) {
+ pr_err("%s: for one channel only one SD lines is"
+ " needed. num_of_sd_lines = %u\n",
+ __func__, num_of_sd_lines);
+
+ ret_val = MI2S_FALSE;
+ goto error;
+ }
+
+ if (sd_line != 0) {
+ pr_err("%s: for one channel tx, need to use SD_0 "
+ "sd_line = %u\n", __func__, sd_line);
+
+ ret_val = MI2S_FALSE;
+ goto error;
+ }
+
+ /* Enable SD line 0 for Tx (only option for
+ * mono audio)
+ */
+ mi2s_set_sd(mi2s, HDMI, MI2S_SD_0_EN_MAP | MI2S_SD_0_TX_MAP);
+
+ } else if (channels == 2) {
+
+ if (num_of_sd_lines != 1) {
+ pr_err("%s: for two channel only one SD lines is"
+ " needed. num_of_sd_lines = %u\n",
+ __func__, num_of_sd_lines);
+ ret_val = MI2S_FALSE;
+ goto error;
+ }
+
+ /* Enable single SD line for Tx */
+ mi2s_set_sd(mi2s, HDMI, (MI2S_SD_0_EN_MAP << sd_line) |
+ (MI2S_SD_0_TX_MAP << sd_line));
+
+ /* Set 2-channel mapping */
+ mi2s_set_output_2ch_map(mi2s, HDMI, sd_line);
+
+ } else if (channels == 4) {
+
+ if (num_of_sd_lines != 2) {
+ pr_err("%s: for 4 channels two SD lines are"
+ " needed. num_of_sd_lines = %u\\n",
+ __func__, num_of_sd_lines);
+ ret_val = MI2S_FALSE;
+ goto error;
+ }
+
+ if ((sd_line_mask && MI2S_SD_0) &&
+ (sd_line_mask && MI2S_SD_1)) {
+
+ mi2s_set_sd(mi2s, HDMI, (MI2S_SD_0_EN_MAP |
+ MI2S_SD_1_EN_MAP) | (MI2S_SD_0_TX_MAP |
+ MI2S_SD_1_TX_MAP));
+ mi2s_set_output_4ch_map(mi2s, HDMI, MI2S_FALSE);
+
+ } else if ((sd_line_mask && MI2S_SD_2) &&
+ (sd_line_mask && MI2S_SD_3)) {
+
+ mi2s_set_sd(mi2s, HDMI, (MI2S_SD_2_EN_MAP |
+ MI2S_SD_3_EN_MAP) | (MI2S_SD_2_TX_MAP |
+ MI2S_SD_3_TX_MAP));
+
+ mi2s_set_output_4ch_map(mi2s, HDMI, MI2S_TRUE);
+ } else {
+
+ pr_err("%s: for 4 channels invalid SD lines usage"
+ " sd_line_mask = 0x%x\n",
+ __func__, sd_line_mask);
+ ret_val = MI2S_FALSE;
+ goto error;
+ }
+ } else if (channels == 6) {
+
+ if (num_of_sd_lines != 3) {
+ pr_err("%s: for 6 channels three SD lines are"
+ " needed. num_of_sd_lines = %u\n",
+ __func__, num_of_sd_lines);
+ ret_val = MI2S_FALSE;
+ goto error;
+ }
+
+ if ((sd_line_mask && MI2S_SD_0) &&
+ (sd_line_mask && MI2S_SD_1) &&
+ (sd_line_mask && MI2S_SD_2)) {
+
+ mi2s_set_sd(mi2s, HDMI, (MI2S_SD_0_EN_MAP |
+ MI2S_SD_1_EN_MAP | MI2S_SD_2_EN_MAP) |
+ (MI2S_SD_0_TX_MAP | MI2S_SD_1_TX_MAP |
+ MI2S_SD_2_TX_MAP));
+
+ } else if ((sd_line_mask && MI2S_SD_1) &&
+ (sd_line_mask && MI2S_SD_2) &&
+ (sd_line_mask && MI2S_SD_3)) {
+
+ mi2s_set_sd(mi2s, HDMI, (MI2S_SD_1_EN_MAP |
+ MI2S_SD_2_EN_MAP | MI2S_SD_3_EN_MAP) |
+ (MI2S_SD_1_TX_MAP | MI2S_SD_2_TX_MAP |
+ MI2S_SD_3_TX_MAP));
+
+ } else {
+
+ pr_err("%s: for 6 channels invalid SD lines usage"
+ " sd_line_mask = 0x%x\n",
+ __func__, sd_line_mask);
+ ret_val = MI2S_FALSE;
+ goto error;
+ }
+ } else if (channels == 8) {
+
+ if (num_of_sd_lines != 4) {
+ pr_err("%s: for 8 channels four SD lines are"
+ " needed. num_of_sd_lines = %u\n",
+ __func__, num_of_sd_lines);
+ ret_val = MI2S_FALSE;
+ goto error;
+ }
+
+ mi2s_set_sd(mi2s, HDMI, (MI2S_SD_0_EN_MAP |
+ MI2S_SD_1_EN_MAP | MI2S_SD_2_EN_MAP |
+ MI2S_SD_3_EN_MAP) | (MI2S_SD_0_TX_MAP |
+ MI2S_SD_1_TX_MAP | MI2S_SD_2_TX_MAP |
+ MI2S_SD_3_TX_MAP));
+ } else {
+ pr_err("%s: invalid number channels = %u\n",
+ __func__, channels);
+ ret_val = MI2S_FALSE;
+ goto error;
+ }
+
+ baddr = get_base_addr(mi2s, HDMI);
+
+ val = readl(baddr + MI2S_MODE_OFFSET);
+ pr_debug("%s(): MI2S_MODE = 0x%x\n", __func__, val);
+
+ val = readl(baddr + MI2S_TX_MODE_OFFSET);
+ pr_debug("%s(): MI2S_TX_MODE = 0x%x\n", __func__, val);
+
+
+error:
+ /* Release device from reset */
+ mi2s_release(mi2s, HDMI);
+
+ mutex_unlock(&mi2s->mutex_lock);
+ mb();
+ return ret_val;
+}
+EXPORT_SYMBOL(mi2s_set_hdmi_output_path);
+
+bool mi2s_set_hdmi_input_path(uint8_t channels, uint8_t size,
+ uint8_t sd_line_mask)
+{
+ bool ret_val = MI2S_TRUE;
+ struct mi2s_state *mi2s = &the_mi2s_state;
+ u8 sd_line, num_of_sd_lines = 0;
+ void __iomem *baddr;
+ uint32_t val;
+
+ pr_debug("%s: channels = %u size = %u sd_line_mask = 0x%x\n", __func__,
+ channels, size, sd_line_mask);
+
+ if ((channels != 1) && (channels != MAX_NUM_CHANNELS_IN)) {
+
+ pr_err("%s: invalid number of channels. channels = %u\n",
+ __func__, channels);
+ return MI2S_FALSE;
+ }
+
+ if (size > WT_MAX) {
+
+ pr_err("%s: mi2s word size can not be greater than 32 bits\n",
+ __func__);
+ return MI2S_FALSE;
+ }
+
+ sd_line_mask &= MI2S_SD_LINE_MASK;
+
+ if (!sd_line_mask) {
+ pr_err("%s: Did not set any data lines to use "
+ " sd_line_mask =0x%x\n", __func__, sd_line_mask);
+ return MI2S_FALSE;
+ }
+
+ num_of_sd_lines = num_of_bits_set(sd_line_mask);
+
+ if (num_of_sd_lines != 1) {
+ pr_err("%s: for two channel input only one SD lines is"
+ " needed. num_of_sd_lines = %u sd_line_mask = 0x%x\n",
+ __func__, num_of_sd_lines, sd_line_mask);
+ return MI2S_FALSE;
+ }
+
+ /*Second argument to find_first_bit should be maximum number of
+ bits interested*/
+ sd_line = find_first_bit((unsigned long *)&sd_line_mask,
+ sizeof(sd_line_mask) * 8);
+ pr_debug("sd_line = %d\n", sd_line);
+
+ /* Ensure sd_line parameter is valid (0-max) */
+ if (sd_line > MAX_SD_LINES) {
+ pr_err("%s: Line number can not be greater than = %u\n",
+ __func__, MAX_SD_LINES);
+ return MI2S_FALSE;
+ }
+
+ mutex_lock(&mi2s->mutex_lock);
+ /* Put device in reset */
+ mi2s_reset(mi2s, HDMI);
+
+ mi2s_master(mi2s, HDMI, 1);
+
+ /* Set word type */
+ mi2s_set_word_type(mi2s, HDMI, size);
+
+ /* Enable clock crossing synchronization of WR DMA ACK */
+ mi2s_set_input_clk_synch(mi2s, HDMI);
+
+ /* Ensure channels parameter is valid (non-zero, less than max,
+ * and even or mono)
+ */
+ mi2s_set_input_num_channels(mi2s, HDMI, channels);
+
+ mi2s_set_input_sd_line(mi2s, HDMI, sd_line);
+
+ mi2s_set_sd(mi2s, HDMI, (MI2S_SD_0_EN_MAP << sd_line));
+
+ baddr = get_base_addr(mi2s, HDMI);
+
+ val = readl(baddr + MI2S_MODE_OFFSET);
+ pr_debug("%s(): MI2S_MODE = 0x%x\n", __func__, val);
+
+ val = readl(baddr + MI2S_RX_MODE_OFFSET);
+ pr_debug("%s(): MI2S_RX_MODE = 0x%x\n", __func__, val);
+
+ /* Release device from reset */
+ mi2s_release(mi2s, HDMI);
+
+ mutex_unlock(&mi2s->mutex_lock);
+ mb();
+ return ret_val;
+}
+EXPORT_SYMBOL(mi2s_set_hdmi_input_path);
+
+bool mi2s_set_codec_output_path(uint8_t channels, uint8_t size)
+{
+ bool ret_val = MI2S_TRUE;
+ struct mi2s_state *mi2s = &the_mi2s_state;
+
+ mutex_lock(&mi2s->mutex_lock);
+ /* Put device in reset */
+ mi2s_reset(mi2s, CODEC_TX);
+
+ mi2s_master(mi2s, CODEC_TX, 1);
+
+ /* Enable clock crossing synchronization of RD DMA ACK */
+ mi2s_set_output_clk_synch(mi2s, CODEC_TX);
+
+ /* Set word type */
+ if (size <= WT_MAX)
+ mi2s_set_word_type(mi2s, CODEC_TX, size);
+ else
+ ret_val = MI2S_FALSE;
+
+ mi2s_set_output_num_channels(mi2s, CODEC_TX, channels);
+
+ /* Enable SD line */
+ mi2s_set_sd(mi2s, CODEC_TX, MI2S_SD_0_EN_MAP | MI2S_SD_0_TX_MAP);
+
+ /* Release device from reset */
+ mi2s_release(mi2s, CODEC_TX);
+
+ mutex_unlock(&mi2s->mutex_lock);
+ mb();
+ return ret_val;
+}
+EXPORT_SYMBOL(mi2s_set_codec_output_path);
+
+bool mi2s_set_codec_input_path(uint8_t channels, uint8_t size)
+{
+ bool ret_val = MI2S_TRUE;
+ struct mi2s_state *mi2s = &the_mi2s_state;
+
+ mutex_lock(&the_mi2s_state.mutex_lock);
+ /* Put device in reset */
+ mi2s_reset(mi2s, CODEC_RX);
+
+ mi2s_master(mi2s, CODEC_RX, 1);
+
+ /* Enable clock crossing synchronization of WR DMA ACK */
+ mi2s_set_input_clk_synch(mi2s, CODEC_RX);
+
+ /* Set word type */
+ if (size <= WT_MAX)
+ mi2s_set_word_type(mi2s, CODEC_RX, size);
+ else
+ ret_val = MI2S_FALSE;
+
+ mi2s_set_input_num_channels(mi2s, CODEC_RX, channels);
+
+ /* Enable SD line */
+ mi2s_set_sd(mi2s, CODEC_RX, MI2S_SD_0_EN_MAP);
+
+ /* Release device from reset */
+ mi2s_release(mi2s, CODEC_RX);
+
+ mutex_unlock(&mi2s->mutex_lock);
+ mb();
+ return ret_val;
+}
+EXPORT_SYMBOL(mi2s_set_codec_input_path);
+
+
+static int mi2s_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct resource *mem_src;
+
+ mem_src = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hdmi");
+ if (!mem_src) {
+ rc = -ENODEV;
+ goto error_hdmi;
+ }
+ the_mi2s_state.mi2s_hdmi_base = ioremap(mem_src->start,
+ (mem_src->end - mem_src->start) + 1);
+ if (!the_mi2s_state.mi2s_hdmi_base) {
+ rc = -ENOMEM;
+ goto error_hdmi;
+ }
+ mem_src = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "codec_rx");
+ if (!mem_src) {
+ rc = -ENODEV;
+ goto error_codec_rx;
+ }
+ the_mi2s_state.mi2s_rx_base = ioremap(mem_src->start,
+ (mem_src->end - mem_src->start) + 1);
+ if (!the_mi2s_state.mi2s_rx_base) {
+ rc = -ENOMEM;
+ goto error_codec_rx;
+ }
+ mem_src = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "codec_tx");
+ if (!mem_src) {
+ rc = -ENODEV;
+ goto error_codec_tx;
+ }
+ the_mi2s_state.mi2s_tx_base = ioremap(mem_src->start,
+ (mem_src->end - mem_src->start) + 1);
+ if (!the_mi2s_state.mi2s_tx_base) {
+ rc = -ENOMEM;
+ goto error_codec_tx;
+ }
+ mutex_init(&the_mi2s_state.mutex_lock);
+
+ return rc;
+
+error_codec_tx:
+ iounmap(the_mi2s_state.mi2s_rx_base);
+error_codec_rx:
+ iounmap(the_mi2s_state.mi2s_hdmi_base);
+error_hdmi:
+ return rc;
+
+}
+
+static int mi2s_remove(struct platform_device *pdev)
+{
+ iounmap(the_mi2s_state.mi2s_tx_base);
+ iounmap(the_mi2s_state.mi2s_rx_base);
+ iounmap(the_mi2s_state.mi2s_hdmi_base);
+ return 0;
+}
+
+static struct platform_driver mi2s_driver = {
+ .probe = mi2s_probe,
+ .remove = mi2s_remove,
+ .driver = {
+ .name = "mi2s",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init mi2s_init(void)
+{
+ return platform_driver_register(&mi2s_driver);
+}
+
+static void __exit mi2s_exit(void)
+{
+ platform_driver_unregister(&mi2s_driver);
+}
+
+module_init(mi2s_init);
+module_exit(mi2s_exit);
+
+MODULE_DESCRIPTION("MSM MI2S driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/mp3_funcs.c b/arch/arm/mach-msm/qdsp5v2/mp3_funcs.c
new file mode 100644
index 0000000..0b20be0
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/mp3_funcs.c
@@ -0,0 +1,45 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+
+#include <linux/msm_audio.h>
+
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/qdsp5audplaycmdi.h>
+#include <mach/qdsp5v2/qdsp5audplaymsg.h>
+#include <mach/qdsp5v2/audpp.h>
+#include <mach/qdsp5v2/codec_utils.h>
+#include <mach/qdsp5v2/mp3_funcs.h>
+#include <mach/debug_mm.h>
+
+long mp3_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ MM_DBG("mp3_ioctl() cmd = %d\b", cmd);
+
+ return -EINVAL;
+}
+
+void audpp_cmd_cfg_mp3_params(struct audio *audio)
+{
+ struct audpp_cmd_cfg_adec_params_mp3 cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+ cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_MP3_LEN;
+ cmd.common.dec_id = audio->dec_id;
+ cmd.common.input_sampling_frequency = audio->out_sample_rate;
+
+ audpp_send_queue2(&cmd, sizeof(cmd));
+}
diff --git a/arch/arm/mach-msm/qdsp5v2/pcm_funcs.c b/arch/arm/mach-msm/qdsp5v2/pcm_funcs.c
new file mode 100644
index 0000000..d7935a7
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/pcm_funcs.c
@@ -0,0 +1,47 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+
+#include <linux/msm_audio.h>
+
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/qdsp5audplaycmdi.h>
+#include <mach/qdsp5v2/qdsp5audplaymsg.h>
+#include <mach/qdsp5v2/audpp.h>
+#include <mach/qdsp5v2/codec_utils.h>
+#include <mach/qdsp5v2/pcm_funcs.h>
+#include <mach/debug_mm.h>
+
+long pcm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ MM_DBG("pcm_ioctl() cmd = %d\n", cmd);
+
+ return -EINVAL;
+}
+
+void audpp_cmd_cfg_pcm_params(struct audio *audio)
+{
+ struct audpp_cmd_cfg_adec_params_wav cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+ cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_WAV_LEN >> 1;
+ cmd.common.dec_id = audio->dec_id;
+ cmd.common.input_sampling_frequency = audio->out_sample_rate;
+ cmd.stereo_cfg = audio->out_channel_mode;
+ cmd.pcm_width = audio->out_bits;
+ cmd.sign = 0;
+ audpp_send_queue2(&cmd, sizeof(cmd));
+}
diff --git a/arch/arm/mach-msm/qdsp5v2/snddev_data_marimba.c b/arch/arm/mach-msm/qdsp5v2/snddev_data_marimba.c
new file mode 100644
index 0000000..b15d4c4
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/snddev_data_marimba.c
@@ -0,0 +1,1537 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/mfd/msm-adie-codec.h>
+#include <linux/uaccess.h>
+#include <mach/qdsp5v2/snddev_icodec.h>
+#include <mach/qdsp5v2/marimba_profile.h>
+#include <mach/qdsp5v2/aux_pcm.h>
+#include <mach/qdsp5v2/snddev_ecodec.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/qdsp5v2/snddev_virtual.h>
+#include <mach/board.h>
+#include <asm/mach-types.h>
+#include <mach/gpio.h>
+#include <mach/qdsp5v2/snddev_mi2s.h>
+#include <mach/qdsp5v2/mi2s.h>
+#include <mach/qdsp5v2/audio_acdb_def.h>
+
+/* define the value for BT_SCO */
+#define BT_SCO_PCM_CTL_VAL (PCM_CTL__RPCM_WIDTH__LINEAR_V |\
+ PCM_CTL__TPCM_WIDTH__LINEAR_V)
+#define BT_SCO_DATA_FORMAT_PADDING (DATA_FORMAT_PADDING_INFO__RPCM_FORMAT_V |\
+ DATA_FORMAT_PADDING_INFO__TPCM_FORMAT_V)
+#define BT_SCO_AUX_CODEC_INTF AUX_CODEC_INTF_CTL__PCMINTF_DATA_EN_V
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *debugfs_hsed_config;
+static void snddev_hsed_config_modify_setting(int type);
+static void snddev_hsed_config_restore_setting(void);
+#endif
+
+static struct adie_codec_action_unit iearpiece_48KHz_osr256_actions[] =
+ HANDSET_RX_48000_OSR_256;
+
+static struct adie_codec_hwsetting_entry iearpiece_settings[] = {
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = iearpiece_48KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(iearpiece_48KHz_osr256_actions),
+ }
+};
+
+static struct adie_codec_dev_profile iearpiece_profile = {
+ .path_type = ADIE_CODEC_RX,
+ .settings = iearpiece_settings,
+ .setting_sz = ARRAY_SIZE(iearpiece_settings),
+};
+
+static struct snddev_icodec_data snddev_iearpiece_data = {
+ .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE),
+ .name = "handset_rx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_HANDSET_SPKR,
+ .profile = &iearpiece_profile,
+ .channel_mode = 1,
+ .pmctl_id = NULL,
+ .pmctl_id_sz = 0,
+ .default_sample_rate = 48000,
+ .pamp_on = NULL,
+ .pamp_off = NULL,
+ .property = SIDE_TONE_MASK,
+ .max_voice_rx_vol[VOC_NB_INDEX] = -200,
+ .min_voice_rx_vol[VOC_NB_INDEX] = -1700,
+ .max_voice_rx_vol[VOC_WB_INDEX] = -200,
+ .min_voice_rx_vol[VOC_WB_INDEX] = -1700
+};
+
+static struct platform_device msm_iearpiece_device = {
+ .name = "snddev_icodec",
+ .id = 0,
+ .dev = { .platform_data = &snddev_iearpiece_data },
+};
+
+static struct adie_codec_action_unit imic_8KHz_osr256_actions[] =
+ HANDSET_TX_8000_OSR_256;
+
+static struct adie_codec_action_unit imic_16KHz_osr256_actions[] =
+ HANDSET_TX_16000_OSR_256;
+
+static struct adie_codec_action_unit imic_48KHz_osr256_actions[] =
+ HANDSET_TX_48000_OSR_256;
+
+static struct adie_codec_hwsetting_entry imic_settings[] = {
+ {
+ .freq_plan = 8000,
+ .osr = 256,
+ .actions = imic_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(imic_8KHz_osr256_actions),
+ },
+ {
+ .freq_plan = 16000,
+ .osr = 256,
+ .actions = imic_16KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(imic_16KHz_osr256_actions),
+ },
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = imic_48KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(imic_48KHz_osr256_actions),
+ }
+};
+
+static struct adie_codec_dev_profile imic_profile = {
+ .path_type = ADIE_CODEC_TX,
+ .settings = imic_settings,
+ .setting_sz = ARRAY_SIZE(imic_settings),
+};
+
+static enum hsed_controller imic_pmctl_id[] = {PM_HSED_CONTROLLER_0};
+
+static struct snddev_icodec_data snddev_imic_data = {
+ .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+ .name = "handset_tx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_HANDSET_MIC,
+ .profile = &imic_profile,
+ .channel_mode = 1,
+ .pmctl_id = imic_pmctl_id,
+ .pmctl_id_sz = ARRAY_SIZE(imic_pmctl_id),
+ .default_sample_rate = 48000,
+ .pamp_on = NULL,
+ .pamp_off = NULL,
+};
+
+static struct platform_device msm_imic_device = {
+ .name = "snddev_icodec",
+ .id = 1,
+ .dev = { .platform_data = &snddev_imic_data },
+};
+
+static struct adie_codec_action_unit ihs_stereo_rx_48KHz_osr256_actions[] =
+ HEADSET_STEREO_RX_LEGACY_48000_OSR_256;
+
+static struct adie_codec_hwsetting_entry ihs_stereo_rx_settings[] = {
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = ihs_stereo_rx_48KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(ihs_stereo_rx_48KHz_osr256_actions),
+ }
+};
+
+static struct adie_codec_dev_profile ihs_stereo_rx_profile = {
+ .path_type = ADIE_CODEC_RX,
+ .settings = ihs_stereo_rx_settings,
+ .setting_sz = ARRAY_SIZE(ihs_stereo_rx_settings),
+};
+
+static struct snddev_icodec_data snddev_ihs_stereo_rx_data = {
+ .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE),
+ .name = "headset_stereo_rx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_HEADSET_SPKR_STEREO,
+ .profile = &ihs_stereo_rx_profile,
+ .channel_mode = 2,
+ .default_sample_rate = 48000,
+ .pamp_on = NULL,
+ .pamp_off = NULL,
+ .property = SIDE_TONE_MASK,
+ .max_voice_rx_vol[VOC_NB_INDEX] = -700,
+ .min_voice_rx_vol[VOC_NB_INDEX] = -2200,
+ .max_voice_rx_vol[VOC_WB_INDEX] = -900,
+ .min_voice_rx_vol[VOC_WB_INDEX] = -2400
+};
+
+static struct platform_device msm_ihs_stereo_rx_device = {
+ .name = "snddev_icodec",
+ .id = 2,
+ .dev = { .platform_data = &snddev_ihs_stereo_rx_data },
+};
+
+static struct adie_codec_action_unit ihs_mono_rx_48KHz_osr256_actions[] =
+ HEADSET_RX_LEGACY_48000_OSR_256;
+
+static struct adie_codec_hwsetting_entry ihs_mono_rx_settings[] = {
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = ihs_mono_rx_48KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(ihs_mono_rx_48KHz_osr256_actions),
+ }
+};
+
+static struct adie_codec_dev_profile ihs_mono_rx_profile = {
+ .path_type = ADIE_CODEC_RX,
+ .settings = ihs_mono_rx_settings,
+ .setting_sz = ARRAY_SIZE(ihs_mono_rx_settings),
+};
+
+static struct snddev_icodec_data snddev_ihs_mono_rx_data = {
+ .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE),
+ .name = "headset_mono_rx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_HEADSET_SPKR_MONO,
+ .profile = &ihs_mono_rx_profile,
+ .channel_mode = 1,
+ .default_sample_rate = 48000,
+ .pamp_on = NULL,
+ .pamp_off = NULL,
+ .property = SIDE_TONE_MASK,
+ .max_voice_rx_vol[VOC_NB_INDEX] = -700,
+ .min_voice_rx_vol[VOC_NB_INDEX] = -2200,
+ .max_voice_rx_vol[VOC_WB_INDEX] = -900,
+ .min_voice_rx_vol[VOC_WB_INDEX] = -2400,
+
+};
+
+static struct platform_device msm_ihs_mono_rx_device = {
+ .name = "snddev_icodec",
+ .id = 3,
+ .dev = { .platform_data = &snddev_ihs_mono_rx_data },
+};
+
+static struct adie_codec_action_unit ihs_ffa_stereo_rx_48KHz_osr256_actions[] =
+ HEADSET_STEREO_RX_CAPLESS_48000_OSR_256;
+
+static struct adie_codec_hwsetting_entry ihs_ffa_stereo_rx_settings[] = {
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = ihs_ffa_stereo_rx_48KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(ihs_ffa_stereo_rx_48KHz_osr256_actions),
+ }
+};
+
+#ifdef CONFIG_DEBUG_FS
+static struct adie_codec_action_unit
+ ihs_ffa_stereo_rx_class_d_legacy_48KHz_osr256_actions[] =
+ HEADSET_STEREO_RX_CLASS_D_LEGACY_48000_OSR_256;
+
+static struct adie_codec_hwsetting_entry
+ ihs_ffa_stereo_rx_class_d_legacy_settings[] = {
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions =
+ ihs_ffa_stereo_rx_class_d_legacy_48KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE
+ (ihs_ffa_stereo_rx_class_d_legacy_48KHz_osr256_actions),
+ }
+};
+
+static struct adie_codec_action_unit
+ ihs_ffa_stereo_rx_class_ab_legacy_48KHz_osr256_actions[] =
+ HEADSET_STEREO_RX_LEGACY_48000_OSR_256;
+
+static struct adie_codec_hwsetting_entry
+ ihs_ffa_stereo_rx_class_ab_legacy_settings[] = {
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions =
+ ihs_ffa_stereo_rx_class_ab_legacy_48KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE
+ (ihs_ffa_stereo_rx_class_ab_legacy_48KHz_osr256_actions),
+ }
+};
+#endif
+
+static struct adie_codec_dev_profile ihs_ffa_stereo_rx_profile = {
+ .path_type = ADIE_CODEC_RX,
+ .settings = ihs_ffa_stereo_rx_settings,
+ .setting_sz = ARRAY_SIZE(ihs_ffa_stereo_rx_settings),
+};
+
+static struct snddev_icodec_data snddev_ihs_ffa_stereo_rx_data = {
+ .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE),
+ .name = "headset_stereo_rx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_HEADSET_SPKR_STEREO,
+ .profile = &ihs_ffa_stereo_rx_profile,
+ .channel_mode = 2,
+ .default_sample_rate = 48000,
+ .voltage_on = msm_snddev_hsed_voltage_on,
+ .voltage_off = msm_snddev_hsed_voltage_off,
+ .max_voice_rx_vol[VOC_NB_INDEX] = -700,
+ .min_voice_rx_vol[VOC_NB_INDEX] = -2200,
+ .max_voice_rx_vol[VOC_WB_INDEX] = -900,
+ .min_voice_rx_vol[VOC_WB_INDEX] = -2400,
+};
+
+static struct platform_device msm_ihs_ffa_stereo_rx_device = {
+ .name = "snddev_icodec",
+ .id = 4,
+ .dev = { .platform_data = &snddev_ihs_ffa_stereo_rx_data },
+};
+
+static struct adie_codec_action_unit ihs_ffa_mono_rx_48KHz_osr256_actions[] =
+ HEADSET_RX_CAPLESS_48000_OSR_256;
+
+static struct adie_codec_hwsetting_entry ihs_ffa_mono_rx_settings[] = {
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = ihs_ffa_mono_rx_48KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(ihs_ffa_mono_rx_48KHz_osr256_actions),
+ }
+};
+
+static struct adie_codec_dev_profile ihs_ffa_mono_rx_profile = {
+ .path_type = ADIE_CODEC_RX,
+ .settings = ihs_ffa_mono_rx_settings,
+ .setting_sz = ARRAY_SIZE(ihs_ffa_mono_rx_settings),
+};
+
+static struct snddev_icodec_data snddev_ihs_ffa_mono_rx_data = {
+ .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE),
+ .name = "headset_mono_rx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_HEADSET_SPKR_MONO,
+ .profile = &ihs_ffa_mono_rx_profile,
+ .channel_mode = 1,
+ .default_sample_rate = 48000,
+ .pamp_on = msm_snddev_hsed_voltage_on,
+ .pamp_off = msm_snddev_hsed_voltage_off,
+ .max_voice_rx_vol[VOC_NB_INDEX] = -700,
+ .min_voice_rx_vol[VOC_NB_INDEX] = -2200,
+ .max_voice_rx_vol[VOC_WB_INDEX] = -900,
+ .min_voice_rx_vol[VOC_WB_INDEX] = -2400,
+};
+
+static struct platform_device msm_ihs_ffa_mono_rx_device = {
+ .name = "snddev_icodec",
+ .id = 5,
+ .dev = { .platform_data = &snddev_ihs_ffa_mono_rx_data },
+};
+
+static struct adie_codec_action_unit ihs_mono_tx_8KHz_osr256_actions[] =
+ HEADSET_MONO_TX_8000_OSR_256;
+
+static struct adie_codec_action_unit ihs_mono_tx_16KHz_osr256_actions[] =
+ HEADSET_MONO_TX_16000_OSR_256;
+
+static struct adie_codec_action_unit ihs_mono_tx_48KHz_osr256_actions[] =
+ HEADSET_MONO_TX_48000_OSR_256;
+
+static struct adie_codec_hwsetting_entry ihs_mono_tx_settings[] = {
+ {
+ .freq_plan = 8000,
+ .osr = 256,
+ .actions = ihs_mono_tx_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(ihs_mono_tx_8KHz_osr256_actions),
+ },
+ {
+ .freq_plan = 16000,
+ .osr = 256,
+ .actions = ihs_mono_tx_16KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(ihs_mono_tx_16KHz_osr256_actions),
+ },
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = ihs_mono_tx_48KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(ihs_mono_tx_48KHz_osr256_actions),
+ }
+};
+
+static struct adie_codec_dev_profile ihs_mono_tx_profile = {
+ .path_type = ADIE_CODEC_TX,
+ .settings = ihs_mono_tx_settings,
+ .setting_sz = ARRAY_SIZE(ihs_mono_tx_settings),
+};
+
+static struct snddev_icodec_data snddev_ihs_mono_tx_data = {
+ .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+ .name = "headset_mono_tx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_HEADSET_MIC,
+ .profile = &ihs_mono_tx_profile,
+ .channel_mode = 1,
+ .pmctl_id = NULL,
+ .pmctl_id_sz = 0,
+ .default_sample_rate = 48000,
+ .pamp_on = msm_snddev_tx_route_config,
+ .pamp_off = msm_snddev_tx_route_deconfig,
+};
+
+static struct platform_device msm_ihs_mono_tx_device = {
+ .name = "snddev_icodec",
+ .id = 6,
+ .dev = { .platform_data = &snddev_ihs_mono_tx_data },
+};
+
+static struct adie_codec_action_unit ifmradio_handset_osr64_actions[] =
+ FM_HANDSET_OSR_64;
+
+static struct adie_codec_hwsetting_entry ifmradio_handset_settings[] = {
+ {
+ .freq_plan = 8000,
+ .osr = 256,
+ .actions = ifmradio_handset_osr64_actions,
+ .action_sz = ARRAY_SIZE(ifmradio_handset_osr64_actions),
+ }
+};
+
+static struct adie_codec_dev_profile ifmradio_handset_profile = {
+ .path_type = ADIE_CODEC_RX,
+ .settings = ifmradio_handset_settings,
+ .setting_sz = ARRAY_SIZE(ifmradio_handset_settings),
+};
+
+static struct snddev_icodec_data snddev_ifmradio_handset_data = {
+ .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_FM),
+ .name = "fmradio_handset_rx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_LP_FM_SPKR_PHONE_STEREO_RX,
+ .profile = &ifmradio_handset_profile,
+ .channel_mode = 1,
+ .default_sample_rate = 8000,
+ .pamp_on = NULL,
+ .pamp_off = NULL,
+ .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL,
+};
+
+static struct platform_device msm_ifmradio_handset_device = {
+ .name = "snddev_icodec",
+ .id = 7,
+ .dev = { .platform_data = &snddev_ifmradio_handset_data },
+};
+
+
+static struct adie_codec_action_unit ispeaker_rx_48KHz_osr256_actions[] =
+ SPEAKER_STEREO_RX_48000_OSR_256;
+
+static struct adie_codec_hwsetting_entry ispeaker_rx_settings[] = {
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = ispeaker_rx_48KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(ispeaker_rx_48KHz_osr256_actions),
+ }
+};
+
+static struct adie_codec_dev_profile ispeaker_rx_profile = {
+ .path_type = ADIE_CODEC_RX,
+ .settings = ispeaker_rx_settings,
+ .setting_sz = ARRAY_SIZE(ispeaker_rx_settings),
+};
+
+static struct snddev_icodec_data snddev_ispeaker_rx_data = {
+ .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE),
+ .name = "speaker_stereo_rx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_SPKR_PHONE_STEREO,
+ .profile = &ispeaker_rx_profile,
+ .channel_mode = 2,
+ .pmctl_id = NULL,
+ .pmctl_id_sz = 0,
+ .default_sample_rate = 48000,
+ .pamp_on = &msm_snddev_poweramp_on,
+ .pamp_off = &msm_snddev_poweramp_off,
+ .max_voice_rx_vol[VOC_NB_INDEX] = 1000,
+ .min_voice_rx_vol[VOC_NB_INDEX] = -500,
+ .max_voice_rx_vol[VOC_WB_INDEX] = 1000,
+ .min_voice_rx_vol[VOC_WB_INDEX] = -500,
+};
+
+static struct platform_device msm_ispeaker_rx_device = {
+ .name = "snddev_icodec",
+ .id = 8,
+ .dev = { .platform_data = &snddev_ispeaker_rx_data },
+
+};
+
+static struct adie_codec_action_unit ifmradio_speaker_osr64_actions[] =
+ FM_SPEAKER_OSR_64;
+
+static struct adie_codec_hwsetting_entry ifmradio_speaker_settings[] = {
+ {
+ .freq_plan = 8000,
+ .osr = 256,
+ .actions = ifmradio_speaker_osr64_actions,
+ .action_sz = ARRAY_SIZE(ifmradio_speaker_osr64_actions),
+ }
+};
+
+static struct adie_codec_dev_profile ifmradio_speaker_profile = {
+ .path_type = ADIE_CODEC_RX,
+ .settings = ifmradio_speaker_settings,
+ .setting_sz = ARRAY_SIZE(ifmradio_speaker_settings),
+};
+
+static struct snddev_icodec_data snddev_ifmradio_speaker_data = {
+ .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_FM),
+ .name = "fmradio_speaker_rx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_LP_FM_SPKR_PHONE_STEREO_RX,
+ .profile = &ifmradio_speaker_profile,
+ .channel_mode = 1,
+ .default_sample_rate = 8000,
+ .pamp_on = &msm_snddev_poweramp_on,
+ .pamp_off = &msm_snddev_poweramp_off,
+ .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL,
+};
+
+static struct platform_device msm_ifmradio_speaker_device = {
+ .name = "snddev_icodec",
+ .id = 9,
+ .dev = { .platform_data = &snddev_ifmradio_speaker_data },
+};
+
+static struct adie_codec_action_unit ifmradio_headset_osr64_actions[] =
+ FM_HEADSET_STEREO_CLASS_D_LEGACY_OSR_64;
+
+static struct adie_codec_hwsetting_entry ifmradio_headset_settings[] = {
+ {
+ .freq_plan = 8000,
+ .osr = 256,
+ .actions = ifmradio_headset_osr64_actions,
+ .action_sz = ARRAY_SIZE(ifmradio_headset_osr64_actions),
+ }
+};
+
+static struct adie_codec_dev_profile ifmradio_headset_profile = {
+ .path_type = ADIE_CODEC_RX,
+ .settings = ifmradio_headset_settings,
+ .setting_sz = ARRAY_SIZE(ifmradio_headset_settings),
+};
+
+static struct snddev_icodec_data snddev_ifmradio_headset_data = {
+ .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_FM),
+ .name = "fmradio_headset_rx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_LP_FM_HEADSET_SPKR_STEREO_RX,
+ .profile = &ifmradio_headset_profile,
+ .channel_mode = 1,
+ .default_sample_rate = 8000,
+ .pamp_on = NULL,
+ .pamp_off = NULL,
+ .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL,
+};
+
+static struct platform_device msm_ifmradio_headset_device = {
+ .name = "snddev_icodec",
+ .id = 10,
+ .dev = { .platform_data = &snddev_ifmradio_headset_data },
+};
+
+
+static struct adie_codec_action_unit ifmradio_ffa_headset_osr64_actions[] =
+ FM_HEADSET_CLASS_AB_STEREO_CAPLESS_OSR_64;
+
+static struct adie_codec_hwsetting_entry ifmradio_ffa_headset_settings[] = {
+ {
+ .freq_plan = 8000,
+ .osr = 256,
+ .actions = ifmradio_ffa_headset_osr64_actions,
+ .action_sz = ARRAY_SIZE(ifmradio_ffa_headset_osr64_actions),
+ }
+};
+
+static struct adie_codec_dev_profile ifmradio_ffa_headset_profile = {
+ .path_type = ADIE_CODEC_RX,
+ .settings = ifmradio_ffa_headset_settings,
+ .setting_sz = ARRAY_SIZE(ifmradio_ffa_headset_settings),
+};
+
+static struct snddev_icodec_data snddev_ifmradio_ffa_headset_data = {
+ .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_FM),
+ .name = "fmradio_headset_rx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_LP_FM_HEADSET_SPKR_STEREO_RX,
+ .profile = &ifmradio_ffa_headset_profile,
+ .channel_mode = 1,
+ .default_sample_rate = 8000,
+ .pamp_on = msm_snddev_hsed_voltage_on,
+ .pamp_off = msm_snddev_hsed_voltage_off,
+ .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL,
+};
+
+static struct platform_device msm_ifmradio_ffa_headset_device = {
+ .name = "snddev_icodec",
+ .id = 11,
+ .dev = { .platform_data = &snddev_ifmradio_ffa_headset_data },
+};
+
+static struct snddev_ecodec_data snddev_bt_sco_earpiece_data = {
+ .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE),
+ .name = "bt_sco_rx",
+ .copp_id = 1,
+ .acdb_id = ACDB_ID_BT_SCO_SPKR,
+ .channel_mode = 1,
+ .conf_pcm_ctl_val = BT_SCO_PCM_CTL_VAL,
+ .conf_aux_codec_intf = BT_SCO_AUX_CODEC_INTF,
+ .conf_data_format_padding_val = BT_SCO_DATA_FORMAT_PADDING,
+ .max_voice_rx_vol[VOC_NB_INDEX] = 400,
+ .min_voice_rx_vol[VOC_NB_INDEX] = -1100,
+ .max_voice_rx_vol[VOC_WB_INDEX] = 400,
+ .min_voice_rx_vol[VOC_WB_INDEX] = -1100,
+};
+
+static struct snddev_ecodec_data snddev_bt_sco_mic_data = {
+ .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+ .name = "bt_sco_tx",
+ .copp_id = 1,
+ .acdb_id = ACDB_ID_BT_SCO_MIC,
+ .channel_mode = 1,
+ .conf_pcm_ctl_val = BT_SCO_PCM_CTL_VAL,
+ .conf_aux_codec_intf = BT_SCO_AUX_CODEC_INTF,
+ .conf_data_format_padding_val = BT_SCO_DATA_FORMAT_PADDING,
+};
+
+struct platform_device msm_bt_sco_earpiece_device = {
+ .name = "msm_snddev_ecodec",
+ .id = 0,
+ .dev = { .platform_data = &snddev_bt_sco_earpiece_data },
+};
+
+struct platform_device msm_bt_sco_mic_device = {
+ .name = "msm_snddev_ecodec",
+ .id = 1,
+ .dev = { .platform_data = &snddev_bt_sco_mic_data },
+};
+
+static struct adie_codec_action_unit idual_mic_endfire_8KHz_osr256_actions[] =
+ MIC1_LEFT_LINE_IN_RIGHT_8000_OSR_256;
+
+static struct adie_codec_hwsetting_entry idual_mic_endfire_settings[] = {
+ {
+ .freq_plan = 8000,
+ .osr = 256,
+ .actions = idual_mic_endfire_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(idual_mic_endfire_8KHz_osr256_actions),
+ }, /* 8KHz profile can be used for 16KHz */
+ {
+ .freq_plan = 16000,
+ .osr = 256,
+ .actions = idual_mic_endfire_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(idual_mic_endfire_8KHz_osr256_actions),
+ }, /* 8KHz profile can be used for 48KHz */
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = idual_mic_endfire_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(idual_mic_endfire_8KHz_osr256_actions),
+ }
+};
+
+static struct adie_codec_dev_profile idual_mic_endfire_profile = {
+ .path_type = ADIE_CODEC_TX,
+ .settings = idual_mic_endfire_settings,
+ .setting_sz = ARRAY_SIZE(idual_mic_endfire_settings),
+};
+
+static enum hsed_controller idual_mic_endfire_pmctl_id[] = {
+ PM_HSED_CONTROLLER_0, PM_HSED_CONTROLLER_2
+};
+
+static struct snddev_icodec_data snddev_idual_mic_endfire_data = {
+ .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+ .name = "handset_dual_mic_endfire_tx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_HANDSET_MIC_ENDFIRE,
+ .profile = &idual_mic_endfire_profile,
+ .channel_mode = 2,
+ .default_sample_rate = 48000,
+ .pmctl_id = idual_mic_endfire_pmctl_id,
+ .pmctl_id_sz = ARRAY_SIZE(idual_mic_endfire_pmctl_id),
+ .pamp_on = NULL,
+ .pamp_off = NULL,
+};
+
+static struct platform_device msm_idual_mic_endfire_device = {
+ .name = "snddev_icodec",
+ .id = 12,
+ .dev = { .platform_data = &snddev_idual_mic_endfire_data },
+};
+
+
+static struct snddev_icodec_data\
+ snddev_idual_mic_endfire_real_stereo_data = {
+ .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+ .name = "handset_dual_mic_endfire_tx_real_stereo",
+ .copp_id = 0,
+ .acdb_id = PSEUDO_ACDB_ID,
+ .profile = &idual_mic_endfire_profile,
+ .channel_mode = REAL_STEREO_CHANNEL_MODE,
+ .default_sample_rate = 48000,
+ .pmctl_id = idual_mic_endfire_pmctl_id,
+ .pmctl_id_sz = ARRAY_SIZE(idual_mic_endfire_pmctl_id),
+ .pamp_on = NULL,
+ .pamp_off = NULL,
+};
+
+static struct platform_device msm_real_stereo_tx_device = {
+ .name = "snddev_icodec",
+ .id = 26,
+ .dev = { .platform_data =
+ &snddev_idual_mic_endfire_real_stereo_data },
+};
+
+static struct adie_codec_action_unit idual_mic_bs_8KHz_osr256_actions[] =
+ MIC1_LEFT_AUX_IN_RIGHT_8000_OSR_256;
+
+static struct adie_codec_hwsetting_entry idual_mic_broadside_settings[] = {
+ {
+ .freq_plan = 8000,
+ .osr = 256,
+ .actions = idual_mic_bs_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(idual_mic_bs_8KHz_osr256_actions),
+ }, /* 8KHz profile can be used for 16KHz */
+ {
+ .freq_plan = 16000,
+ .osr = 256,
+ .actions = idual_mic_bs_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(idual_mic_bs_8KHz_osr256_actions),
+ }, /* 8KHz profile can be used for 16KHz */
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = idual_mic_bs_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(idual_mic_bs_8KHz_osr256_actions),
+ }
+};
+
+static struct adie_codec_dev_profile idual_mic_broadside_profile = {
+ .path_type = ADIE_CODEC_TX,
+ .settings = idual_mic_broadside_settings,
+ .setting_sz = ARRAY_SIZE(idual_mic_broadside_settings),
+};
+
+static enum hsed_controller idual_mic_broadside_pmctl_id[] = {
+ PM_HSED_CONTROLLER_0, PM_HSED_CONTROLLER_2
+};
+
+static struct snddev_icodec_data snddev_idual_mic_broadside_data = {
+ .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+ .name = "handset_dual_mic_broadside_tx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_HANDSET_MIC_BROADSIDE,
+ .profile = &idual_mic_broadside_profile,
+ .channel_mode = 2,
+ .default_sample_rate = 48000,
+ .pmctl_id = idual_mic_broadside_pmctl_id,
+ .pmctl_id_sz = ARRAY_SIZE(idual_mic_broadside_pmctl_id),
+ .pamp_on = NULL,
+ .pamp_off = NULL,
+};
+
+static struct platform_device msm_idual_mic_broadside_device = {
+ .name = "snddev_icodec",
+ .id = 13,
+ .dev = { .platform_data = &snddev_idual_mic_broadside_data },
+};
+
+static struct adie_codec_action_unit ispk_dual_mic_ef_8KHz_osr256_actions[] =
+ SPEAKER_MIC1_LEFT_LINE_IN_RIGHT_8000_OSR_256;
+
+static struct adie_codec_hwsetting_entry ispk_dual_mic_ef_settings[] = {
+ {
+ .freq_plan = 8000,
+ .osr = 256,
+ .actions = ispk_dual_mic_ef_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(ispk_dual_mic_ef_8KHz_osr256_actions),
+ }, /* 8KHz profile can be used for 16Khz */
+ {
+ .freq_plan = 16000,
+ .osr = 256,
+ .actions = ispk_dual_mic_ef_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(ispk_dual_mic_ef_8KHz_osr256_actions),
+ }, /* 8KHz profile can be used for 48KHz */
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = ispk_dual_mic_ef_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(ispk_dual_mic_ef_8KHz_osr256_actions),
+ },
+};
+
+static struct adie_codec_dev_profile ispk_dual_mic_ef_profile = {
+ .path_type = ADIE_CODEC_TX,
+ .settings = ispk_dual_mic_ef_settings,
+ .setting_sz = ARRAY_SIZE(ispk_dual_mic_ef_settings),
+};
+
+static struct snddev_icodec_data snddev_spk_idual_mic_endfire_data = {
+ .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+ .name = "speaker_dual_mic_endfire_tx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_SPKR_PHONE_MIC_ENDFIRE,
+ .profile = &ispk_dual_mic_ef_profile,
+ .channel_mode = 2,
+ .default_sample_rate = 48000,
+ .pmctl_id = idual_mic_endfire_pmctl_id,
+ .pmctl_id_sz = ARRAY_SIZE(idual_mic_endfire_pmctl_id),
+ .pamp_on = NULL,
+ .pamp_off = NULL,
+};
+
+static struct platform_device msm_spk_idual_mic_endfire_device = {
+ .name = "snddev_icodec",
+ .id = 14,
+ .dev = { .platform_data = &snddev_spk_idual_mic_endfire_data },
+};
+
+static struct adie_codec_action_unit ispk_dual_mic_bs_8KHz_osr256_actions[] =
+ SPEAKER_MIC1_LEFT_AUX_IN_RIGHT_8000_OSR_256;
+
+static struct adie_codec_hwsetting_entry ispk_dual_mic_bs_settings[] = {
+ {
+ .freq_plan = 8000,
+ .osr = 256,
+ .actions = ispk_dual_mic_bs_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(ispk_dual_mic_bs_8KHz_osr256_actions),
+ }, /* 8KHz profile can be used for 16Khz */
+ {
+ .freq_plan = 16000,
+ .osr = 256,
+ .actions = ispk_dual_mic_bs_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(ispk_dual_mic_bs_8KHz_osr256_actions),
+ }, /* 8KHz profile can be used for 48KHz */
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = ispk_dual_mic_bs_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(ispk_dual_mic_bs_8KHz_osr256_actions),
+ },
+};
+
+static struct adie_codec_dev_profile ispk_dual_mic_bs_profile = {
+ .path_type = ADIE_CODEC_TX,
+ .settings = ispk_dual_mic_bs_settings,
+ .setting_sz = ARRAY_SIZE(ispk_dual_mic_bs_settings),
+};
+static struct snddev_icodec_data snddev_spk_idual_mic_broadside_data = {
+ .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+ .name = "speaker_dual_mic_broadside_tx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_SPKR_PHONE_MIC_BROADSIDE,
+ .profile = &ispk_dual_mic_bs_profile,
+ .channel_mode = 2,
+ .default_sample_rate = 48000,
+ .pmctl_id = idual_mic_broadside_pmctl_id,
+ .pmctl_id_sz = ARRAY_SIZE(idual_mic_broadside_pmctl_id),
+ .pamp_on = NULL,
+ .pamp_off = NULL,
+};
+
+static struct platform_device msm_spk_idual_mic_broadside_device = {
+ .name = "snddev_icodec",
+ .id = 15,
+ .dev = { .platform_data = &snddev_spk_idual_mic_broadside_data },
+};
+
+static struct adie_codec_action_unit itty_hs_mono_tx_8KHz_osr256_actions[] =
+ TTY_HEADSET_MONO_TX_8000_OSR_256;
+
+static struct adie_codec_hwsetting_entry itty_hs_mono_tx_settings[] = {
+ /* 8KHz, 16KHz, 48KHz TTY Tx devices can shared same set of actions */
+ {
+ .freq_plan = 8000,
+ .osr = 256,
+ .actions = itty_hs_mono_tx_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(itty_hs_mono_tx_8KHz_osr256_actions),
+ },
+ {
+ .freq_plan = 16000,
+ .osr = 256,
+ .actions = itty_hs_mono_tx_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(itty_hs_mono_tx_8KHz_osr256_actions),
+ },
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = itty_hs_mono_tx_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(itty_hs_mono_tx_8KHz_osr256_actions),
+ }
+};
+
+static struct adie_codec_dev_profile itty_hs_mono_tx_profile = {
+ .path_type = ADIE_CODEC_TX,
+ .settings = itty_hs_mono_tx_settings,
+ .setting_sz = ARRAY_SIZE(itty_hs_mono_tx_settings),
+};
+
+static struct snddev_icodec_data snddev_itty_hs_mono_tx_data = {
+ .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE | SNDDEV_CAP_TTY),
+ .name = "tty_headset_mono_tx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_TTY_HEADSET_MIC,
+ .profile = &itty_hs_mono_tx_profile,
+ .channel_mode = 1,
+ .default_sample_rate = 48000,
+ .pmctl_id = NULL,
+ .pmctl_id_sz = 0,
+ .pamp_on = NULL,
+ .pamp_off = NULL,
+};
+
+static struct platform_device msm_itty_hs_mono_tx_device = {
+ .name = "snddev_icodec",
+ .id = 16,
+ .dev = { .platform_data = &snddev_itty_hs_mono_tx_data },
+};
+
+static struct adie_codec_action_unit itty_hs_mono_rx_8KHz_osr256_actions[] =
+ TTY_HEADSET_MONO_RX_CLASS_D_8000_OSR_256;
+
+static struct adie_codec_action_unit itty_hs_mono_rx_16KHz_osr256_actions[] =
+ TTY_HEADSET_MONO_RX_CLASS_D_16000_OSR_256;
+
+static struct adie_codec_action_unit itty_hs_mono_rx_48KHz_osr256_actions[] =
+ TTY_HEADSET_MONO_RX_CLASS_D_48000_OSR_256;
+
+static struct adie_codec_hwsetting_entry itty_hs_mono_rx_settings[] = {
+ {
+ .freq_plan = 8000,
+ .osr = 256,
+ .actions = itty_hs_mono_rx_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(itty_hs_mono_rx_8KHz_osr256_actions),
+ },
+ {
+ .freq_plan = 16000,
+ .osr = 256,
+ .actions = itty_hs_mono_rx_16KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(itty_hs_mono_rx_16KHz_osr256_actions),
+ },
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = itty_hs_mono_rx_48KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(itty_hs_mono_rx_48KHz_osr256_actions),
+ }
+};
+
+static struct adie_codec_dev_profile itty_hs_mono_rx_profile = {
+ .path_type = ADIE_CODEC_RX,
+ .settings = itty_hs_mono_rx_settings,
+ .setting_sz = ARRAY_SIZE(itty_hs_mono_rx_settings),
+};
+
+static struct snddev_icodec_data snddev_itty_hs_mono_rx_data = {
+ .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE | SNDDEV_CAP_TTY),
+ .name = "tty_headset_mono_rx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_TTY_HEADSET_SPKR,
+ .profile = &itty_hs_mono_rx_profile,
+ .channel_mode = 1,
+ .default_sample_rate = 48000,
+ .pamp_on = NULL,
+ .pamp_off = NULL,
+ .max_voice_rx_vol[VOC_NB_INDEX] = 0,
+ .min_voice_rx_vol[VOC_NB_INDEX] = 0,
+ .max_voice_rx_vol[VOC_WB_INDEX] = 0,
+ .min_voice_rx_vol[VOC_WB_INDEX] = 0,
+};
+
+static struct platform_device msm_itty_hs_mono_rx_device = {
+ .name = "snddev_icodec",
+ .id = 17,
+ .dev = { .platform_data = &snddev_itty_hs_mono_rx_data },
+};
+
+static struct adie_codec_action_unit ispeaker_tx_8KHz_osr256_actions[] =
+ SPEAKER_TX_8000_OSR_256;
+
+static struct adie_codec_action_unit ispeaker_tx_48KHz_osr256_actions[] =
+ SPEAKER_TX_48000_OSR_256;
+
+static struct adie_codec_hwsetting_entry ispeaker_tx_settings[] = {
+ {
+ .freq_plan = 8000,
+ .osr = 256,
+ .actions = ispeaker_tx_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(ispeaker_tx_8KHz_osr256_actions),
+ },
+ { /* 8KHz profile is good for 16KHz */
+ .freq_plan = 16000,
+ .osr = 256,
+ .actions = ispeaker_tx_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(ispeaker_tx_8KHz_osr256_actions),
+ },
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = ispeaker_tx_48KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(ispeaker_tx_48KHz_osr256_actions),
+ }
+};
+
+static struct adie_codec_dev_profile ispeaker_tx_profile = {
+ .path_type = ADIE_CODEC_TX,
+ .settings = ispeaker_tx_settings,
+ .setting_sz = ARRAY_SIZE(ispeaker_tx_settings),
+};
+
+static enum hsed_controller ispk_pmctl_id[] = {PM_HSED_CONTROLLER_0};
+
+static struct snddev_icodec_data snddev_ispeaker_tx_data = {
+ .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+ .name = "speaker_mono_tx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_SPKR_PHONE_MIC,
+ .profile = &ispeaker_tx_profile,
+ .channel_mode = 1,
+ .pmctl_id = ispk_pmctl_id,
+ .pmctl_id_sz = ARRAY_SIZE(ispk_pmctl_id),
+ .default_sample_rate = 48000,
+ .pamp_on = msm_snddev_tx_route_config,
+ .pamp_off = msm_snddev_tx_route_deconfig,
+};
+
+static struct platform_device msm_ispeaker_tx_device = {
+ .name = "snddev_icodec",
+ .id = 18,
+ .dev = { .platform_data = &snddev_ispeaker_tx_data },
+};
+
+static struct adie_codec_action_unit iearpiece_ffa_48KHz_osr256_actions[] =
+ HANDSET_RX_48000_OSR_256_FFA;
+
+static struct adie_codec_hwsetting_entry iearpiece_ffa_settings[] = {
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = iearpiece_ffa_48KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(iearpiece_ffa_48KHz_osr256_actions),
+ }
+};
+
+static struct adie_codec_dev_profile iearpiece_ffa_profile = {
+ .path_type = ADIE_CODEC_RX,
+ .settings = iearpiece_ffa_settings,
+ .setting_sz = ARRAY_SIZE(iearpiece_ffa_settings),
+};
+
+static struct snddev_icodec_data snddev_iearpiece_ffa_data = {
+ .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE),
+ .name = "handset_rx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_HANDSET_SPKR,
+ .profile = &iearpiece_ffa_profile,
+ .channel_mode = 1,
+ .pmctl_id = NULL,
+ .pmctl_id_sz = 0,
+ .default_sample_rate = 48000,
+ .pamp_on = NULL,
+ .pamp_off = NULL,
+ .max_voice_rx_vol[VOC_NB_INDEX] = -700,
+ .min_voice_rx_vol[VOC_NB_INDEX] = -2200,
+ .max_voice_rx_vol[VOC_WB_INDEX] = -1400,
+ .min_voice_rx_vol[VOC_WB_INDEX] = -2900,
+};
+
+static struct platform_device msm_iearpiece_ffa_device = {
+ .name = "snddev_icodec",
+ .id = 19,
+ .dev = { .platform_data = &snddev_iearpiece_ffa_data },
+};
+
+static struct adie_codec_action_unit imic_ffa_8KHz_osr256_actions[] =
+ HANDSET_TX_8000_OSR_256_FFA;
+
+static struct adie_codec_action_unit imic_ffa_16KHz_osr256_actions[] =
+ HANDSET_TX_16000_OSR_256_FFA;
+
+static struct adie_codec_action_unit imic_ffa_48KHz_osr256_actions[] =
+ HANDSET_TX_48000_OSR_256_FFA;
+
+static struct adie_codec_hwsetting_entry imic_ffa_settings[] = {
+ {
+ .freq_plan = 8000,
+ .osr = 256,
+ .actions = imic_ffa_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(imic_ffa_8KHz_osr256_actions),
+ },
+ {
+ .freq_plan = 16000,
+ .osr = 256,
+ .actions = imic_ffa_16KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(imic_ffa_16KHz_osr256_actions),
+ },
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = imic_ffa_48KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(imic_ffa_48KHz_osr256_actions),
+ }
+};
+
+static struct adie_codec_dev_profile imic_ffa_profile = {
+ .path_type = ADIE_CODEC_TX,
+ .settings = imic_ffa_settings,
+ .setting_sz = ARRAY_SIZE(imic_ffa_settings),
+};
+
+static struct snddev_icodec_data snddev_imic_ffa_data = {
+ .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+ .name = "handset_tx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_HANDSET_MIC,
+ .profile = &imic_ffa_profile,
+ .channel_mode = 1,
+ .pmctl_id = imic_pmctl_id,
+ .pmctl_id_sz = ARRAY_SIZE(imic_pmctl_id),
+ .default_sample_rate = 48000,
+ .pamp_on = NULL,
+ .pamp_off = NULL,
+};
+
+static struct platform_device msm_imic_ffa_device = {
+ .name = "snddev_icodec",
+ .id = 20,
+ .dev = { .platform_data = &snddev_imic_ffa_data },
+};
+
+
+static struct adie_codec_action_unit
+ ihs_stereo_speaker_stereo_rx_48KHz_osr256_actions[] =
+ HEADSET_STEREO_SPEAKER_STEREO_RX_CAPLESS_48000_OSR_256;
+
+
+static struct adie_codec_hwsetting_entry
+ ihs_stereo_speaker_stereo_rx_settings[] = {
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = ihs_stereo_speaker_stereo_rx_48KHz_osr256_actions,
+ .action_sz =
+ ARRAY_SIZE(ihs_stereo_speaker_stereo_rx_48KHz_osr256_actions),
+ }
+};
+
+static struct adie_codec_dev_profile ihs_stereo_speaker_stereo_rx_profile = {
+ .path_type = ADIE_CODEC_RX,
+ .settings = ihs_stereo_speaker_stereo_rx_settings,
+ .setting_sz = ARRAY_SIZE(ihs_stereo_speaker_stereo_rx_settings),
+};
+
+static struct snddev_icodec_data snddev_ihs_stereo_speaker_stereo_rx_data = {
+ .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE),
+ .name = "headset_stereo_speaker_stereo_rx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_HEADSET_STEREO_PLUS_SPKR_STEREO_RX,
+ .profile = &ihs_stereo_speaker_stereo_rx_profile,
+ .channel_mode = 2,
+ .default_sample_rate = 48000,
+ .pamp_on = msm_snddev_poweramp_on,
+ .pamp_off = msm_snddev_poweramp_off,
+ .voltage_on = msm_snddev_hsed_voltage_on,
+ .voltage_off = msm_snddev_hsed_voltage_off,
+ .max_voice_rx_vol[VOC_NB_INDEX] = -500,
+ .min_voice_rx_vol[VOC_NB_INDEX] = -2000,
+ .max_voice_rx_vol[VOC_WB_INDEX] = -500,
+ .min_voice_rx_vol[VOC_WB_INDEX] = -2000,
+};
+
+static struct platform_device msm_ihs_stereo_speaker_stereo_rx_device = {
+ .name = "snddev_icodec",
+ .id = 21,
+ .dev = { .platform_data = &snddev_ihs_stereo_speaker_stereo_rx_data },
+};
+
+static struct snddev_mi2s_data snddev_mi2s_stereo_rx_data = {
+ .capability = SNDDEV_CAP_RX ,
+ .name = "hdmi_stereo_rx",
+ .copp_id = 3,
+ .acdb_id = ACDB_ID_HDMI,
+ .channel_mode = 2,
+ .sd_lines = MI2S_SD_0,
+ .route = msm_snddev_tx_route_config,
+ .deroute = msm_snddev_tx_route_deconfig,
+ .default_sample_rate = 48000,
+};
+
+static struct platform_device msm_snddev_mi2s_stereo_rx_device = {
+ .name = "snddev_mi2s",
+ .id = 0,
+ .dev = { .platform_data = &snddev_mi2s_stereo_rx_data },
+};
+
+
+static struct snddev_mi2s_data snddev_mi2s_fm_tx_data = {
+ .capability = SNDDEV_CAP_TX ,
+ .name = "fmradio_stereo_tx",
+ .copp_id = 2,
+ .acdb_id = ACDB_ID_FM_TX,
+ .channel_mode = 2,
+ .sd_lines = MI2S_SD_3,
+ .route = NULL,
+ .deroute = NULL,
+ .default_sample_rate = 48000,
+};
+
+static struct platform_device msm_snddev_mi2s_fm_tx_device = {
+ .name = "snddev_mi2s",
+ .id = 1,
+ .dev = { .platform_data = &snddev_mi2s_fm_tx_data},
+};
+
+static struct snddev_icodec_data snddev_fluid_imic_tx_data = {
+ .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+ .name = "handset_tx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_SPKR_PHONE_MIC,
+ .profile = &ispeaker_tx_profile,
+ .channel_mode = 1,
+ .pmctl_id = ispk_pmctl_id,
+ .pmctl_id_sz = ARRAY_SIZE(ispk_pmctl_id),
+ .default_sample_rate = 48000,
+ .pamp_on = msm_snddev_tx_route_config,
+ .pamp_off = msm_snddev_tx_route_deconfig,
+};
+
+static struct platform_device msm_fluid_imic_tx_device = {
+ .name = "snddev_icodec",
+ .id = 22,
+ .dev = { .platform_data = &snddev_fluid_imic_tx_data },
+};
+
+static struct snddev_icodec_data snddev_fluid_iearpiece_rx_data = {
+ .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE),
+ .name = "handset_rx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_SPKR_PHONE_STEREO,
+ .profile = &ispeaker_rx_profile,
+ .channel_mode = 2,
+ .pmctl_id = NULL,
+ .pmctl_id_sz = 0,
+ .default_sample_rate = 48000,
+ .pamp_on = &msm_snddev_poweramp_on,
+ .pamp_off = &msm_snddev_poweramp_off,
+ .max_voice_rx_vol[VOC_NB_INDEX] = -500,
+ .min_voice_rx_vol[VOC_NB_INDEX] = -1000,
+ .max_voice_rx_vol[VOC_WB_INDEX] = -500,
+ .min_voice_rx_vol[VOC_WB_INDEX] = -1000,
+};
+
+static struct platform_device msm_fluid_iearpeice_rx_device = {
+ .name = "snddev_icodec",
+ .id = 23,
+ .dev = { .platform_data = &snddev_fluid_iearpiece_rx_data },
+};
+
+static struct adie_codec_action_unit fluid_idual_mic_ef_8KHz_osr256_actions[] =
+ MIC1_LEFT_AUX_IN_RIGHT_8000_OSR_256;
+
+static struct adie_codec_hwsetting_entry fluid_idual_mic_endfire_settings[] = {
+ {
+ .freq_plan = 8000,
+ .osr = 256,
+ .actions = fluid_idual_mic_ef_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(fluid_idual_mic_ef_8KHz_osr256_actions),
+ }, /* 8KHz profile can be used for 16KHz */
+ {
+ .freq_plan = 16000,
+ .osr = 256,
+ .actions = fluid_idual_mic_ef_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(fluid_idual_mic_ef_8KHz_osr256_actions),
+ }, /* 8KHz profile can also be used for 48KHz */
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = fluid_idual_mic_ef_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(fluid_idual_mic_ef_8KHz_osr256_actions),
+ }
+};
+
+static struct adie_codec_dev_profile fluid_idual_mic_endfire_profile = {
+ .path_type = ADIE_CODEC_TX,
+ .settings = fluid_idual_mic_endfire_settings,
+ .setting_sz = ARRAY_SIZE(fluid_idual_mic_endfire_settings),
+};
+
+static enum hsed_controller fluid_idual_mic_endfire_pmctl_id[] = {
+ PM_HSED_CONTROLLER_0, PM_HSED_CONTROLLER_2
+};
+
+static struct snddev_icodec_data snddev_fluid_idual_mic_endfire_data = {
+ .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+ .name = "handset_dual_mic_endfire_tx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_SPKR_PHONE_MIC_ENDFIRE,
+ .profile = &fluid_idual_mic_endfire_profile,
+ .channel_mode = 2,
+ .default_sample_rate = 48000,
+ .pmctl_id = fluid_idual_mic_endfire_pmctl_id,
+ .pmctl_id_sz = ARRAY_SIZE(fluid_idual_mic_endfire_pmctl_id),
+ .pamp_on = msm_snddev_tx_route_config,
+ .pamp_off = msm_snddev_tx_route_deconfig,
+};
+
+static struct platform_device msm_fluid_idual_mic_endfire_device = {
+ .name = "snddev_icodec",
+ .id = 24,
+ .dev = { .platform_data = &snddev_fluid_idual_mic_endfire_data },
+};
+
+static struct snddev_icodec_data snddev_fluid_spk_idual_mic_endfire_data = {
+ .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+ .name = "speaker_dual_mic_endfire_tx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_SPKR_PHONE_MIC_ENDFIRE,
+ .profile = &fluid_idual_mic_endfire_profile,
+ .channel_mode = 2,
+ .default_sample_rate = 48000,
+ .pmctl_id = fluid_idual_mic_endfire_pmctl_id,
+ .pmctl_id_sz = ARRAY_SIZE(fluid_idual_mic_endfire_pmctl_id),
+ .pamp_on = msm_snddev_tx_route_config,
+ .pamp_off = msm_snddev_tx_route_deconfig,
+};
+
+static struct platform_device msm_fluid_spk_idual_mic_endfire_device = {
+ .name = "snddev_icodec",
+ .id = 25,
+ .dev = { .platform_data = &snddev_fluid_spk_idual_mic_endfire_data },
+};
+
+static struct snddev_virtual_data snddev_a2dp_tx_data = {
+ .capability = SNDDEV_CAP_TX,
+ .name = "a2dp_tx",
+ .copp_id = 5,
+ .acdb_id = PSEUDO_ACDB_ID,
+};
+
+static struct snddev_virtual_data snddev_a2dp_rx_data = {
+ .capability = SNDDEV_CAP_RX,
+ .name = "a2dp_rx",
+ .copp_id = 2,
+ .acdb_id = PSEUDO_ACDB_ID,
+};
+
+static struct platform_device msm_a2dp_rx_device = {
+ .name = "snddev_virtual",
+ .id = 0,
+ .dev = { .platform_data = &snddev_a2dp_rx_data },
+};
+
+static struct platform_device msm_a2dp_tx_device = {
+ .name = "snddev_virtual",
+ .id = 1,
+ .dev = { .platform_data = &snddev_a2dp_tx_data },
+};
+
+static struct snddev_virtual_data snddev_uplink_rx_data = {
+ .capability = SNDDEV_CAP_RX,
+ .name = "uplink_rx",
+ .copp_id = 5,
+ .acdb_id = PSEUDO_ACDB_ID,
+};
+
+static struct platform_device msm_uplink_rx_device = {
+ .name = "snddev_virtual",
+ .id = 2,
+ .dev = { .platform_data = &snddev_uplink_rx_data },
+};
+
+static struct platform_device *snd_devices_ffa[] __initdata = {
+ &msm_iearpiece_ffa_device,
+ &msm_imic_ffa_device,
+ &msm_ifmradio_handset_device,
+ &msm_ihs_ffa_stereo_rx_device,
+ &msm_ihs_ffa_mono_rx_device,
+ &msm_ihs_mono_tx_device,
+ &msm_bt_sco_earpiece_device,
+ &msm_bt_sco_mic_device,
+ &msm_ispeaker_rx_device,
+ &msm_ifmradio_speaker_device,
+ &msm_ifmradio_ffa_headset_device,
+ &msm_idual_mic_endfire_device,
+ &msm_idual_mic_broadside_device,
+ &msm_spk_idual_mic_endfire_device,
+ &msm_spk_idual_mic_broadside_device,
+ &msm_itty_hs_mono_tx_device,
+ &msm_itty_hs_mono_rx_device,
+ &msm_ispeaker_tx_device,
+ &msm_ihs_stereo_speaker_stereo_rx_device,
+ &msm_a2dp_rx_device,
+ &msm_a2dp_tx_device,
+ &msm_snddev_mi2s_stereo_rx_device,
+ &msm_snddev_mi2s_fm_tx_device,
+ &msm_uplink_rx_device,
+ &msm_real_stereo_tx_device,
+};
+
+static struct platform_device *snd_devices_surf[] __initdata = {
+ &msm_iearpiece_device,
+ &msm_imic_device,
+ &msm_ihs_stereo_rx_device,
+ &msm_ihs_mono_rx_device,
+ &msm_ihs_mono_tx_device,
+ &msm_bt_sco_earpiece_device,
+ &msm_bt_sco_mic_device,
+ &msm_ifmradio_handset_device,
+ &msm_ispeaker_rx_device,
+ &msm_ifmradio_speaker_device,
+ &msm_ifmradio_headset_device,
+ &msm_itty_hs_mono_tx_device,
+ &msm_itty_hs_mono_rx_device,
+ &msm_ispeaker_tx_device,
+ &msm_ihs_stereo_speaker_stereo_rx_device,
+ &msm_a2dp_rx_device,
+ &msm_a2dp_tx_device,
+ &msm_snddev_mi2s_stereo_rx_device,
+ &msm_snddev_mi2s_fm_tx_device,
+ &msm_uplink_rx_device,
+};
+
+static struct platform_device *snd_devices_fluid[] __initdata = {
+ &msm_ihs_stereo_rx_device,
+ &msm_ihs_mono_rx_device,
+ &msm_ihs_mono_tx_device,
+ &msm_ispeaker_rx_device,
+ &msm_ispeaker_tx_device,
+ &msm_fluid_imic_tx_device,
+ &msm_fluid_iearpeice_rx_device,
+ &msm_fluid_idual_mic_endfire_device,
+ &msm_fluid_spk_idual_mic_endfire_device,
+ &msm_a2dp_rx_device,
+ &msm_a2dp_tx_device,
+ &msm_snddev_mi2s_stereo_rx_device,
+ &msm_uplink_rx_device,
+ &msm_ifmradio_speaker_device,
+ &msm_ifmradio_headset_device,
+};
+
+#ifdef CONFIG_DEBUG_FS
+static void snddev_hsed_config_modify_setting(int type)
+{
+ struct platform_device *device;
+ struct snddev_icodec_data *icodec_data;
+
+ device = &msm_ihs_ffa_stereo_rx_device;
+ icodec_data = (struct snddev_icodec_data *)device->dev.platform_data;
+
+ if (icodec_data) {
+ if (type == 1) {
+ icodec_data->voltage_on = NULL;
+ icodec_data->voltage_off = NULL;
+ icodec_data->profile->settings =
+ ihs_ffa_stereo_rx_class_d_legacy_settings;
+ icodec_data->profile->setting_sz =
+ ARRAY_SIZE(ihs_ffa_stereo_rx_class_d_legacy_settings);
+ } else if (type == 2) {
+ icodec_data->voltage_on = NULL;
+ icodec_data->voltage_off = NULL;
+ icodec_data->profile->settings =
+ ihs_ffa_stereo_rx_class_ab_legacy_settings;
+ icodec_data->profile->setting_sz =
+ ARRAY_SIZE(ihs_ffa_stereo_rx_class_ab_legacy_settings);
+ }
+ }
+}
+
+static void snddev_hsed_config_restore_setting(void)
+{
+ struct platform_device *device;
+ struct snddev_icodec_data *icodec_data;
+
+ device = &msm_ihs_ffa_stereo_rx_device;
+ icodec_data = (struct snddev_icodec_data *)device->dev.platform_data;
+
+ if (icodec_data) {
+ icodec_data->voltage_on = msm_snddev_hsed_voltage_on;
+ icodec_data->voltage_off = msm_snddev_hsed_voltage_off;
+ icodec_data->profile->settings = ihs_ffa_stereo_rx_settings;
+ icodec_data->profile->setting_sz =
+ ARRAY_SIZE(ihs_ffa_stereo_rx_settings);
+ }
+}
+
+static ssize_t snddev_hsed_config_debug_write(struct file *filp,
+ const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+ char *lb_str = filp->private_data;
+ char cmd;
+
+ if (get_user(cmd, ubuf))
+ return -EFAULT;
+
+ if (!strcmp(lb_str, "msm_hsed_config")) {
+ switch (cmd) {
+ case '0':
+ snddev_hsed_config_restore_setting();
+ break;
+
+ case '1':
+ snddev_hsed_config_modify_setting(1);
+ break;
+
+ case '2':
+ snddev_hsed_config_modify_setting(2);
+ break;
+
+ default:
+ break;
+ }
+ }
+ return cnt;
+}
+
+static int snddev_hsed_config_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static const struct file_operations snddev_hsed_config_debug_fops = {
+ .open = snddev_hsed_config_debug_open,
+ .write = snddev_hsed_config_debug_write
+};
+#endif
+
+void __ref msm_snddev_init(void)
+{
+ if (machine_is_msm7x30_ffa() || machine_is_msm8x55_ffa() ||
+ machine_is_msm8x55_svlte_ffa()) {
+ platform_add_devices(snd_devices_ffa,
+ ARRAY_SIZE(snd_devices_ffa));
+#ifdef CONFIG_DEBUG_FS
+ debugfs_hsed_config = debugfs_create_file("msm_hsed_config",
+ S_IFREG | S_IRUGO, NULL,
+ (void *) "msm_hsed_config", &snddev_hsed_config_debug_fops);
+#endif
+ } else if (machine_is_msm7x30_surf() || machine_is_msm8x55_surf() ||
+ machine_is_msm8x55_svlte_surf())
+ platform_add_devices(snd_devices_surf,
+ ARRAY_SIZE(snd_devices_surf));
+ else if (machine_is_msm7x30_fluid())
+ platform_add_devices(snd_devices_fluid,
+ ARRAY_SIZE(snd_devices_fluid));
+ else
+ pr_err("%s: Unknown machine type\n", __func__);
+}
diff --git a/arch/arm/mach-msm/qdsp5v2/snddev_data_timpani.c b/arch/arm/mach-msm/qdsp5v2/snddev_data_timpani.c
new file mode 100644
index 0000000..c0a48c8
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/snddev_data_timpani.c
@@ -0,0 +1,1006 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/mfd/msm-adie-codec.h>
+#include <linux/uaccess.h>
+#include <asm/mach-types.h>
+#include <mach/qdsp5v2/aux_pcm.h>
+#include <mach/qdsp5v2/snddev_ecodec.h>
+#include <mach/board.h>
+#include <mach/qdsp5v2/snddev_icodec.h>
+#include <mach/qdsp5v2/snddev_mi2s.h>
+#include <mach/qdsp5v2/mi2s.h>
+#include <mach/qdsp5v2/audio_acdb_def.h>
+#include <mach/qdsp5v2/snddev_virtual.h>
+#include "timpani_profile_7x30.h"
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+
+/* define the value for BT_SCO */
+#define BT_SCO_PCM_CTL_VAL (PCM_CTL__RPCM_WIDTH__LINEAR_V |\
+ PCM_CTL__TPCM_WIDTH__LINEAR_V)
+#define BT_SCO_DATA_FORMAT_PADDING (DATA_FORMAT_PADDING_INFO__RPCM_FORMAT_V |\
+ DATA_FORMAT_PADDING_INFO__TPCM_FORMAT_V)
+#define BT_SCO_AUX_CODEC_INTF AUX_CODEC_INTF_CTL__PCMINTF_DATA_EN_V
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *debugfs_hsed_config;
+static void snddev_hsed_config_modify_setting(int type);
+static void snddev_hsed_config_restore_setting(void);
+#endif
+
+static struct adie_codec_action_unit iearpiece_ffa_48KHz_osr256_actions[] =
+ EAR_PRI_MONO_8000_OSR_256; /* 8000 profile also works for 48k */
+
+static struct adie_codec_hwsetting_entry iearpiece_ffa_settings[] = {
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = iearpiece_ffa_48KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(iearpiece_ffa_48KHz_osr256_actions),
+ }
+};
+
+static struct adie_codec_dev_profile iearpiece_ffa_profile = {
+ .path_type = ADIE_CODEC_RX,
+ .settings = iearpiece_ffa_settings,
+ .setting_sz = ARRAY_SIZE(iearpiece_ffa_settings),
+};
+
+static struct snddev_icodec_data snddev_iearpiece_ffa_data = {
+ .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE),
+ .name = "handset_rx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_HANDSET_SPKR,
+ .profile = &iearpiece_ffa_profile,
+ .channel_mode = 1,
+ .pmctl_id = NULL,
+ .pmctl_id_sz = 0,
+ .default_sample_rate = 48000,
+ .pamp_on = NULL,
+ .pamp_off = NULL,
+ .property = SIDE_TONE_MASK,
+ .max_voice_rx_vol[VOC_NB_INDEX] = -700,
+ .min_voice_rx_vol[VOC_NB_INDEX] = -2200,
+ .max_voice_rx_vol[VOC_WB_INDEX] = -1400,
+ .min_voice_rx_vol[VOC_WB_INDEX] = -2900,
+};
+
+static struct platform_device msm_iearpiece_ffa_device = {
+ .name = "snddev_icodec",
+ .id = 19,
+ .dev = { .platform_data = &snddev_iearpiece_ffa_data },
+};
+
+static struct adie_codec_action_unit imic_ffa_48KHz_osr256_actions[] =
+ AMIC_PRI_MONO_8000_OSR_256; /* 8000 profile also works for 48k */
+
+static struct adie_codec_hwsetting_entry imic_ffa_settings[] = {
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = imic_ffa_48KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(imic_ffa_48KHz_osr256_actions),
+ }
+};
+
+static enum hsed_controller imic_pmctl_id[] = {PM_HSED_CONTROLLER_0};
+
+static struct adie_codec_dev_profile imic_ffa_profile = {
+ .path_type = ADIE_CODEC_TX,
+ .settings = imic_ffa_settings,
+ .setting_sz = ARRAY_SIZE(imic_ffa_settings),
+};
+
+static struct snddev_icodec_data snddev_imic_ffa_data = {
+ .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+ .name = "handset_tx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_HANDSET_MIC,
+ .profile = &imic_ffa_profile,
+ .channel_mode = 1,
+ .pmctl_id = imic_pmctl_id,
+ .pmctl_id_sz = ARRAY_SIZE(imic_pmctl_id),
+ .default_sample_rate = 48000,
+ .pamp_on = NULL,
+ .pamp_off = NULL,
+};
+
+static struct platform_device msm_imic_ffa_device = {
+ .name = "snddev_icodec",
+ .id = 20,
+ .dev = { .platform_data = &snddev_imic_ffa_data },
+};
+
+static struct adie_codec_action_unit ispkr_stereo_48KHz_osr256_actions[] =
+ SPEAKER_PRI_STEREO_48000_OSR_256;
+
+static struct adie_codec_hwsetting_entry ispkr_stereo_settings[] = {
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = ispkr_stereo_48KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(ispkr_stereo_48KHz_osr256_actions),
+ }
+};
+
+static struct adie_codec_dev_profile ispkr_stereo_profile = {
+ .path_type = ADIE_CODEC_RX,
+ .settings = ispkr_stereo_settings,
+ .setting_sz = ARRAY_SIZE(ispkr_stereo_settings),
+};
+
+static struct snddev_icodec_data snddev_ispkr_stereo_data = {
+ .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE),
+ .name = "speaker_stereo_rx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_SPKR_PHONE_STEREO,
+ .profile = &ispkr_stereo_profile,
+ .channel_mode = 2,
+ .pmctl_id = NULL,
+ .pmctl_id_sz = 0,
+ .default_sample_rate = 48000,
+ .pamp_on = msm_snddev_poweramp_on,
+ .pamp_off = msm_snddev_poweramp_off,
+ .max_voice_rx_vol[VOC_NB_INDEX] = 1000,
+ .min_voice_rx_vol[VOC_NB_INDEX] = -500,
+ .max_voice_rx_vol[VOC_WB_INDEX] = 1000,
+ .min_voice_rx_vol[VOC_WB_INDEX] = -500
+};
+
+static struct platform_device msm_ispkr_stereo_device = {
+ .name = "snddev_icodec",
+ .id = 8,
+ .dev = { .platform_data = &snddev_ispkr_stereo_data },
+};
+
+static struct adie_codec_action_unit iheadset_mic_tx_osr256_actions[] =
+ AMIC1_HEADSET_TX_MONO_PRIMARY_OSR256;
+
+static struct adie_codec_hwsetting_entry iheadset_mic_tx_settings[] = {
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = iheadset_mic_tx_osr256_actions,
+ .action_sz = ARRAY_SIZE(iheadset_mic_tx_osr256_actions),
+ }
+};
+
+static struct adie_codec_dev_profile iheadset_mic_profile = {
+ .path_type = ADIE_CODEC_TX,
+ .settings = iheadset_mic_tx_settings,
+ .setting_sz = ARRAY_SIZE(iheadset_mic_tx_settings),
+};
+
+static struct snddev_icodec_data snddev_headset_mic_data = {
+ .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+ .name = "headset_mono_tx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_HEADSET_MIC,
+ .profile = &iheadset_mic_profile,
+ .channel_mode = 1,
+ .pmctl_id = NULL,
+ .pmctl_id_sz = 0,
+ .default_sample_rate = 48000,
+ .pamp_on = msm_snddev_tx_route_config,
+ .pamp_off = msm_snddev_tx_route_deconfig,
+};
+
+static struct platform_device msm_headset_mic_device = {
+ .name = "snddev_icodec",
+ .id = 6,
+ .dev = { .platform_data = &snddev_headset_mic_data },
+};
+
+static struct snddev_mi2s_data snddev_mi2s_fm_tx_data = {
+ .capability = SNDDEV_CAP_TX ,
+ .name = "fmradio_stereo_tx",
+ .copp_id = 2,
+ .acdb_id = ACDB_ID_FM_TX,
+ .channel_mode = 2,
+ .sd_lines = MI2S_SD_3,
+ .route = NULL,
+ .deroute = NULL,
+ .default_sample_rate = 48000,
+};
+
+static struct platform_device msm_snddev_mi2s_fm_tx_device = {
+ .name = "snddev_mi2s",
+ .id = 1,
+ .dev = { .platform_data = &snddev_mi2s_fm_tx_data},
+};
+
+static struct snddev_mi2s_data snddev_mi2s_fm_rx_data = {
+ .capability = SNDDEV_CAP_RX ,
+ .name = "fmradio_stereo_rx",
+ .copp_id = 3,
+ .acdb_id = ACDB_ID_FM_RX,
+ .channel_mode = 2,
+ .sd_lines = MI2S_SD_3,
+ .route = NULL,
+ .deroute = NULL,
+ .default_sample_rate = 48000,
+};
+
+static struct platform_device msm_snddev_mi2s_fm_rx_device = {
+ .name = "snddev_mi2s",
+ .id = 2,
+ .dev = { .platform_data = &snddev_mi2s_fm_rx_data},
+};
+
+static struct snddev_ecodec_data snddev_bt_sco_earpiece_data = {
+ .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE),
+ .name = "bt_sco_rx",
+ .copp_id = 1,
+ .acdb_id = ACDB_ID_BT_SCO_SPKR,
+ .channel_mode = 1,
+ .conf_pcm_ctl_val = BT_SCO_PCM_CTL_VAL,
+ .conf_aux_codec_intf = BT_SCO_AUX_CODEC_INTF,
+ .conf_data_format_padding_val = BT_SCO_DATA_FORMAT_PADDING,
+ .max_voice_rx_vol[VOC_NB_INDEX] = 400,
+ .min_voice_rx_vol[VOC_NB_INDEX] = -1100,
+ .max_voice_rx_vol[VOC_WB_INDEX] = 400,
+ .min_voice_rx_vol[VOC_WB_INDEX] = -1100,
+};
+
+static struct snddev_ecodec_data snddev_bt_sco_mic_data = {
+ .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+ .name = "bt_sco_tx",
+ .copp_id = 1,
+ .acdb_id = ACDB_ID_BT_SCO_MIC,
+ .channel_mode = 1,
+ .conf_pcm_ctl_val = BT_SCO_PCM_CTL_VAL,
+ .conf_aux_codec_intf = BT_SCO_AUX_CODEC_INTF,
+ .conf_data_format_padding_val = BT_SCO_DATA_FORMAT_PADDING,
+};
+
+static struct platform_device msm_bt_sco_earpiece_device = {
+ .name = "msm_snddev_ecodec",
+ .id = 0,
+ .dev = { .platform_data = &snddev_bt_sco_earpiece_data },
+};
+
+static struct platform_device msm_bt_sco_mic_device = {
+ .name = "msm_snddev_ecodec",
+ .id = 1,
+ .dev = { .platform_data = &snddev_bt_sco_mic_data },
+};
+
+static struct adie_codec_action_unit headset_ab_cpls_48KHz_osr256_actions[] =
+ HEADSET_AB_CPLS_48000_OSR_256;
+
+static struct adie_codec_hwsetting_entry headset_ab_cpls_settings[] = {
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = headset_ab_cpls_48KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(headset_ab_cpls_48KHz_osr256_actions),
+ }
+};
+
+static struct adie_codec_dev_profile headset_ab_cpls_profile = {
+ .path_type = ADIE_CODEC_RX,
+ .settings = headset_ab_cpls_settings,
+ .setting_sz = ARRAY_SIZE(headset_ab_cpls_settings),
+};
+
+static struct snddev_icodec_data snddev_ihs_stereo_rx_data = {
+ .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE),
+ .name = "headset_stereo_rx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_HEADSET_SPKR_STEREO,
+ .profile = &headset_ab_cpls_profile,
+ .channel_mode = 2,
+ .pmctl_id = NULL,
+ .pmctl_id_sz = 0,
+ .default_sample_rate = 48000,
+ .pamp_on = NULL,
+ .pamp_off = NULL,
+ .property = SIDE_TONE_MASK,
+ .voltage_on = msm_snddev_hsed_voltage_on,
+ .voltage_off = msm_snddev_hsed_voltage_off,
+ .max_voice_rx_vol[VOC_NB_INDEX] = -700,
+ .min_voice_rx_vol[VOC_NB_INDEX] = -2200,
+ .max_voice_rx_vol[VOC_WB_INDEX] = -900,
+ .min_voice_rx_vol[VOC_WB_INDEX] = -2400,
+};
+
+static struct platform_device msm_headset_stereo_device = {
+ .name = "snddev_icodec",
+ .id = 2,
+ .dev = { .platform_data = &snddev_ihs_stereo_rx_data },
+};
+
+/*debug FS interface is exposed to test Class D and class AB mode
+ * amplifers for headset device folloowing options are supported
+ * 0 -> settings will be restored
+ * 1 -> Cladd D mode is selected
+ * 2 -> Class AB mode is selected
+*/
+#ifdef CONFIG_DEBUG_FS
+static struct adie_codec_action_unit
+ ihs_stereo_rx_class_d_legacy_48KHz_osr256_actions[] =
+ HPH_PRI_D_LEG_STEREO;
+
+static struct adie_codec_hwsetting_entry
+ ihs_stereo_rx_class_d_legacy_settings[] = {
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions =
+ ihs_stereo_rx_class_d_legacy_48KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE
+ (ihs_stereo_rx_class_d_legacy_48KHz_osr256_actions),
+ }
+};
+
+static struct adie_codec_action_unit
+ ihs_stereo_rx_class_ab_legacy_48KHz_osr256_actions[] =
+ HPH_PRI_AB_LEG_STEREO;
+
+static struct adie_codec_hwsetting_entry
+ ihs_stereo_rx_class_ab_legacy_settings[] = {
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions =
+ ihs_stereo_rx_class_ab_legacy_48KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE
+ (ihs_stereo_rx_class_ab_legacy_48KHz_osr256_actions),
+ }
+};
+
+static void snddev_hsed_config_modify_setting(int type)
+{
+ struct platform_device *device;
+ struct snddev_icodec_data *icodec_data;
+
+ device = &msm_headset_stereo_device;
+ icodec_data = (struct snddev_icodec_data *)device->dev.platform_data;
+
+ if (icodec_data) {
+ if (type == 1) {
+ icodec_data->voltage_on = NULL;
+ icodec_data->voltage_off = NULL;
+ icodec_data->profile->settings =
+ ihs_stereo_rx_class_d_legacy_settings;
+ icodec_data->profile->setting_sz =
+ ARRAY_SIZE(ihs_stereo_rx_class_d_legacy_settings);
+ } else if (type == 2) {
+ icodec_data->voltage_on = NULL;
+ icodec_data->voltage_off = NULL;
+ icodec_data->profile->settings =
+ ihs_stereo_rx_class_ab_legacy_settings;
+ icodec_data->profile->setting_sz =
+ ARRAY_SIZE(ihs_stereo_rx_class_ab_legacy_settings);
+ }
+ }
+}
+
+static void snddev_hsed_config_restore_setting(void)
+{
+ struct platform_device *device;
+ struct snddev_icodec_data *icodec_data;
+
+ device = &msm_headset_stereo_device;
+ icodec_data = device->dev.platform_data;
+
+ if (icodec_data) {
+ icodec_data->voltage_on = msm_snddev_hsed_voltage_on;
+ icodec_data->voltage_off = msm_snddev_hsed_voltage_off;
+ icodec_data->profile->settings = headset_ab_cpls_settings;
+ icodec_data->profile->setting_sz =
+ ARRAY_SIZE(headset_ab_cpls_settings);
+ }
+}
+
+static ssize_t snddev_hsed_config_debug_write(struct file *filp,
+ const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+ char *lb_str = filp->private_data;
+ char cmd;
+
+ if (get_user(cmd, ubuf))
+ return -EFAULT;
+
+ if (!strcmp(lb_str, "msm_hsed_config")) {
+ switch (cmd) {
+ case '0':
+ snddev_hsed_config_restore_setting();
+ break;
+
+ case '1':
+ snddev_hsed_config_modify_setting(1);
+ break;
+
+ case '2':
+ snddev_hsed_config_modify_setting(2);
+ break;
+
+ default:
+ break;
+ }
+ }
+ return cnt;
+}
+
+static int snddev_hsed_config_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static const struct file_operations snddev_hsed_config_debug_fops = {
+ .open = snddev_hsed_config_debug_open,
+ .write = snddev_hsed_config_debug_write
+};
+#endif
+
+static enum hsed_controller ispk_pmctl_id[] = {PM_HSED_CONTROLLER_0};
+
+static struct snddev_icodec_data snddev_ispkr_mic_data = {
+ .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+ .name = "speaker_mono_tx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_SPKR_PHONE_MIC,
+ .profile = &imic_ffa_profile,
+ .channel_mode = 1,
+ .pmctl_id = ispk_pmctl_id,
+ .pmctl_id_sz = ARRAY_SIZE(ispk_pmctl_id),
+ .default_sample_rate = 48000,
+ .pamp_on = msm_snddev_tx_route_config,
+ .pamp_off = msm_snddev_tx_route_deconfig,
+};
+
+static struct platform_device msm_ispkr_mic_device = {
+ .name = "snddev_icodec",
+ .id = 18,
+ .dev = { .platform_data = &snddev_ispkr_mic_data },
+};
+
+static struct adie_codec_action_unit idual_mic_endfire_8KHz_osr256_actions[] =
+ AMIC_DUAL_8000_OSR_256;
+
+static struct adie_codec_hwsetting_entry idual_mic_endfire_settings[] = {
+ {
+ .freq_plan = 8000,
+ .osr = 256,
+ .actions = idual_mic_endfire_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(idual_mic_endfire_8KHz_osr256_actions),
+ }, /* 8KHz profile can be used for 16KHz */
+ {
+ .freq_plan = 16000,
+ .osr = 256,
+ .actions = idual_mic_endfire_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(idual_mic_endfire_8KHz_osr256_actions),
+ }, /* 8KHz profile can be used for 48KHz */
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = idual_mic_endfire_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(idual_mic_endfire_8KHz_osr256_actions),
+ }
+};
+
+static struct adie_codec_dev_profile idual_mic_endfire_profile = {
+ .path_type = ADIE_CODEC_TX,
+ .settings = idual_mic_endfire_settings,
+ .setting_sz = ARRAY_SIZE(idual_mic_endfire_settings),
+};
+
+static enum hsed_controller idual_mic_endfire_pmctl_id[] = {
+ PM_HSED_CONTROLLER_0, PM_HSED_CONTROLLER_2
+};
+
+static struct snddev_icodec_data snddev_idual_mic_endfire_data = {
+ .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+ .name = "handset_dual_mic_endfire_tx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_HANDSET_MIC_ENDFIRE,
+ .profile = &idual_mic_endfire_profile,
+ .channel_mode = 2,
+ .default_sample_rate = 48000,
+ .pmctl_id = idual_mic_endfire_pmctl_id,
+ .pmctl_id_sz = ARRAY_SIZE(idual_mic_endfire_pmctl_id),
+ .pamp_on = NULL,
+ .pamp_off = NULL,
+};
+
+static struct platform_device msm_idual_mic_endfire_device = {
+ .name = "snddev_icodec",
+ .id = 12,
+ .dev = { .platform_data = &snddev_idual_mic_endfire_data },
+};
+
+static struct snddev_icodec_data snddev_spk_idual_mic_endfire_data = {
+ .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+ .name = "speaker_dual_mic_endfire_tx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_SPKR_PHONE_MIC_ENDFIRE,
+ .profile = &idual_mic_endfire_profile,
+ .channel_mode = 2,
+ .default_sample_rate = 48000,
+ .pmctl_id = idual_mic_endfire_pmctl_id,
+ .pmctl_id_sz = ARRAY_SIZE(idual_mic_endfire_pmctl_id),
+ .pamp_on = NULL,
+ .pamp_off = NULL,
+};
+
+static struct platform_device msm_spk_idual_mic_endfire_device = {
+ .name = "snddev_icodec",
+ .id = 14,
+ .dev = { .platform_data = &snddev_spk_idual_mic_endfire_data },
+};
+
+static struct adie_codec_action_unit itty_mono_tx_actions[] =
+ TTY_HEADSET_MONO_TX_8000_OSR_256;
+
+static struct adie_codec_hwsetting_entry itty_mono_tx_settings[] = {
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = itty_mono_tx_actions,
+ .action_sz = ARRAY_SIZE(itty_mono_tx_actions),
+ },
+};
+
+static struct adie_codec_dev_profile itty_mono_tx_profile = {
+ .path_type = ADIE_CODEC_TX,
+ .settings = itty_mono_tx_settings,
+ .setting_sz = ARRAY_SIZE(itty_mono_tx_settings),
+};
+
+static struct snddev_icodec_data snddev_itty_mono_tx_data = {
+ .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE | SNDDEV_CAP_TTY),
+ .name = "tty_headset_mono_tx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_TTY_HEADSET_MIC,
+ .profile = &itty_mono_tx_profile,
+ .channel_mode = 1,
+ .default_sample_rate = 48000,
+ .pmctl_id = NULL,
+ .pmctl_id_sz = 0,
+ .pamp_on = NULL,
+ .pamp_off = NULL,
+};
+
+static struct platform_device msm_itty_mono_tx_device = {
+ .name = "snddev_icodec",
+ .id = 16,
+ .dev = { .platform_data = &snddev_itty_mono_tx_data },
+};
+
+static struct adie_codec_action_unit itty_mono_rx_actions[] =
+ TTY_HEADSET_MONO_RX_8000_OSR_256;
+
+static struct adie_codec_hwsetting_entry itty_mono_rx_settings[] = {
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = itty_mono_rx_actions,
+ .action_sz = ARRAY_SIZE(itty_mono_rx_actions),
+ },
+};
+
+static struct adie_codec_dev_profile itty_mono_rx_profile = {
+ .path_type = ADIE_CODEC_RX,
+ .settings = itty_mono_rx_settings,
+ .setting_sz = ARRAY_SIZE(itty_mono_rx_settings),
+};
+
+static struct snddev_icodec_data snddev_itty_mono_rx_data = {
+ .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE | SNDDEV_CAP_TTY),
+ .name = "tty_headset_mono_rx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_TTY_HEADSET_SPKR,
+ .profile = &itty_mono_rx_profile,
+ .channel_mode = 1,
+ .default_sample_rate = 48000,
+ .pamp_on = NULL,
+ .pamp_off = NULL,
+ .max_voice_rx_vol[VOC_NB_INDEX] = 0,
+ .min_voice_rx_vol[VOC_NB_INDEX] = 0,
+ .max_voice_rx_vol[VOC_WB_INDEX] = 0,
+ .min_voice_rx_vol[VOC_WB_INDEX] = 0,
+};
+
+static struct platform_device msm_itty_mono_rx_device = {
+ .name = "snddev_icodec",
+ .id = 17,
+ .dev = { .platform_data = &snddev_itty_mono_rx_data },
+};
+
+static struct snddev_virtual_data snddev_a2dp_tx_data = {
+ .capability = SNDDEV_CAP_TX,
+ .name = "a2dp_tx",
+ .copp_id = 5,
+ .acdb_id = PSEUDO_ACDB_ID,
+};
+
+static struct snddev_virtual_data snddev_a2dp_rx_data = {
+ .capability = SNDDEV_CAP_RX,
+ .name = "a2dp_rx",
+ .copp_id = 2,
+ .acdb_id = PSEUDO_ACDB_ID,
+};
+
+static struct platform_device msm_a2dp_rx_device = {
+ .name = "snddev_virtual",
+ .id = 0,
+ .dev = { .platform_data = &snddev_a2dp_rx_data },
+};
+
+static struct platform_device msm_a2dp_tx_device = {
+ .name = "snddev_virtual",
+ .id = 1,
+ .dev = { .platform_data = &snddev_a2dp_tx_data },
+};
+
+static struct snddev_virtual_data snddev_uplink_rx_data = {
+ .capability = SNDDEV_CAP_RX,
+ .name = "uplink_rx",
+ .copp_id = 5,
+ .acdb_id = PSEUDO_ACDB_ID,
+};
+
+static struct platform_device msm_uplink_rx_device = {
+ .name = "snddev_virtual",
+ .id = 2,
+ .dev = { .platform_data = &snddev_uplink_rx_data },
+};
+
+static struct snddev_icodec_data\
+ snddev_idual_mic_endfire_real_stereo_data = {
+ .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+ .name = "handset_dual_mic_endfire_tx_real_stereo",
+ .copp_id = 0,
+ .acdb_id = PSEUDO_ACDB_ID,
+ .profile = &idual_mic_endfire_profile,
+ .channel_mode = REAL_STEREO_CHANNEL_MODE,
+ .default_sample_rate = 48000,
+ .pmctl_id = idual_mic_endfire_pmctl_id,
+ .pmctl_id_sz = ARRAY_SIZE(idual_mic_endfire_pmctl_id),
+ .pamp_on = NULL,
+ .pamp_off = NULL,
+};
+
+static struct platform_device msm_real_stereo_tx_device = {
+ .name = "snddev_icodec",
+ .id = 26,
+ .dev = { .platform_data =
+ &snddev_idual_mic_endfire_real_stereo_data },
+};
+
+static struct adie_codec_action_unit ihs_ffa_mono_rx_48KHz_osr256_actions[] =
+ HEADSET_RX_CAPLESS_48000_OSR_256;
+
+static struct adie_codec_hwsetting_entry ihs_ffa_mono_rx_settings[] = {
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = ihs_ffa_mono_rx_48KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(ihs_ffa_mono_rx_48KHz_osr256_actions),
+ }
+};
+
+static struct adie_codec_dev_profile ihs_ffa_mono_rx_profile = {
+ .path_type = ADIE_CODEC_RX,
+ .settings = ihs_ffa_mono_rx_settings,
+ .setting_sz = ARRAY_SIZE(ihs_ffa_mono_rx_settings),
+};
+
+static struct snddev_icodec_data snddev_ihs_ffa_mono_rx_data = {
+ .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE),
+ .name = "headset_mono_rx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_HEADSET_SPKR_MONO,
+ .profile = &ihs_ffa_mono_rx_profile,
+ .channel_mode = 1,
+ .default_sample_rate = 48000,
+ .pamp_on = msm_snddev_hsed_voltage_on,
+ .pamp_off = msm_snddev_hsed_voltage_off,
+ .max_voice_rx_vol[VOC_NB_INDEX] = -700,
+ .min_voice_rx_vol[VOC_NB_INDEX] = -2200,
+ .max_voice_rx_vol[VOC_WB_INDEX] = -900,
+ .min_voice_rx_vol[VOC_WB_INDEX] = -2400,
+ .property = SIDE_TONE_MASK,
+};
+
+static struct platform_device msm_ihs_ffa_mono_rx_device = {
+ .name = "snddev_icodec",
+ .id = 5,
+ .dev = { .platform_data = &snddev_ihs_ffa_mono_rx_data },
+};
+
+static struct adie_codec_action_unit
+ ihs_stereo_speaker_stereo_rx_48KHz_osr256_actions[] =
+ HEADSET_STEREO_SPEAKER_STEREO_RX_CAPLESS_48000_OSR_256;
+
+
+static struct adie_codec_hwsetting_entry
+ ihs_stereo_speaker_stereo_rx_settings[] = {
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = ihs_stereo_speaker_stereo_rx_48KHz_osr256_actions,
+ .action_sz =
+ ARRAY_SIZE(ihs_stereo_speaker_stereo_rx_48KHz_osr256_actions),
+ }
+};
+
+static struct adie_codec_dev_profile ihs_stereo_speaker_stereo_rx_profile = {
+ .path_type = ADIE_CODEC_RX,
+ .settings = ihs_stereo_speaker_stereo_rx_settings,
+ .setting_sz = ARRAY_SIZE(ihs_stereo_speaker_stereo_rx_settings),
+};
+
+static struct snddev_icodec_data snddev_ihs_stereo_speaker_stereo_rx_data = {
+ .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE),
+ .name = "headset_stereo_speaker_stereo_rx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_HEADSET_STEREO_PLUS_SPKR_STEREO_RX,
+ .profile = &ihs_stereo_speaker_stereo_rx_profile,
+ .channel_mode = 2,
+ .default_sample_rate = 48000,
+ .pamp_on = msm_snddev_poweramp_on,
+ .pamp_off = msm_snddev_poweramp_off,
+ .voltage_on = msm_snddev_hsed_voltage_on,
+ .voltage_off = msm_snddev_hsed_voltage_off,
+ .max_voice_rx_vol[VOC_NB_INDEX] = -500,
+ .min_voice_rx_vol[VOC_NB_INDEX] = -2000,
+ .max_voice_rx_vol[VOC_WB_INDEX] = -900,
+ .min_voice_rx_vol[VOC_WB_INDEX] = -2400,
+};
+
+static struct platform_device msm_ihs_stereo_speaker_stereo_rx_device = {
+ .name = "snddev_icodec",
+ .id = 21,
+ .dev = { .platform_data = &snddev_ihs_stereo_speaker_stereo_rx_data },
+};
+
+static struct adie_codec_action_unit ispk_dual_mic_bs_8KHz_osr256_actions[] =
+ HS_DMIC2_STEREO_8000_OSR_256;
+
+static struct adie_codec_hwsetting_entry ispk_dual_mic_bs_settings[] = {
+ {
+ .freq_plan = 8000,
+ .osr = 256,
+ .actions = ispk_dual_mic_bs_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(ispk_dual_mic_bs_8KHz_osr256_actions),
+ }, /* 8KHz profile can be used for 16Khz */
+ {
+ .freq_plan = 16000,
+ .osr = 256,
+ .actions = ispk_dual_mic_bs_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(ispk_dual_mic_bs_8KHz_osr256_actions),
+ }, /* 8KHz profile can be used for 48KHz */
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = ispk_dual_mic_bs_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(ispk_dual_mic_bs_8KHz_osr256_actions),
+ },
+};
+
+static enum hsed_controller idual_mic_broadside_pmctl_id[] = {
+ PM_HSED_CONTROLLER_0, PM_HSED_CONTROLLER_2
+};
+
+static struct adie_codec_dev_profile ispk_dual_mic_bs_profile = {
+ .path_type = ADIE_CODEC_TX,
+ .settings = ispk_dual_mic_bs_settings,
+ .setting_sz = ARRAY_SIZE(ispk_dual_mic_bs_settings),
+};
+static struct snddev_icodec_data snddev_spk_idual_mic_broadside_data = {
+ .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+ .name = "speaker_dual_mic_broadside_tx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_SPKR_PHONE_MIC_BROADSIDE,
+ .profile = &ispk_dual_mic_bs_profile,
+ .channel_mode = 2,
+ .default_sample_rate = 48000,
+ .pmctl_id = idual_mic_broadside_pmctl_id,
+ .pmctl_id_sz = ARRAY_SIZE(idual_mic_broadside_pmctl_id),
+ .pamp_on = NULL,
+ .pamp_off = NULL,
+};
+
+static struct platform_device msm_spk_idual_mic_broadside_device = {
+ .name = "snddev_icodec",
+ .id = 15,
+ .dev = { .platform_data = &snddev_spk_idual_mic_broadside_data },
+};
+
+static struct adie_codec_action_unit idual_mic_bs_8KHz_osr256_actions[] =
+ HS_DMIC2_STEREO_8000_OSR_256;
+
+static struct adie_codec_hwsetting_entry idual_mic_broadside_settings[] = {
+ {
+ .freq_plan = 8000,
+ .osr = 256,
+ .actions = idual_mic_bs_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(idual_mic_bs_8KHz_osr256_actions),
+ }, /* 8KHz profile can be used for 16KHz */
+ {
+ .freq_plan = 16000,
+ .osr = 256,
+ .actions = idual_mic_bs_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(idual_mic_bs_8KHz_osr256_actions),
+ }, /* 8KHz profile can be used for 16KHz */
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = idual_mic_bs_8KHz_osr256_actions,
+ .action_sz = ARRAY_SIZE(idual_mic_bs_8KHz_osr256_actions),
+ }
+};
+
+static struct adie_codec_dev_profile idual_mic_broadside_profile = {
+ .path_type = ADIE_CODEC_TX,
+ .settings = idual_mic_broadside_settings,
+ .setting_sz = ARRAY_SIZE(idual_mic_broadside_settings),
+};
+
+static struct snddev_icodec_data snddev_idual_mic_broadside_data = {
+ .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+ .name = "handset_dual_mic_broadside_tx",
+ .copp_id = 0,
+ .acdb_id = ACDB_ID_HANDSET_MIC_BROADSIDE,
+ .profile = &idual_mic_broadside_profile,
+ .channel_mode = 2,
+ .default_sample_rate = 48000,
+ .pmctl_id = idual_mic_broadside_pmctl_id,
+ .pmctl_id_sz = ARRAY_SIZE(idual_mic_broadside_pmctl_id),
+ .pamp_on = NULL,
+ .pamp_off = NULL,
+};
+
+static struct platform_device msm_idual_mic_broadside_device = {
+ .name = "snddev_icodec",
+ .id = 13,
+ .dev = { .platform_data = &snddev_idual_mic_broadside_data },
+};
+
+static struct snddev_mi2s_data snddev_mi2s_stereo_rx_data = {
+ .capability = SNDDEV_CAP_RX ,
+ .name = "hdmi_stereo_rx",
+ .copp_id = 3,
+ .acdb_id = ACDB_ID_HDMI,
+ .channel_mode = 2,
+ .sd_lines = MI2S_SD_0,
+ .route = msm_snddev_tx_route_config,
+ .deroute = msm_snddev_tx_route_deconfig,
+ .default_sample_rate = 48000,
+};
+
+static struct platform_device msm_snddev_mi2s_stereo_rx_device = {
+ .name = "snddev_mi2s",
+ .id = 0,
+ .dev = { .platform_data = &snddev_mi2s_stereo_rx_data },
+};
+
+static struct adie_codec_action_unit auxpga_lb_lo_actions[] =
+ LB_AUXPGA_LO_STEREO;
+
+static struct adie_codec_hwsetting_entry auxpga_lb_lo_settings[] = {
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = auxpga_lb_lo_actions,
+ .action_sz = ARRAY_SIZE(auxpga_lb_lo_actions),
+ },
+};
+
+static struct adie_codec_dev_profile auxpga_lb_lo_profile = {
+ .path_type = ADIE_CODEC_LB,
+ .settings = auxpga_lb_lo_settings,
+ .setting_sz = ARRAY_SIZE(auxpga_lb_lo_settings),
+};
+
+static struct snddev_icodec_data snddev_auxpga_lb_lo_data = {
+ .capability = SNDDEV_CAP_LB,
+ .name = "auxpga_loopback_lo",
+ .copp_id = 0,
+ .acdb_id = PSEUDO_ACDB_ID,
+ .profile = &auxpga_lb_lo_profile,
+ .channel_mode = 2,
+ .default_sample_rate = 48000,
+ .pamp_on = msm_snddev_poweramp_on,
+ .pamp_off = msm_snddev_poweramp_off,
+ .dev_vol_type = SNDDEV_DEV_VOL_ANALOG,
+};
+
+static struct platform_device msm_auxpga_lb_lo_device = {
+ .name = "snddev_icodec",
+ .id = 27,
+ .dev = { .platform_data = &snddev_auxpga_lb_lo_data },
+};
+
+static struct adie_codec_action_unit auxpga_lb_hs_actions[] =
+ LB_AUXPGA_HPH_AB_CPLS_STEREO;
+
+static struct adie_codec_hwsetting_entry auxpga_lb_hs_settings[] = {
+ {
+ .freq_plan = 48000,
+ .osr = 256,
+ .actions = auxpga_lb_hs_actions,
+ .action_sz = ARRAY_SIZE(auxpga_lb_hs_actions),
+ },
+};
+
+static struct adie_codec_dev_profile auxpga_lb_hs_profile = {
+ .path_type = ADIE_CODEC_LB,
+ .settings = auxpga_lb_hs_settings,
+ .setting_sz = ARRAY_SIZE(auxpga_lb_hs_settings),
+};
+
+static struct snddev_icodec_data snddev_auxpga_lb_hs_data = {
+ .capability = SNDDEV_CAP_LB,
+ .name = "auxpga_loopback_hs",
+ .copp_id = 0,
+ .acdb_id = PSEUDO_ACDB_ID,
+ .profile = &auxpga_lb_hs_profile,
+ .channel_mode = 2,
+ .default_sample_rate = 48000,
+ .voltage_on = msm_snddev_hsed_voltage_on,
+ .voltage_off = msm_snddev_hsed_voltage_off,
+ .dev_vol_type = SNDDEV_DEV_VOL_ANALOG,
+};
+
+static struct platform_device msm_auxpga_lb_hs_device = {
+ .name = "snddev_icodec",
+ .id = 25,
+ .dev = { .platform_data = &snddev_auxpga_lb_hs_data },
+};
+
+static struct platform_device *snd_devices_ffa[] __initdata = {
+ &msm_iearpiece_ffa_device,
+ &msm_imic_ffa_device,
+ &msm_ispkr_stereo_device,
+ &msm_headset_mic_device,
+ &msm_ihs_ffa_mono_rx_device,
+ &msm_snddev_mi2s_fm_rx_device,
+ &msm_snddev_mi2s_fm_tx_device,
+ &msm_bt_sco_earpiece_device,
+ &msm_bt_sco_mic_device,
+ &msm_ispkr_mic_device,
+ &msm_headset_stereo_device,
+ &msm_idual_mic_endfire_device,
+ &msm_spk_idual_mic_endfire_device,
+ &msm_itty_mono_tx_device,
+ &msm_itty_mono_rx_device,
+ &msm_a2dp_rx_device,
+ &msm_a2dp_tx_device,
+ &msm_uplink_rx_device,
+ &msm_real_stereo_tx_device,
+ &msm_ihs_stereo_speaker_stereo_rx_device,
+ &msm_spk_idual_mic_broadside_device,
+ &msm_idual_mic_broadside_device,
+ &msm_snddev_mi2s_stereo_rx_device,
+ &msm_auxpga_lb_hs_device,
+ &msm_auxpga_lb_lo_device,
+};
+
+void __ref msm_snddev_init_timpani(void)
+{
+ platform_add_devices(snd_devices_ffa,
+ ARRAY_SIZE(snd_devices_ffa));
+#ifdef CONFIG_DEBUG_FS
+ debugfs_hsed_config = debugfs_create_file("msm_hsed_config",
+ S_IFREG | S_IWUGO, NULL,
+ (void *) "msm_hsed_config", &snddev_hsed_config_debug_fops);
+ if (!debugfs_hsed_config)
+ pr_err("failed to create msm_head_config debug fs entry\n");
+#endif
+
+}
diff --git a/arch/arm/mach-msm/qdsp5v2/snddev_ecodec.c b/arch/arm/mach-msm/qdsp5v2/snddev_ecodec.c
new file mode 100644
index 0000000..a5da912
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/snddev_ecodec.c
@@ -0,0 +1,484 @@
+/* Copyright (c) 2009,2011 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <asm/uaccess.h>
+#include <mach/qdsp5v2/snddev_ecodec.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/qdsp5v2/audio_interct.h>
+#include <mach/qdsp5v2/aux_pcm.h>
+#include <mach/qdsp5v2/afe.h>
+#include <mach/debug_mm.h>
+#include <linux/slab.h>
+
+/* Context for each external codec device */
+struct snddev_ecodec_state {
+ struct snddev_ecodec_data *data;
+ u32 sample_rate;
+ bool enabled;
+};
+
+/* Global state for the driver */
+struct snddev_ecodec_drv_state {
+ struct mutex dev_lock;
+ u32 rx_active; /* ensure one rx device at a time */
+ u32 tx_active; /* ensure one tx device at a time */
+ struct clk *lpa_core_clk;
+ struct clk *ecodec_clk;
+};
+
+#define ADSP_CTL 1
+
+static struct snddev_ecodec_drv_state snddev_ecodec_drv;
+
+static int snddev_ecodec_open_rx(struct snddev_ecodec_state *ecodec)
+{
+ int rc = 0;
+ struct snddev_ecodec_drv_state *drv = &snddev_ecodec_drv;
+ struct msm_afe_config afe_config;
+ int ret = 0;
+
+ MM_DBG("snddev_ecodec_open_rx\n");
+
+ if (!drv->tx_active) {
+ /* request GPIO */
+ rc = aux_pcm_gpios_request();
+ if (rc) {
+ MM_ERR("GPIO enable failed\n");
+ goto done;
+ }
+ /* config clocks */
+ clk_enable(drv->lpa_core_clk);
+
+ /*if long sync is selected in aux PCM interface
+ ecodec clock is updated to work with 128KHz,
+ if short sync is selected ecodec clock is updated to
+ work with 2.048MHz frequency, actual clock output is
+ different than the SW configuration by factor of two*/
+ if (!(ecodec->data->conf_aux_codec_intf &
+ AUX_CODEC_CTL__AUX_CODEC_MODE__I2S_V)) {
+ if (ecodec->data->conf_aux_codec_intf &
+ AUX_CODEC_CTL__AUX_PCM_MODE__AUX_MASTER_V) {
+ MM_DBG("Update ecodec clock to 128 KHz, long "
+ "sync in master mode is selected\n");
+ ret = clk_set_rate(drv->ecodec_clk, 256000);
+ if (ret < 0)
+ MM_ERR("Error updating ecodec clock"
+ " to 128KHz\n");
+ } else if (ecodec->data->conf_aux_codec_intf &
+ AUX_CODEC_CTL__AUX_PCM_MODE__PRIM_SLAVE_V) {
+ MM_DBG("Update ecodec clock to 2 MHz, short"
+ " sync in slave mode is selected\n");
+ ret = clk_set_rate(drv->ecodec_clk, 4096000);
+ if (ret < 0)
+ MM_ERR("Error updating ecodec clock"
+ " to 2.048MHz\n");
+ } else {
+ MM_DBG("Update ecodec clock to 2 MHz, short"
+ " sync in master mode is selected\n");
+ ret = clk_set_rate(drv->ecodec_clk, 4096000);
+ if (ret < 0)
+ MM_ERR("Error updating ecodec clock"
+ " to 2.048MHz\n");
+ }
+ }
+
+ /* enable ecodec clk */
+ clk_enable(drv->ecodec_clk);
+
+ /* let ADSP confiure AUX PCM regs */
+ aux_codec_adsp_codec_ctl_en(ADSP_CTL);
+
+ /* let adsp configure pcm path */
+ aux_codec_pcm_path_ctl_en(ADSP_CTL);
+
+ /* choose ADSP_A */
+ audio_interct_aux_regsel(AUDIO_ADSP_A);
+ audio_interct_tpcm_source(AUDIO_ADSP_A);
+ audio_interct_rpcm_source(AUDIO_ADSP_A);
+
+ clk_disable(drv->lpa_core_clk);
+
+ /* send AUX_CODEC_CONFIG to AFE */
+ rc = afe_config_aux_codec(ecodec->data->conf_pcm_ctl_val,
+ ecodec->data->conf_aux_codec_intf,
+ ecodec->data->conf_data_format_padding_val);
+ if (IS_ERR_VALUE(rc))
+ goto error;
+ }
+ /* send CODEC CONFIG to AFE */
+ afe_config.sample_rate = ecodec->sample_rate / 1000;
+ afe_config.channel_mode = ecodec->data->channel_mode;
+ afe_config.volume = AFE_VOLUME_UNITY;
+ rc = afe_enable(AFE_HW_PATH_AUXPCM_RX, &afe_config);
+ if (IS_ERR_VALUE(rc)) {
+ if (!drv->tx_active) {
+ aux_pcm_gpios_free();
+ clk_disable(drv->ecodec_clk);
+ }
+ goto done;
+ }
+
+ ecodec->enabled = 1;
+ return 0;
+
+error:
+ aux_pcm_gpios_free();
+ clk_disable(drv->ecodec_clk);
+done:
+ return rc;
+}
+
+static int snddev_ecodec_close_rx(struct snddev_ecodec_state *ecodec)
+{
+ struct snddev_ecodec_drv_state *drv = &snddev_ecodec_drv;
+
+ /* free GPIO */
+ if (!drv->tx_active) {
+ aux_pcm_gpios_free();
+ clk_disable(drv->ecodec_clk);
+ }
+
+ /* disable AFE */
+ afe_disable(AFE_HW_PATH_AUXPCM_RX);
+
+ ecodec->enabled = 0;
+
+ return 0;
+}
+
+static int snddev_ecodec_open_tx(struct snddev_ecodec_state *ecodec)
+{
+ int rc = 0;
+ struct snddev_ecodec_drv_state *drv = &snddev_ecodec_drv;
+ struct msm_afe_config afe_config;
+ int ret = 0;
+
+ MM_DBG("snddev_ecodec_open_tx\n");
+
+ /* request GPIO */
+ if (!drv->rx_active) {
+ rc = aux_pcm_gpios_request();
+ if (rc) {
+ MM_ERR("GPIO enable failed\n");
+ goto done;
+ }
+ /* config clocks */
+ clk_enable(drv->lpa_core_clk);
+
+ /*if long sync is selected in aux PCM interface
+ ecodec clock is updated to work with 128KHz,
+ if short sync is selected ecodec clock is updated to
+ work with 2.048MHz frequency, actual clock output is
+ different than the SW configuration by factor of two*/
+ if (!(ecodec->data->conf_aux_codec_intf &
+ AUX_CODEC_CTL__AUX_CODEC_MODE__I2S_V)) {
+ if (ecodec->data->conf_aux_codec_intf &
+ AUX_CODEC_CTL__AUX_PCM_MODE__AUX_MASTER_V) {
+ MM_DBG("Update ecodec clock to 128 KHz, long "
+ "sync in master mode is selected\n");
+ ret = clk_set_rate(drv->ecodec_clk, 256000);
+ if (ret < 0)
+ MM_ERR("Error updating ecodec clock"
+ " to 128KHz\n");
+ } else if (ecodec->data->conf_aux_codec_intf &
+ AUX_CODEC_CTL__AUX_PCM_MODE__PRIM_SLAVE_V) {
+ MM_DBG("Update ecodec clock to 2 MHz, short"
+ " sync in slave mode is selected\n");
+ ret = clk_set_rate(drv->ecodec_clk, 4096000);
+ if (ret < 0)
+ MM_ERR("Error updating ecodec clock"
+ " to 2.048MHz\n");
+ } else {
+ MM_DBG("Update ecodec clock to 2 MHz, short"
+ " sync in master mode is selected\n");
+ ret = clk_set_rate(drv->ecodec_clk, 4096000);
+ if (ret < 0)
+ MM_ERR("Error updating ecodec clock"
+ " to 2.048MHz\n");
+ }
+ }
+
+ /* enable ecodec clk */
+ clk_enable(drv->ecodec_clk);
+
+ /* let ADSP confiure AUX PCM regs */
+ aux_codec_adsp_codec_ctl_en(ADSP_CTL);
+
+ /* let adsp configure pcm path */
+ aux_codec_pcm_path_ctl_en(ADSP_CTL);
+
+ /* choose ADSP_A */
+ audio_interct_aux_regsel(AUDIO_ADSP_A);
+ audio_interct_tpcm_source(AUDIO_ADSP_A);
+ audio_interct_rpcm_source(AUDIO_ADSP_A);
+
+ clk_disable(drv->lpa_core_clk);
+
+ /* send AUX_CODEC_CONFIG to AFE */
+ rc = afe_config_aux_codec(ecodec->data->conf_pcm_ctl_val,
+ ecodec->data->conf_aux_codec_intf,
+ ecodec->data->conf_data_format_padding_val);
+ if (IS_ERR_VALUE(rc))
+ goto error;
+ }
+ /* send CODEC CONFIG to AFE */
+ afe_config.sample_rate = ecodec->sample_rate / 1000;
+ afe_config.channel_mode = ecodec->data->channel_mode;
+ afe_config.volume = AFE_VOLUME_UNITY;
+ rc = afe_enable(AFE_HW_PATH_AUXPCM_TX, &afe_config);
+ if (IS_ERR_VALUE(rc)) {
+ if (!drv->rx_active) {
+ aux_pcm_gpios_free();
+ clk_disable(drv->ecodec_clk);
+ }
+ goto done;
+ }
+
+ ecodec->enabled = 1;
+ return 0;
+
+error:
+ clk_disable(drv->ecodec_clk);
+ aux_pcm_gpios_free();
+done:
+ return rc;
+}
+
+static int snddev_ecodec_close_tx(struct snddev_ecodec_state *ecodec)
+{
+ struct snddev_ecodec_drv_state *drv = &snddev_ecodec_drv;
+
+ /* free GPIO */
+ if (!drv->rx_active) {
+ aux_pcm_gpios_free();
+ clk_disable(drv->ecodec_clk);
+ }
+
+ /* disable AFE */
+ afe_disable(AFE_HW_PATH_AUXPCM_TX);
+
+ ecodec->enabled = 0;
+
+ return 0;
+}
+
+
+static int snddev_ecodec_open(struct msm_snddev_info *dev_info)
+{
+ int rc = 0;
+ struct snddev_ecodec_state *ecodec;
+ struct snddev_ecodec_drv_state *drv = &snddev_ecodec_drv;
+
+ if (!dev_info) {
+ rc = -EINVAL;
+ goto error;
+ }
+
+ ecodec = dev_info->private_data;
+
+ if (ecodec->data->capability & SNDDEV_CAP_RX) {
+ mutex_lock(&drv->dev_lock);
+ if (drv->rx_active) {
+ mutex_unlock(&drv->dev_lock);
+ rc = -EBUSY;
+ goto error;
+ }
+ rc = snddev_ecodec_open_rx(ecodec);
+ if (!IS_ERR_VALUE(rc))
+ drv->rx_active = 1;
+ mutex_unlock(&drv->dev_lock);
+ } else {
+ mutex_lock(&drv->dev_lock);
+ if (drv->tx_active) {
+ mutex_unlock(&drv->dev_lock);
+ rc = -EBUSY;
+ goto error;
+ }
+ rc = snddev_ecodec_open_tx(ecodec);
+ if (!IS_ERR_VALUE(rc))
+ drv->tx_active = 1;
+ mutex_unlock(&drv->dev_lock);
+ }
+error:
+ return rc;
+}
+
+static int snddev_ecodec_close(struct msm_snddev_info *dev_info)
+{
+ int rc = 0;
+ struct snddev_ecodec_state *ecodec;
+ struct snddev_ecodec_drv_state *drv = &snddev_ecodec_drv;
+ if (!dev_info) {
+ rc = -EINVAL;
+ goto error;
+ }
+
+ ecodec = dev_info->private_data;
+
+ if (ecodec->data->capability & SNDDEV_CAP_RX) {
+ mutex_lock(&drv->dev_lock);
+ if (!drv->rx_active) {
+ mutex_unlock(&drv->dev_lock);
+ rc = -EPERM;
+ goto error;
+ }
+ rc = snddev_ecodec_close_rx(ecodec);
+ if (!IS_ERR_VALUE(rc))
+ drv->rx_active = 0;
+ mutex_unlock(&drv->dev_lock);
+ } else {
+ mutex_lock(&drv->dev_lock);
+ if (!drv->tx_active) {
+ mutex_unlock(&drv->dev_lock);
+ rc = -EPERM;
+ goto error;
+ }
+ rc = snddev_ecodec_close_tx(ecodec);
+ if (!IS_ERR_VALUE(rc))
+ drv->tx_active = 0;
+ mutex_unlock(&drv->dev_lock);
+ }
+
+error:
+ return rc;
+}
+
+static int snddev_ecodec_set_freq(struct msm_snddev_info *dev_info, u32 rate)
+{
+ int rc = 0;
+
+ if (!dev_info) {
+ rc = -EINVAL;
+ goto error;
+ }
+ return 8000;
+
+error:
+ return rc;
+}
+
+static int snddev_ecodec_probe(struct platform_device *pdev)
+{
+ int rc = 0, i;
+ struct snddev_ecodec_data *pdata;
+ struct msm_snddev_info *dev_info;
+ struct snddev_ecodec_state *ecodec;
+
+ if (!pdev || !pdev->dev.platform_data) {
+ printk(KERN_ALERT "Invalid caller \n");
+ rc = -1;
+ goto error;
+ }
+ pdata = pdev->dev.platform_data;
+
+ ecodec = kzalloc(sizeof(struct snddev_ecodec_state), GFP_KERNEL);
+ if (!ecodec) {
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ dev_info = kzalloc(sizeof(struct msm_snddev_info), GFP_KERNEL);
+ if (!dev_info) {
+ kfree(ecodec);
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ dev_info->name = pdata->name;
+ dev_info->copp_id = pdata->copp_id;
+ dev_info->acdb_id = pdata->acdb_id;
+ dev_info->private_data = (void *) ecodec;
+ dev_info->dev_ops.open = snddev_ecodec_open;
+ dev_info->dev_ops.close = snddev_ecodec_close;
+ dev_info->dev_ops.set_freq = snddev_ecodec_set_freq;
+ dev_info->dev_ops.enable_sidetone = NULL;
+ dev_info->capability = pdata->capability;
+ dev_info->opened = 0;
+
+ msm_snddev_register(dev_info);
+ ecodec->data = pdata;
+ ecodec->sample_rate = 8000; /* Default to 8KHz */
+ if (pdata->capability & SNDDEV_CAP_RX) {
+ for (i = 0; i < VOC_RX_VOL_ARRAY_NUM; i++) {
+ dev_info->max_voc_rx_vol[i] =
+ pdata->max_voice_rx_vol[i];
+ dev_info->min_voc_rx_vol[i] =
+ pdata->min_voice_rx_vol[i];
+ }
+ }
+error:
+ return rc;
+}
+
+static int snddev_ecodec_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct platform_driver snddev_ecodec_driver = {
+ .probe = snddev_ecodec_probe,
+ .remove = snddev_ecodec_remove,
+ .driver = { .name = "msm_snddev_ecodec" }
+};
+
+static int __init snddev_ecodec_init(void)
+{
+ int rc = 0;
+ struct snddev_ecodec_drv_state *ecodec_drv = &snddev_ecodec_drv;
+
+ MM_INFO("snddev_ecodec_init\n");
+ rc = platform_driver_register(&snddev_ecodec_driver);
+ if (IS_ERR_VALUE(rc))
+ goto error_platform_driver;
+ ecodec_drv->ecodec_clk = clk_get(NULL, "ecodec_clk");
+ if (IS_ERR(ecodec_drv->ecodec_clk))
+ goto error_ecodec_clk;
+ ecodec_drv->lpa_core_clk = clk_get(NULL, "lpa_core_clk");
+ if (IS_ERR(ecodec_drv->lpa_core_clk))
+ goto error_lpa_core_clk;
+
+
+ mutex_init(&ecodec_drv->dev_lock);
+ ecodec_drv->rx_active = 0;
+ ecodec_drv->tx_active = 0;
+ return 0;
+
+error_lpa_core_clk:
+ clk_put(ecodec_drv->ecodec_clk);
+error_ecodec_clk:
+ platform_driver_unregister(&snddev_ecodec_driver);
+error_platform_driver:
+
+ MM_ERR("encounter error\n");
+ return -ENODEV;
+}
+
+static void __exit snddev_ecodec_exit(void)
+{
+ struct snddev_ecodec_drv_state *ecodec_drv = &snddev_ecodec_drv;
+
+ platform_driver_unregister(&snddev_ecodec_driver);
+ clk_put(ecodec_drv->ecodec_clk);
+
+ return;
+}
+
+module_init(snddev_ecodec_init);
+module_exit(snddev_ecodec_exit);
+
+MODULE_DESCRIPTION("ECodec Sound Device driver");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/snddev_icodec.c b/arch/arm/mach-msm/qdsp5v2/snddev_icodec.c
new file mode 100644
index 0000000..dbeda82
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/snddev_icodec.c
@@ -0,0 +1,1211 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/msm-adie-codec.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/debugfs.h>
+#include <asm/uaccess.h>
+#include <mach/qdsp5v2/snddev_icodec.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/qdsp5v2/audio_interct.h>
+#include <mach/qdsp5v2/mi2s.h>
+#include <mach/qdsp5v2/afe.h>
+#include <mach/qdsp5v2/lpa.h>
+#include <mach/qdsp5v2/marimba_profile.h>
+#include <mach/vreg.h>
+#include <mach/pmic.h>
+#include <linux/wakelock.h>
+#include <mach/debug_mm.h>
+#include <mach/rpc_pmapp.h>
+#include <mach/qdsp5v2/audio_acdb_def.h>
+#include <linux/slab.h>
+
+#define SMPS_AUDIO_PLAYBACK_ID "AUPB"
+#define SMPS_AUDIO_RECORD_ID "AURC"
+
+#define SNDDEV_ICODEC_PCM_SZ 32 /* 16 bit / sample stereo mode */
+#define SNDDEV_ICODEC_MUL_FACTOR 3 /* Multi by 8 Shift by 3 */
+#define SNDDEV_ICODEC_CLK_RATE(freq) \
+ (((freq) * (SNDDEV_ICODEC_PCM_SZ)) << (SNDDEV_ICODEC_MUL_FACTOR))
+
+#ifdef CONFIG_DEBUG_FS
+static struct adie_codec_action_unit debug_rx_actions[] =
+ HANDSET_RX_8000_OSR_256;
+
+static struct adie_codec_action_unit debug_tx_lb_actions[] = {
+ { ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF },
+ { ADIE_CODEC_ACTION_ENTRY,
+ ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x01)},
+ { ADIE_CODEC_ACTION_ENTRY,
+ ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x00) },
+ { ADIE_CODEC_ACTION_ENTRY,
+ ADIE_CODEC_PACK_ENTRY(0x8A, 0x30, 0x30)},
+ { ADIE_CODEC_ACTION_ENTRY,
+ ADIE_CODEC_PACK_ENTRY(0x11, 0xfc, 0xfc)},
+ { ADIE_CODEC_ACTION_ENTRY,
+ ADIE_CODEC_PACK_ENTRY(0x13, 0xfc, 0x58)},
+ { ADIE_CODEC_ACTION_ENTRY,
+ ADIE_CODEC_PACK_ENTRY(0x14, 0xff, 0x65)},
+ { ADIE_CODEC_ACTION_ENTRY,
+ ADIE_CODEC_PACK_ENTRY(0x15, 0xff, 0x64)},
+ { ADIE_CODEC_ACTION_ENTRY,
+ ADIE_CODEC_PACK_ENTRY(0x82, 0xff, 0x5C)},
+ { ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY },
+ { ADIE_CODEC_ACTION_ENTRY,
+ ADIE_CODEC_PACK_ENTRY(0x0D, 0xF0, 0xd0)},
+ { ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8},
+ { ADIE_CODEC_ACTION_ENTRY,
+ ADIE_CODEC_PACK_ENTRY(0x83, 0x14, 0x14)},
+ { ADIE_CODEC_ACTION_ENTRY,
+ ADIE_CODEC_PACK_ENTRY(0x86, 0xff, 0x00)},
+ { ADIE_CODEC_ACTION_ENTRY,
+ ADIE_CODEC_PACK_ENTRY(0x8A, 0x50, 0x40)},
+ { ADIE_CODEC_ACTION_ENTRY,
+ ADIE_CODEC_PACK_ENTRY(0x91, 0xFF, 0x01)}, /* Start loop back */
+ { ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY},
+ { ADIE_CODEC_ACTION_ENTRY,
+ ADIE_CODEC_PACK_ENTRY(0x8A, 0x10, 0x30)},
+ { ADIE_CODEC_ACTION_ENTRY,
+ ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)},
+ { ADIE_CODEC_ACTION_ENTRY,
+ ADIE_CODEC_PACK_ENTRY(0x83, 0x14, 0x00)},
+ { ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF},
+ { ADIE_CODEC_ACTION_ENTRY,
+ ADIE_CODEC_PACK_ENTRY(0x11, 0xff, 0x00)}
+};
+
+static struct adie_codec_action_unit debug_tx_actions[] =
+ HANDSET_TX_8000_OSR_256;
+
+static struct adie_codec_hwsetting_entry debug_rx_settings[] = {
+ {
+ .freq_plan = 8000,
+ .osr = 256,
+ .actions = debug_rx_actions,
+ .action_sz = ARRAY_SIZE(debug_rx_actions),
+ }
+};
+
+static struct adie_codec_hwsetting_entry debug_tx_settings[] = {
+ {
+ .freq_plan = 8000,
+ .osr = 256,
+ .actions = debug_tx_actions,
+ .action_sz = ARRAY_SIZE(debug_tx_actions),
+ }
+};
+
+static struct adie_codec_hwsetting_entry debug_tx_lb_settings[] = {
+ {
+ .freq_plan = 8000,
+ .osr = 256,
+ .actions = debug_tx_lb_actions,
+ .action_sz = ARRAY_SIZE(debug_tx_lb_actions),
+ }
+};
+
+static struct adie_codec_dev_profile debug_rx_profile = {
+ .path_type = ADIE_CODEC_RX,
+ .settings = debug_rx_settings,
+ .setting_sz = ARRAY_SIZE(debug_rx_settings),
+};
+
+static struct adie_codec_dev_profile debug_tx_profile = {
+ .path_type = ADIE_CODEC_TX,
+ .settings = debug_tx_settings,
+ .setting_sz = ARRAY_SIZE(debug_tx_settings),
+};
+
+static struct adie_codec_dev_profile debug_tx_lb_profile = {
+ .path_type = ADIE_CODEC_TX,
+ .settings = debug_tx_lb_settings,
+ .setting_sz = ARRAY_SIZE(debug_tx_lb_settings),
+};
+#endif /* CONFIG_DEBUG_FS */
+
+/* Context for each internal codec sound device */
+struct snddev_icodec_state {
+ struct snddev_icodec_data *data;
+ struct adie_codec_path *adie_path;
+ u32 sample_rate;
+ u32 enabled;
+};
+
+/* Global state for the driver */
+struct snddev_icodec_drv_state {
+ struct mutex rx_lock;
+ struct mutex lb_lock;
+ struct mutex tx_lock;
+ u32 rx_active; /* ensure one rx device at a time */
+ u32 tx_active; /* ensure one tx device at a time */
+ struct clk *rx_mclk;
+ struct clk *rx_sclk;
+ struct clk *tx_mclk;
+ struct clk *tx_sclk;
+ struct clk *lpa_codec_clk;
+ struct clk *lpa_core_clk;
+ struct clk *lpa_p_clk;
+ struct lpa_drv *lpa;
+
+ struct wake_lock rx_idlelock;
+ struct wake_lock tx_idlelock;
+};
+
+static struct snddev_icodec_drv_state snddev_icodec_drv;
+
+static int snddev_icodec_open_rx(struct snddev_icodec_state *icodec)
+{
+ int trc, err;
+ int smps_mode = PMAPP_SMPS_MODE_VOTE_PWM;
+ struct msm_afe_config afe_config;
+ struct snddev_icodec_drv_state *drv = &snddev_icodec_drv;
+ struct lpa_codec_config lpa_config;
+
+ wake_lock(&drv->rx_idlelock);
+
+ if ((icodec->data->acdb_id == ACDB_ID_HEADSET_SPKR_MONO) ||
+ (icodec->data->acdb_id == ACDB_ID_HEADSET_SPKR_STEREO)) {
+ /* Vote PMAPP_SMPS_MODE_VOTE_PFM for headset */
+ smps_mode = PMAPP_SMPS_MODE_VOTE_PFM;
+ MM_DBG("snddev_icodec_open_rx: PMAPP_SMPS_MODE_VOTE_PFM \n");
+ } else
+ MM_DBG("snddev_icodec_open_rx: PMAPP_SMPS_MODE_VOTE_PWM \n");
+
+ /* Vote for SMPS mode*/
+ err = pmapp_smps_mode_vote(SMPS_AUDIO_PLAYBACK_ID,
+ PMAPP_VREG_S4, smps_mode);
+ if (err != 0)
+ MM_ERR("pmapp_smps_mode_vote error %d\n", err);
+
+ /* enable MI2S RX master block */
+ /* enable MI2S RX bit clock */
+ trc = clk_set_rate(drv->rx_mclk,
+ SNDDEV_ICODEC_CLK_RATE(icodec->sample_rate));
+ if (IS_ERR_VALUE(trc))
+ goto error_invalid_freq;
+ clk_enable(drv->rx_mclk);
+ clk_enable(drv->rx_sclk);
+ /* clk_set_rate(drv->lpa_codec_clk, 1); */ /* Remove if use pcom */
+ clk_enable(drv->lpa_p_clk);
+ clk_enable(drv->lpa_codec_clk);
+ clk_enable(drv->lpa_core_clk);
+
+ /* Enable LPA sub system
+ */
+ drv->lpa = lpa_get();
+ if (!drv->lpa)
+ goto error_lpa;
+ lpa_config.sample_rate = icodec->sample_rate;
+ lpa_config.sample_width = 16;
+ lpa_config.output_interface = LPA_OUTPUT_INTF_WB_CODEC;
+ lpa_config.num_channels = icodec->data->channel_mode;
+ lpa_cmd_codec_config(drv->lpa, &lpa_config);
+
+ /* Set audio interconnect reg to LPA */
+ audio_interct_codec(AUDIO_INTERCT_LPA);
+
+ /* Set MI2S */
+ mi2s_set_codec_output_path((icodec->data->channel_mode == 2 ?
+ MI2S_CHAN_STEREO : MI2S_CHAN_MONO_PACKED), WT_16_BIT);
+
+ if (icodec->data->voltage_on)
+ icodec->data->voltage_on();
+
+ /* Configure ADIE */
+ trc = adie_codec_open(icodec->data->profile, &icodec->adie_path);
+ if (IS_ERR_VALUE(trc))
+ goto error_adie;
+ /* OSR default to 256, can be changed for power optimization
+ * If OSR is to be changed, need clock API for setting the divider
+ */
+ adie_codec_setpath(icodec->adie_path, icodec->sample_rate, 256);
+ /* Start AFE */
+ afe_config.sample_rate = icodec->sample_rate / 1000;
+ afe_config.channel_mode = icodec->data->channel_mode;
+ afe_config.volume = AFE_VOLUME_UNITY;
+ trc = afe_enable(AFE_HW_PATH_CODEC_RX, &afe_config);
+ if (IS_ERR_VALUE(trc))
+ goto error_afe;
+ lpa_cmd_enable_codec(drv->lpa, 1);
+ /* Enable ADIE */
+ adie_codec_proceed_stage(icodec->adie_path, ADIE_CODEC_DIGITAL_READY);
+ adie_codec_proceed_stage(icodec->adie_path,
+ ADIE_CODEC_DIGITAL_ANALOG_READY);
+
+ /* Enable power amplifier */
+ if (icodec->data->pamp_on)
+ icodec->data->pamp_on();
+
+ icodec->enabled = 1;
+
+ wake_unlock(&drv->rx_idlelock);
+ return 0;
+
+error_afe:
+ adie_codec_close(icodec->adie_path);
+ icodec->adie_path = NULL;
+error_adie:
+ lpa_put(drv->lpa);
+error_lpa:
+ clk_disable(drv->lpa_p_clk);
+ clk_disable(drv->lpa_codec_clk);
+ clk_disable(drv->lpa_core_clk);
+ clk_disable(drv->rx_sclk);
+ clk_disable(drv->rx_mclk);
+error_invalid_freq:
+
+ MM_ERR("encounter error\n");
+
+ wake_unlock(&drv->rx_idlelock);
+ return -ENODEV;
+}
+
+static int snddev_icodec_open_tx(struct snddev_icodec_state *icodec)
+{
+ int trc;
+ int i, err;
+ struct msm_afe_config afe_config;
+ struct snddev_icodec_drv_state *drv = &snddev_icodec_drv;;
+
+ wake_lock(&drv->tx_idlelock);
+
+ /* Vote for PWM mode*/
+ err = pmapp_smps_mode_vote(SMPS_AUDIO_RECORD_ID,
+ PMAPP_VREG_S4, PMAPP_SMPS_MODE_VOTE_PWM);
+ if (err != 0)
+ MM_ERR("pmapp_smps_mode_vote error %d\n", err);
+
+ /* Reuse pamp_on for TX platform-specific setup */
+ if (icodec->data->pamp_on)
+ icodec->data->pamp_on();
+
+ for (i = 0; i < icodec->data->pmctl_id_sz; i++) {
+ pmic_hsed_enable(icodec->data->pmctl_id[i],
+ PM_HSED_ENABLE_PWM_TCXO);
+ }
+
+ /* enable MI2S TX master block */
+ /* enable MI2S TX bit clock */
+ trc = clk_set_rate(drv->tx_mclk,
+ SNDDEV_ICODEC_CLK_RATE(icodec->sample_rate));
+ if (IS_ERR_VALUE(trc))
+ goto error_invalid_freq;
+ clk_enable(drv->tx_mclk);
+ clk_enable(drv->tx_sclk);
+
+ /* Set MI2S */
+ mi2s_set_codec_input_path((icodec->data->channel_mode ==
+ REAL_STEREO_CHANNEL_MODE ? MI2S_CHAN_STEREO :
+ (icodec->data->channel_mode == 2 ?
+ MI2S_CHAN_STEREO : MI2S_CHAN_MONO_RAW)),
+ WT_16_BIT);
+ /* Configure ADIE */
+ trc = adie_codec_open(icodec->data->profile, &icodec->adie_path);
+ if (IS_ERR_VALUE(trc))
+ goto error_adie;
+ /* Enable ADIE */
+ adie_codec_setpath(icodec->adie_path, icodec->sample_rate, 256);
+ adie_codec_proceed_stage(icodec->adie_path, ADIE_CODEC_DIGITAL_READY);
+ adie_codec_proceed_stage(icodec->adie_path,
+ ADIE_CODEC_DIGITAL_ANALOG_READY);
+
+ /* Start AFE */
+ afe_config.sample_rate = icodec->sample_rate / 1000;
+ afe_config.channel_mode = icodec->data->channel_mode;
+ afe_config.volume = AFE_VOLUME_UNITY;
+ trc = afe_enable(AFE_HW_PATH_CODEC_TX, &afe_config);
+ if (IS_ERR_VALUE(trc))
+ goto error_afe;
+
+
+ icodec->enabled = 1;
+
+ wake_unlock(&drv->tx_idlelock);
+ return 0;
+
+error_afe:
+ adie_codec_close(icodec->adie_path);
+ icodec->adie_path = NULL;
+error_adie:
+ clk_disable(drv->tx_sclk);
+ clk_disable(drv->tx_mclk);
+error_invalid_freq:
+
+ /* Disable mic bias */
+ for (i = 0; i < icodec->data->pmctl_id_sz; i++) {
+ pmic_hsed_enable(icodec->data->pmctl_id[i],
+ PM_HSED_ENABLE_OFF);
+ }
+
+ if (icodec->data->pamp_off)
+ icodec->data->pamp_off();
+
+ MM_ERR("encounter error\n");
+
+ wake_unlock(&drv->tx_idlelock);
+ return -ENODEV;
+}
+
+static int snddev_icodec_close_lb(struct snddev_icodec_state *icodec)
+{
+ /* Disable power amplifier */
+ if (icodec->data->pamp_off)
+ icodec->data->pamp_off();
+
+ if (icodec->adie_path) {
+ adie_codec_proceed_stage(icodec->adie_path,
+ ADIE_CODEC_DIGITAL_OFF);
+ adie_codec_close(icodec->adie_path);
+ icodec->adie_path = NULL;
+ }
+ if (icodec->data->voltage_off)
+ icodec->data->voltage_off();
+
+ return 0;
+}
+
+static int snddev_icodec_close_rx(struct snddev_icodec_state *icodec)
+{
+ int err;
+ struct snddev_icodec_drv_state *drv = &snddev_icodec_drv;
+
+ wake_lock(&drv->rx_idlelock);
+
+ /* Remove the vote for SMPS mode*/
+ err = pmapp_smps_mode_vote(SMPS_AUDIO_PLAYBACK_ID,
+ PMAPP_VREG_S4, PMAPP_SMPS_MODE_VOTE_DONTCARE);
+ if (err != 0)
+ MM_ERR("pmapp_smps_mode_vote error %d\n", err);
+
+ /* Disable power amplifier */
+ if (icodec->data->pamp_off)
+ icodec->data->pamp_off();
+
+ /* Disable ADIE */
+ adie_codec_proceed_stage(icodec->adie_path, ADIE_CODEC_DIGITAL_OFF);
+ adie_codec_close(icodec->adie_path);
+ icodec->adie_path = NULL;
+
+ afe_disable(AFE_HW_PATH_CODEC_RX);
+
+ if (icodec->data->voltage_off)
+ icodec->data->voltage_off();
+
+ /* Disable LPA Sub system */
+ lpa_cmd_enable_codec(drv->lpa, 0);
+ lpa_put(drv->lpa);
+
+ /* Disable LPA clocks */
+ clk_disable(drv->lpa_p_clk);
+ clk_disable(drv->lpa_codec_clk);
+ clk_disable(drv->lpa_core_clk);
+
+ /* Disable MI2S RX master block */
+ /* Disable MI2S RX bit clock */
+ clk_disable(drv->rx_sclk);
+ clk_disable(drv->rx_mclk);
+
+ icodec->enabled = 0;
+
+ wake_unlock(&drv->rx_idlelock);
+ return 0;
+}
+
+static int snddev_icodec_close_tx(struct snddev_icodec_state *icodec)
+{
+ struct snddev_icodec_drv_state *drv = &snddev_icodec_drv;
+ int i, err;
+
+ wake_lock(&drv->tx_idlelock);
+
+ /* Remove the vote for SMPS mode*/
+ err = pmapp_smps_mode_vote(SMPS_AUDIO_RECORD_ID,
+ PMAPP_VREG_S4, PMAPP_SMPS_MODE_VOTE_DONTCARE);
+ if (err != 0)
+ MM_ERR("pmapp_smps_mode_vote error %d\n", err);
+
+ afe_disable(AFE_HW_PATH_CODEC_TX);
+
+ /* Disable ADIE */
+ adie_codec_proceed_stage(icodec->adie_path, ADIE_CODEC_DIGITAL_OFF);
+ adie_codec_close(icodec->adie_path);
+ icodec->adie_path = NULL;
+
+ /* Disable MI2S TX master block */
+ /* Disable MI2S TX bit clock */
+ clk_disable(drv->tx_sclk);
+ clk_disable(drv->tx_mclk);
+
+ /* Disable mic bias */
+ for (i = 0; i < icodec->data->pmctl_id_sz; i++) {
+ pmic_hsed_enable(icodec->data->pmctl_id[i],
+ PM_HSED_ENABLE_OFF);
+ }
+
+ /* Reuse pamp_off for TX platform-specific setup */
+ if (icodec->data->pamp_off)
+ icodec->data->pamp_off();
+
+ icodec->enabled = 0;
+
+ wake_unlock(&drv->tx_idlelock);
+ return 0;
+}
+
+static int snddev_icodec_open_lb(struct snddev_icodec_state *icodec)
+{
+ int trc;
+ trc = adie_codec_open(icodec->data->profile, &icodec->adie_path);
+ if (IS_ERR_VALUE(trc))
+ pr_err("%s: adie codec open failed\n", __func__);
+ else
+ adie_codec_setpath(icodec->adie_path,
+ icodec->sample_rate, 256);
+
+ if (icodec->adie_path)
+ adie_codec_proceed_stage(icodec->adie_path,
+ ADIE_CODEC_DIGITAL_ANALOG_READY);
+ if (icodec->data->pamp_on)
+ icodec->data->pamp_on();
+
+ icodec->enabled = 1;
+ return 0;
+}
+
+static int snddev_icodec_set_device_volume_impl(
+ struct msm_snddev_info *dev_info, u32 volume)
+{
+ struct snddev_icodec_state *icodec;
+ u8 afe_path_id;
+
+ int rc = 0;
+
+ icodec = dev_info->private_data;
+
+ if (icodec->data->capability & SNDDEV_CAP_RX)
+ afe_path_id = AFE_HW_PATH_CODEC_RX;
+ else
+ afe_path_id = AFE_HW_PATH_CODEC_TX;
+
+ if (icodec->data->dev_vol_type & SNDDEV_DEV_VOL_DIGITAL) {
+
+ rc = adie_codec_set_device_digital_volume(icodec->adie_path,
+ icodec->data->channel_mode ==
+ REAL_STEREO_CHANNEL_MODE ?
+ 2 : icodec->data->channel_mode, volume);
+ if (rc < 0) {
+ MM_ERR("unable to set_device_digital_volume for"
+ "%s volume in percentage = %u\n",
+ dev_info->name, volume);
+ return rc;
+ }
+
+ } else if (icodec->data->dev_vol_type & SNDDEV_DEV_VOL_ANALOG) {
+ rc = adie_codec_set_device_analog_volume(icodec->adie_path,
+ icodec->data->channel_mode ==
+ REAL_STEREO_CHANNEL_MODE ?
+ 2 : icodec->data->channel_mode, volume);
+ if (rc < 0) {
+ MM_ERR("unable to set_device_analog_volume for"
+ "%s volume in percentage = %u\n",
+ dev_info->name, volume);
+ return rc;
+ }
+ }
+ else {
+ MM_ERR("Invalid device volume control\n");
+ return -EPERM;
+ }
+ return rc;
+}
+
+static int snddev_icodec_close(struct msm_snddev_info *dev_info)
+{
+ int rc = 0;
+ struct snddev_icodec_state *icodec;
+ struct snddev_icodec_drv_state *drv = &snddev_icodec_drv;
+ if (!dev_info) {
+ rc = -EINVAL;
+ goto error;
+ }
+
+ icodec = dev_info->private_data;
+
+ if (icodec->data->capability & SNDDEV_CAP_RX) {
+ mutex_lock(&drv->rx_lock);
+ if (!drv->rx_active) {
+ mutex_unlock(&drv->rx_lock);
+ rc = -EPERM;
+ goto error;
+ }
+ rc = snddev_icodec_close_rx(icodec);
+ if (!IS_ERR_VALUE(rc))
+ drv->rx_active = 0;
+ mutex_unlock(&drv->rx_lock);
+ } else if (icodec->data->capability & SNDDEV_CAP_LB) {
+ mutex_lock(&drv->lb_lock);
+ rc = snddev_icodec_close_lb(icodec);
+ mutex_unlock(&drv->lb_lock);
+ } else {
+ mutex_lock(&drv->tx_lock);
+ if (!drv->tx_active) {
+ mutex_unlock(&drv->tx_lock);
+ rc = -EPERM;
+ goto error;
+ }
+ rc = snddev_icodec_close_tx(icodec);
+ if (!IS_ERR_VALUE(rc))
+ drv->tx_active = 0;
+ mutex_unlock(&drv->tx_lock);
+ }
+
+error:
+ return rc;
+}
+
+static int snddev_icodec_open(struct msm_snddev_info *dev_info)
+{
+ int rc = 0;
+ struct snddev_icodec_state *icodec;
+ struct snddev_icodec_drv_state *drv = &snddev_icodec_drv;
+
+ if (!dev_info) {
+ rc = -EINVAL;
+ goto error;
+ }
+
+ icodec = dev_info->private_data;
+
+ if (icodec->data->capability & SNDDEV_CAP_RX) {
+ mutex_lock(&drv->rx_lock);
+ if (drv->rx_active) {
+ mutex_unlock(&drv->rx_lock);
+ rc = -EBUSY;
+ goto error;
+ }
+ rc = snddev_icodec_open_rx(icodec);
+
+ if (!IS_ERR_VALUE(rc)) {
+ drv->rx_active = 1;
+ if ((icodec->data->dev_vol_type & (
+ SNDDEV_DEV_VOL_DIGITAL |
+ SNDDEV_DEV_VOL_ANALOG)))
+ rc = snddev_icodec_set_device_volume_impl(
+ dev_info, dev_info->dev_volume);
+ if (IS_ERR_VALUE(rc)) {
+ MM_ERR("Failed to set device volume"
+ " impl for rx device\n");
+ snddev_icodec_close(dev_info);
+ mutex_unlock(&drv->rx_lock);
+ goto error;
+ }
+ }
+ mutex_unlock(&drv->rx_lock);
+ } else if (icodec->data->capability & SNDDEV_CAP_LB) {
+ mutex_lock(&drv->lb_lock);
+ rc = snddev_icodec_open_lb(icodec);
+ if (!IS_ERR_VALUE(rc)) {
+ if ((icodec->data->dev_vol_type & (
+ SNDDEV_DEV_VOL_DIGITAL |
+ SNDDEV_DEV_VOL_ANALOG)))
+ rc = snddev_icodec_set_device_volume_impl(
+ dev_info,
+ dev_info->dev_volume);
+ if (rc < 0)
+ MM_ERR("failed to set device volume\n");
+ }
+ mutex_unlock(&drv->lb_lock);
+ } else {
+ mutex_lock(&drv->tx_lock);
+ if (drv->tx_active) {
+ mutex_unlock(&drv->tx_lock);
+ rc = -EBUSY;
+ goto error;
+ }
+ rc = snddev_icodec_open_tx(icodec);
+
+ if (!IS_ERR_VALUE(rc)) {
+ drv->tx_active = 1;
+ if ((icodec->data->dev_vol_type & (
+ SNDDEV_DEV_VOL_DIGITAL |
+ SNDDEV_DEV_VOL_ANALOG)))
+ rc = snddev_icodec_set_device_volume_impl(
+ dev_info, dev_info->dev_volume);
+ if (IS_ERR_VALUE(rc)) {
+ MM_ERR("Failed to set device volume"
+ " impl for tx device\n");
+ snddev_icodec_close(dev_info);
+ mutex_unlock(&drv->tx_lock);
+ goto error;
+ }
+ }
+ mutex_unlock(&drv->tx_lock);
+ }
+error:
+ return rc;
+}
+
+static int snddev_icodec_check_freq(u32 req_freq)
+{
+ int rc = -EINVAL;
+
+ if ((req_freq != 0) && (req_freq >= 8000) && (req_freq <= 48000)) {
+ if ((req_freq == 8000) || (req_freq == 11025) ||
+ (req_freq == 12000) || (req_freq == 16000) ||
+ (req_freq == 22050) || (req_freq == 24000) ||
+ (req_freq == 32000) || (req_freq == 44100) ||
+ (req_freq == 48000)) {
+ rc = 0;
+ } else
+ MM_INFO("Unsupported Frequency:%d\n", req_freq);
+ }
+ return rc;
+}
+
+static int snddev_icodec_set_freq(struct msm_snddev_info *dev_info, u32 rate)
+{
+ int rc;
+ struct snddev_icodec_state *icodec;
+
+ if (!dev_info) {
+ rc = -EINVAL;
+ goto error;
+ }
+
+ icodec = dev_info->private_data;
+ if (adie_codec_freq_supported(icodec->data->profile, rate) != 0) {
+ rc = -EINVAL;
+ goto error;
+ } else {
+ if (snddev_icodec_check_freq(rate) != 0) {
+ rc = -EINVAL;
+ goto error;
+ } else
+ icodec->sample_rate = rate;
+ }
+
+ if (icodec->enabled) {
+ snddev_icodec_close(dev_info);
+ snddev_icodec_open(dev_info);
+ }
+
+ return icodec->sample_rate;
+
+error:
+ return rc;
+}
+
+static int snddev_icodec_enable_sidetone(struct msm_snddev_info *dev_info,
+ u32 enable)
+{
+ int rc = 0;
+ struct snddev_icodec_state *icodec;
+ struct snddev_icodec_drv_state *drv = &snddev_icodec_drv;
+
+ if (!dev_info) {
+ MM_ERR("invalid dev_info\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ icodec = dev_info->private_data;
+
+ if (icodec->data->capability & SNDDEV_CAP_RX) {
+ mutex_lock(&drv->rx_lock);
+ if (!drv->rx_active || !dev_info->opened) {
+ MM_ERR("dev not active\n");
+ rc = -EPERM;
+ mutex_unlock(&drv->rx_lock);
+ goto error;
+ }
+ rc = adie_codec_enable_sidetone(icodec->adie_path, enable);
+ mutex_unlock(&drv->rx_lock);
+ } else {
+ rc = -EINVAL;
+ MM_ERR("rx device only\n");
+ }
+
+error:
+ return rc;
+
+}
+
+int snddev_icodec_set_device_volume(struct msm_snddev_info *dev_info,
+ u32 volume)
+{
+ struct snddev_icodec_state *icodec;
+ struct mutex *lock;
+ struct snddev_icodec_drv_state *drv = &snddev_icodec_drv;
+ int rc = -EPERM;
+
+ if (!dev_info) {
+ MM_INFO("device not intilized.\n");
+ return -EINVAL;
+ }
+
+ icodec = dev_info->private_data;
+
+ if (!(icodec->data->dev_vol_type & (SNDDEV_DEV_VOL_DIGITAL
+ | SNDDEV_DEV_VOL_ANALOG))) {
+
+ MM_INFO("device %s does not support device volume "
+ "control.", dev_info->name);
+ return -EPERM;
+ }
+ dev_info->dev_volume = volume;
+
+ if (icodec->data->capability & SNDDEV_CAP_RX)
+ lock = &drv->rx_lock;
+ else if (icodec->data->capability & SNDDEV_CAP_LB)
+ lock = &drv->lb_lock;
+ else
+ lock = &drv->tx_lock;
+
+ mutex_lock(lock);
+
+ rc = snddev_icodec_set_device_volume_impl(dev_info,
+ dev_info->dev_volume);
+ mutex_unlock(lock);
+ return rc;
+}
+
+static int snddev_icodec_probe(struct platform_device *pdev)
+{
+ int rc = 0, i;
+ struct snddev_icodec_data *pdata;
+ struct msm_snddev_info *dev_info;
+ struct snddev_icodec_state *icodec;
+
+ if (!pdev || !pdev->dev.platform_data) {
+ printk(KERN_ALERT "Invalid caller \n");
+ rc = -1;
+ goto error;
+ }
+ pdata = pdev->dev.platform_data;
+ if ((pdata->capability & SNDDEV_CAP_RX) &&
+ (pdata->capability & SNDDEV_CAP_TX)) {
+ MM_ERR("invalid device data either RX or TX\n");
+ goto error;
+ }
+ icodec = kzalloc(sizeof(struct snddev_icodec_state), GFP_KERNEL);
+ if (!icodec) {
+ rc = -ENOMEM;
+ goto error;
+ }
+ dev_info = kmalloc(sizeof(struct msm_snddev_info), GFP_KERNEL);
+ if (!dev_info) {
+ kfree(icodec);
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ dev_info->name = pdata->name;
+ dev_info->copp_id = pdata->copp_id;
+ dev_info->acdb_id = pdata->acdb_id;
+ dev_info->private_data = (void *) icodec;
+ dev_info->dev_ops.open = snddev_icodec_open;
+ dev_info->dev_ops.close = snddev_icodec_close;
+ dev_info->dev_ops.set_freq = snddev_icodec_set_freq;
+ dev_info->dev_ops.set_device_volume = snddev_icodec_set_device_volume;
+ dev_info->capability = pdata->capability;
+ dev_info->opened = 0;
+ msm_snddev_register(dev_info);
+ icodec->data = pdata;
+ icodec->sample_rate = pdata->default_sample_rate;
+ dev_info->sample_rate = pdata->default_sample_rate;
+ if (pdata->capability & SNDDEV_CAP_RX) {
+ for (i = 0; i < VOC_RX_VOL_ARRAY_NUM; i++) {
+ dev_info->max_voc_rx_vol[i] =
+ pdata->max_voice_rx_vol[i];
+ dev_info->min_voc_rx_vol[i] =
+ pdata->min_voice_rx_vol[i];
+ }
+ /*sidetone is enabled only for the device which
+ property set for side tone*/
+ if (pdata->property & SIDE_TONE_MASK)
+ dev_info->dev_ops.enable_sidetone =
+ snddev_icodec_enable_sidetone;
+ else
+ dev_info->dev_ops.enable_sidetone = NULL;
+ } else {
+ dev_info->dev_ops.enable_sidetone = NULL;
+ }
+
+error:
+ return rc;
+}
+
+static int snddev_icodec_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct platform_driver snddev_icodec_driver = {
+ .probe = snddev_icodec_probe,
+ .remove = snddev_icodec_remove,
+ .driver = { .name = "snddev_icodec" }
+};
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *debugfs_sdev_dent;
+static struct dentry *debugfs_afelb;
+static struct dentry *debugfs_adielb;
+static struct adie_codec_path *debugfs_rx_adie;
+static struct adie_codec_path *debugfs_tx_adie;
+
+static int snddev_icodec_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ MM_INFO("snddev_icodec: debug intf %s\n", (char *) file->private_data);
+ return 0;
+}
+
+static void debugfs_adie_loopback(u32 loop)
+{
+ struct snddev_icodec_drv_state *drv = &snddev_icodec_drv;
+
+ if (loop) {
+
+ /* enable MI2S RX master block */
+ /* enable MI2S RX bit clock */
+ clk_set_rate(drv->rx_mclk,
+ SNDDEV_ICODEC_CLK_RATE(8000));
+ clk_enable(drv->rx_mclk);
+ clk_enable(drv->rx_sclk);
+
+ MM_INFO("configure ADIE RX path\n");
+ /* Configure ADIE */
+ adie_codec_open(&debug_rx_profile, &debugfs_rx_adie);
+ adie_codec_setpath(debugfs_rx_adie, 8000, 256);
+ adie_codec_proceed_stage(debugfs_rx_adie,
+ ADIE_CODEC_DIGITAL_ANALOG_READY);
+
+ MM_INFO("Enable Handset Mic bias\n");
+ pmic_hsed_enable(PM_HSED_CONTROLLER_0, PM_HSED_ENABLE_PWM_TCXO);
+ /* enable MI2S TX master block */
+ /* enable MI2S TX bit clock */
+ clk_set_rate(drv->tx_mclk,
+ SNDDEV_ICODEC_CLK_RATE(8000));
+ clk_enable(drv->tx_mclk);
+ clk_enable(drv->tx_sclk);
+
+ MM_INFO("configure ADIE TX path\n");
+ /* Configure ADIE */
+ adie_codec_open(&debug_tx_lb_profile, &debugfs_tx_adie);
+ adie_codec_setpath(debugfs_tx_adie, 8000, 256);
+ adie_codec_proceed_stage(debugfs_tx_adie,
+ ADIE_CODEC_DIGITAL_ANALOG_READY);
+ } else {
+ /* Disable ADIE */
+ adie_codec_proceed_stage(debugfs_rx_adie,
+ ADIE_CODEC_DIGITAL_OFF);
+ adie_codec_close(debugfs_rx_adie);
+ adie_codec_proceed_stage(debugfs_tx_adie,
+ ADIE_CODEC_DIGITAL_OFF);
+ adie_codec_close(debugfs_tx_adie);
+
+ pmic_hsed_enable(PM_HSED_CONTROLLER_0, PM_HSED_ENABLE_OFF);
+
+ /* Disable MI2S RX master block */
+ /* Disable MI2S RX bit clock */
+ clk_disable(drv->rx_sclk);
+ clk_disable(drv->rx_mclk);
+
+ /* Disable MI2S TX master block */
+ /* Disable MI2S TX bit clock */
+ clk_disable(drv->tx_sclk);
+ clk_disable(drv->tx_mclk);
+ }
+}
+
+static void debugfs_afe_loopback(u32 loop)
+{
+ int trc;
+ struct msm_afe_config afe_config;
+ struct snddev_icodec_drv_state *drv = &snddev_icodec_drv;
+ struct lpa_codec_config lpa_config;
+
+ if (loop) {
+ /* Vote for SMPS mode*/
+ pmapp_smps_mode_vote(SMPS_AUDIO_PLAYBACK_ID,
+ PMAPP_VREG_S4, PMAPP_SMPS_MODE_VOTE_PWM);
+
+ /* enable MI2S RX master block */
+ /* enable MI2S RX bit clock */
+ trc = clk_set_rate(drv->rx_mclk,
+ SNDDEV_ICODEC_CLK_RATE(8000));
+ if (IS_ERR_VALUE(trc))
+ MM_ERR("failed to set clk rate\n");
+ clk_enable(drv->rx_mclk);
+ clk_enable(drv->rx_sclk);
+ clk_enable(drv->lpa_p_clk);
+ clk_enable(drv->lpa_codec_clk);
+ clk_enable(drv->lpa_core_clk);
+ /* Enable LPA sub system
+ */
+ drv->lpa = lpa_get();
+ if (!drv->lpa)
+ MM_ERR("failed to enable lpa\n");
+ lpa_config.sample_rate = 8000;
+ lpa_config.sample_width = 16;
+ lpa_config.output_interface = LPA_OUTPUT_INTF_WB_CODEC;
+ lpa_config.num_channels = 1;
+ lpa_cmd_codec_config(drv->lpa, &lpa_config);
+ /* Set audio interconnect reg to LPA */
+ audio_interct_codec(AUDIO_INTERCT_LPA);
+ mi2s_set_codec_output_path(MI2S_CHAN_MONO_PACKED, WT_16_BIT);
+ MM_INFO("configure ADIE RX path\n");
+ /* Configure ADIE */
+ adie_codec_open(&debug_rx_profile, &debugfs_rx_adie);
+ adie_codec_setpath(debugfs_rx_adie, 8000, 256);
+ lpa_cmd_enable_codec(drv->lpa, 1);
+
+ /* Start AFE for RX */
+ afe_config.sample_rate = 0x8;
+ afe_config.channel_mode = 1;
+ afe_config.volume = AFE_VOLUME_UNITY;
+ MM_INFO("enable afe\n");
+ trc = afe_enable(AFE_HW_PATH_CODEC_RX, &afe_config);
+ if (IS_ERR_VALUE(trc))
+ MM_ERR("fail to enable afe RX\n");
+ adie_codec_proceed_stage(debugfs_rx_adie,
+ ADIE_CODEC_DIGITAL_READY);
+ adie_codec_proceed_stage(debugfs_rx_adie,
+ ADIE_CODEC_DIGITAL_ANALOG_READY);
+
+ /* Vote for PWM mode*/
+ pmapp_smps_mode_vote(SMPS_AUDIO_RECORD_ID,
+ PMAPP_VREG_S4, PMAPP_SMPS_MODE_VOTE_PWM);
+
+ MM_INFO("Enable Handset Mic bias\n");
+ pmic_hsed_enable(PM_HSED_CONTROLLER_0, PM_HSED_ENABLE_PWM_TCXO);
+
+ /* enable MI2S TX master block */
+ /* enable MI2S TX bit clock */
+ clk_set_rate(drv->tx_mclk,
+ SNDDEV_ICODEC_CLK_RATE(8000));
+ clk_enable(drv->tx_mclk);
+ clk_enable(drv->tx_sclk);
+ /* Set MI2S */
+ mi2s_set_codec_input_path(MI2S_CHAN_MONO_PACKED, WT_16_BIT);
+ MM_INFO("configure ADIE TX path\n");
+ /* Configure ADIE */
+ adie_codec_open(&debug_tx_profile, &debugfs_tx_adie);
+ adie_codec_setpath(debugfs_tx_adie, 8000, 256);
+ adie_codec_proceed_stage(debugfs_tx_adie,
+ ADIE_CODEC_DIGITAL_READY);
+ adie_codec_proceed_stage(debugfs_tx_adie,
+ ADIE_CODEC_DIGITAL_ANALOG_READY);
+ /* Start AFE for TX */
+ afe_config.sample_rate = 0x8;
+ afe_config.channel_mode = 1;
+ afe_config.volume = AFE_VOLUME_UNITY;
+ trc = afe_enable(AFE_HW_PATH_CODEC_TX, &afe_config);
+ if (IS_ERR_VALUE(trc))
+ MM_ERR("failed to enable AFE TX\n");
+ /* Set the volume level to non unity, to avoid
+ loopback effect */
+ afe_device_volume_ctrl(AFE_HW_PATH_CODEC_RX, 0x0500);
+
+ /* enable afe loopback */
+ afe_loopback(1);
+ MM_INFO("AFE loopback enabled\n");
+ } else {
+ /* disable afe loopback */
+ afe_loopback(0);
+ /* Remove the vote for SMPS mode*/
+ pmapp_smps_mode_vote(SMPS_AUDIO_PLAYBACK_ID,
+ PMAPP_VREG_S4, PMAPP_SMPS_MODE_VOTE_DONTCARE);
+
+ /* Disable ADIE */
+ adie_codec_proceed_stage(debugfs_rx_adie,
+ ADIE_CODEC_DIGITAL_OFF);
+ adie_codec_close(debugfs_rx_adie);
+ /* Disable AFE for RX */
+ afe_disable(AFE_HW_PATH_CODEC_RX);
+
+ /* Disable LPA Sub system */
+ lpa_cmd_enable_codec(drv->lpa, 0);
+ lpa_put(drv->lpa);
+
+ /* Disable LPA clocks */
+ clk_disable(drv->lpa_p_clk);
+ clk_disable(drv->lpa_codec_clk);
+ clk_disable(drv->lpa_core_clk);
+
+ /* Disable MI2S RX master block */
+ /* Disable MI2S RX bit clock */
+ clk_disable(drv->rx_sclk);
+ clk_disable(drv->rx_mclk);
+
+ pmapp_smps_mode_vote(SMPS_AUDIO_RECORD_ID,
+ PMAPP_VREG_S4, PMAPP_SMPS_MODE_VOTE_DONTCARE);
+
+ /* Disable AFE for TX */
+ afe_disable(AFE_HW_PATH_CODEC_TX);
+
+ /* Disable ADIE */
+ adie_codec_proceed_stage(debugfs_tx_adie,
+ ADIE_CODEC_DIGITAL_OFF);
+ adie_codec_close(debugfs_tx_adie);
+ /* Disable MI2S TX master block */
+ /* Disable MI2S TX bit clock */
+ clk_disable(drv->tx_sclk);
+ clk_disable(drv->tx_mclk);
+ pmic_hsed_enable(PM_HSED_CONTROLLER_0, PM_HSED_ENABLE_OFF);
+ MM_INFO("AFE loopback disabled\n");
+ }
+}
+
+static ssize_t snddev_icodec_debug_write(struct file *filp,
+ const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+ char *lb_str = filp->private_data;
+ char cmd;
+
+ if (get_user(cmd, ubuf))
+ return -EFAULT;
+
+ MM_INFO("%s %c\n", lb_str, cmd);
+
+ if (!strcmp(lb_str, "adie_loopback")) {
+ switch (cmd) {
+ case '1':
+ debugfs_adie_loopback(1);
+ break;
+ case '0':
+ debugfs_adie_loopback(0);
+ break;
+ }
+ } else if (!strcmp(lb_str, "afe_loopback")) {
+ switch (cmd) {
+ case '1':
+ debugfs_afe_loopback(1);
+ break;
+ case '0':
+ debugfs_afe_loopback(0);
+ break;
+ }
+ }
+
+ return cnt;
+}
+
+static const struct file_operations snddev_icodec_debug_fops = {
+ .open = snddev_icodec_debug_open,
+ .write = snddev_icodec_debug_write
+};
+#endif
+
+static int __init snddev_icodec_init(void)
+{
+ s32 rc;
+ struct snddev_icodec_drv_state *icodec_drv = &snddev_icodec_drv;
+
+ rc = platform_driver_register(&snddev_icodec_driver);
+ if (IS_ERR_VALUE(rc))
+ goto error_platform_driver;
+ icodec_drv->rx_mclk = clk_get(NULL, "mi2s_codec_rx_m_clk");
+ if (IS_ERR(icodec_drv->rx_mclk))
+ goto error_rx_mclk;
+ icodec_drv->rx_sclk = clk_get(NULL, "mi2s_codec_rx_s_clk");
+ if (IS_ERR(icodec_drv->rx_sclk))
+ goto error_rx_sclk;
+ icodec_drv->tx_mclk = clk_get(NULL, "mi2s_codec_tx_m_clk");
+ if (IS_ERR(icodec_drv->tx_mclk))
+ goto error_tx_mclk;
+ icodec_drv->tx_sclk = clk_get(NULL, "mi2s_codec_tx_s_clk");
+ if (IS_ERR(icodec_drv->tx_sclk))
+ goto error_tx_sclk;
+ icodec_drv->lpa_codec_clk = clk_get(NULL, "lpa_codec_clk");
+ if (IS_ERR(icodec_drv->lpa_codec_clk))
+ goto error_lpa_codec_clk;
+ icodec_drv->lpa_core_clk = clk_get(NULL, "lpa_core_clk");
+ if (IS_ERR(icodec_drv->lpa_core_clk))
+ goto error_lpa_core_clk;
+ icodec_drv->lpa_p_clk = clk_get(NULL, "lpa_pclk");
+ if (IS_ERR(icodec_drv->lpa_p_clk))
+ goto error_lpa_p_clk;
+
+#ifdef CONFIG_DEBUG_FS
+ debugfs_sdev_dent = debugfs_create_dir("snddev_icodec", 0);
+ if (debugfs_sdev_dent) {
+ debugfs_afelb = debugfs_create_file("afe_loopback",
+ S_IFREG | S_IWUGO, debugfs_sdev_dent,
+ (void *) "afe_loopback", &snddev_icodec_debug_fops);
+ debugfs_adielb = debugfs_create_file("adie_loopback",
+ S_IFREG | S_IWUGO, debugfs_sdev_dent,
+ (void *) "adie_loopback", &snddev_icodec_debug_fops);
+ }
+#endif
+ mutex_init(&icodec_drv->rx_lock);
+ mutex_init(&icodec_drv->lb_lock);
+ mutex_init(&icodec_drv->tx_lock);
+ icodec_drv->rx_active = 0;
+ icodec_drv->tx_active = 0;
+ icodec_drv->lpa = NULL;
+ wake_lock_init(&icodec_drv->tx_idlelock, WAKE_LOCK_IDLE,
+ "snddev_tx_idle");
+ wake_lock_init(&icodec_drv->rx_idlelock, WAKE_LOCK_IDLE,
+ "snddev_rx_idle");
+ return 0;
+
+error_lpa_p_clk:
+ clk_put(icodec_drv->lpa_core_clk);
+error_lpa_core_clk:
+ clk_put(icodec_drv->lpa_codec_clk);
+error_lpa_codec_clk:
+ clk_put(icodec_drv->tx_sclk);
+error_tx_sclk:
+ clk_put(icodec_drv->tx_mclk);
+error_tx_mclk:
+ clk_put(icodec_drv->rx_sclk);
+error_rx_sclk:
+ clk_put(icodec_drv->rx_mclk);
+error_rx_mclk:
+ platform_driver_unregister(&snddev_icodec_driver);
+error_platform_driver:
+
+ MM_ERR("encounter error\n");
+ return -ENODEV;
+}
+
+static void __exit snddev_icodec_exit(void)
+{
+ struct snddev_icodec_drv_state *icodec_drv = &snddev_icodec_drv;
+
+#ifdef CONFIG_DEBUG_FS
+ if (debugfs_afelb)
+ debugfs_remove(debugfs_afelb);
+ if (debugfs_adielb)
+ debugfs_remove(debugfs_adielb);
+ if (debugfs_sdev_dent)
+ debugfs_remove(debugfs_sdev_dent);
+#endif
+ platform_driver_unregister(&snddev_icodec_driver);
+
+ clk_put(icodec_drv->rx_sclk);
+ clk_put(icodec_drv->rx_mclk);
+ clk_put(icodec_drv->tx_sclk);
+ clk_put(icodec_drv->tx_mclk);
+ return;
+}
+
+module_init(snddev_icodec_init);
+module_exit(snddev_icodec_exit);
+
+MODULE_DESCRIPTION("ICodec Sound Device driver");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/snddev_mi2s.c b/arch/arm/mach-msm/qdsp5v2/snddev_mi2s.c
new file mode 100644
index 0000000..939cc8b
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/snddev_mi2s.c
@@ -0,0 +1,405 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/qdsp5v2/audio_interct.h>
+#include <mach/qdsp5v2/mi2s.h>
+#include <mach/qdsp5v2/afe.h>
+#include <mach/debug_mm.h>
+#include <mach/qdsp5v2/snddev_mi2s.h>
+
+/* Global state for the driver */
+struct snddev_mi2s_drv_state {
+ struct clk *mclk;
+ struct clk *sclk;
+ struct mutex lock;
+ u8 sd_lines_used;
+ u8 clocks_enabled;
+};
+
+static struct snddev_mi2s_drv_state snddev_mi2s_drv;
+
+static int snddev_mi2s_open_tx(struct msm_snddev_info *dev_info)
+{
+ u8 channels;
+ struct msm_afe_config afe_config;
+ int rc;
+ struct snddev_mi2s_data *snddev_mi2s_data = dev_info->private_data;
+
+ MM_DBG("%s: channel_mode = %u sd_line_mask = 0x%x "
+ "default_sample_rate = %u\n", __func__,
+ snddev_mi2s_data->channel_mode, snddev_mi2s_data->sd_lines,
+ snddev_mi2s_data->default_sample_rate);
+
+ if (snddev_mi2s_data->channel_mode == 2) {
+ channels = MI2S_CHAN_STEREO;
+ } else {
+ MM_ERR("%s: Invalid number of channels = %u\n", __func__,
+ snddev_mi2s_data->channel_mode);
+ return -EINVAL;
+ }
+
+ /* Set MI2S */
+ mi2s_set_hdmi_input_path(channels, WT_16_BIT,
+ snddev_mi2s_data->sd_lines);
+
+ afe_config.sample_rate = snddev_mi2s_data->default_sample_rate / 1000;
+ afe_config.channel_mode = snddev_mi2s_data->channel_mode;
+ afe_config.volume = AFE_VOLUME_UNITY;
+ rc = afe_enable(AFE_HW_PATH_MI2S_TX, &afe_config);
+
+ if (IS_ERR_VALUE(rc)) {
+ MM_ERR("%s: afe_enable failed for AFE_HW_PATH_MI2S_TX "
+ "rc = %d\n", __func__, rc);
+ return -ENODEV;
+ }
+
+ /* Enable audio path */
+ if (snddev_mi2s_data->route)
+ snddev_mi2s_data->route();
+
+ return 0;
+}
+
+static int snddev_mi2s_open_rx(struct msm_snddev_info *dev_info)
+{
+ int rc;
+ struct msm_afe_config afe_config;
+ u8 channels;
+ struct snddev_mi2s_data *snddev_mi2s_data = dev_info->private_data;
+
+ MM_DBG("%s: channel_mode = %u sd_line_mask = 0x%x "
+ "default_sample_rate = %u\n", __func__,
+ snddev_mi2s_data->channel_mode, snddev_mi2s_data->sd_lines,
+ snddev_mi2s_data->default_sample_rate);
+
+ if (snddev_mi2s_data->channel_mode == 2)
+ channels = MI2S_CHAN_STEREO;
+ else if (snddev_mi2s_data->channel_mode == 4)
+ channels = MI2S_CHAN_4CHANNELS;
+ else if (snddev_mi2s_data->channel_mode == 6)
+ channels = MI2S_CHAN_6CHANNELS;
+ else if (snddev_mi2s_data->channel_mode == 8)
+ channels = MI2S_CHAN_8CHANNELS;
+ else
+ channels = MI2S_CHAN_MONO_RAW;
+
+ /* Set MI2S */
+ mi2s_set_hdmi_output_path(channels, WT_16_BIT,
+ snddev_mi2s_data->sd_lines);
+
+ /* Start AFE */
+ afe_config.sample_rate = snddev_mi2s_data->default_sample_rate / 1000;
+ afe_config.channel_mode = snddev_mi2s_data->channel_mode;
+ afe_config.volume = AFE_VOLUME_UNITY;
+ rc = afe_enable(AFE_HW_PATH_MI2S_RX, &afe_config);
+
+ if (IS_ERR_VALUE(rc)) {
+ MM_ERR("%s: encounter error\n", __func__);
+ return -ENODEV;
+ }
+
+ /* Enable audio path */
+ if (snddev_mi2s_data->route)
+ snddev_mi2s_data->route();
+
+ MM_DBG("%s: enabled %s \n", __func__, snddev_mi2s_data->name);
+
+ return 0;
+}
+
+static int snddev_mi2s_open(struct msm_snddev_info *dev_info)
+{
+ int rc = 0;
+ struct snddev_mi2s_drv_state *drv = &snddev_mi2s_drv;
+ u32 dir;
+ struct snddev_mi2s_data *snddev_mi2s_data = dev_info->private_data;
+
+ if (!dev_info) {
+ MM_ERR("%s: msm_snddev_info is null \n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&drv->lock);
+
+ if (drv->sd_lines_used & snddev_mi2s_data->sd_lines) {
+ MM_ERR("%s: conflict in SD data line. can not use the device\n",
+ __func__);
+ mutex_unlock(&drv->lock);
+ return -EBUSY;
+ }
+
+ if (!drv->clocks_enabled) {
+
+ rc = mi2s_config_clk_gpio();
+ if (rc) {
+ MM_ERR("%s: mi2s GPIO config failed for %s\n",
+ __func__, snddev_mi2s_data->name);
+ mutex_unlock(&drv->lock);
+ return -EIO;
+ }
+ clk_enable(drv->mclk);
+ clk_enable(drv->sclk);
+ drv->clocks_enabled = 1;
+ MM_DBG("%s: clks enabled \n", __func__);
+ } else
+ MM_DBG("%s: clks already enabled \n", __func__);
+
+ if (snddev_mi2s_data->capability & SNDDEV_CAP_RX) {
+
+ dir = DIR_RX;
+ rc = mi2s_config_data_gpio(dir, snddev_mi2s_data->sd_lines);
+
+ if (rc) {
+ rc = -EIO;
+ MM_ERR("%s: mi2s GPIO config failed for %s\n",
+ __func__, snddev_mi2s_data->name);
+ goto mi2s_data_gpio_failure;
+ }
+
+ MM_DBG("%s: done gpio config rx SD lines\n", __func__);
+
+ rc = snddev_mi2s_open_rx(dev_info);
+
+ if (IS_ERR_VALUE(rc)) {
+ MM_ERR(" snddev_mi2s_open_rx failed \n");
+ goto mi2s_cleanup_open;
+ }
+
+ drv->sd_lines_used |= snddev_mi2s_data->sd_lines;
+
+ MM_DBG("%s: sd_lines_used = 0x%x\n", __func__,
+ drv->sd_lines_used);
+ mutex_unlock(&drv->lock);
+
+ } else {
+ dir = DIR_TX;
+ rc = mi2s_config_data_gpio(dir, snddev_mi2s_data->sd_lines);
+
+ if (rc) {
+ rc = -EIO;
+ MM_ERR("%s: mi2s GPIO config failed for %s\n",
+ __func__, snddev_mi2s_data->name);
+ goto mi2s_data_gpio_failure;
+ }
+ MM_DBG("%s: done data line gpio config for %s\n",
+ __func__, snddev_mi2s_data->name);
+
+ rc = snddev_mi2s_open_tx(dev_info);
+
+ if (IS_ERR_VALUE(rc)) {
+ MM_ERR(" snddev_mi2s_open_tx failed \n");
+ goto mi2s_cleanup_open;
+ }
+
+ drv->sd_lines_used |= snddev_mi2s_data->sd_lines;
+ MM_DBG("%s: sd_lines_used = 0x%x\n", __func__,
+ drv->sd_lines_used);
+ mutex_unlock(&drv->lock);
+ }
+
+ return 0;
+
+mi2s_cleanup_open:
+ mi2s_unconfig_data_gpio(dir, snddev_mi2s_data->sd_lines);
+
+ /* Disable audio path */
+ if (snddev_mi2s_data->deroute)
+ snddev_mi2s_data->deroute();
+
+mi2s_data_gpio_failure:
+ if (!drv->sd_lines_used) {
+ clk_disable(drv->sclk);
+ clk_disable(drv->mclk);
+ drv->clocks_enabled = 0;
+ mi2s_unconfig_clk_gpio();
+ }
+ mutex_unlock(&drv->lock);
+ return rc;
+}
+
+static int snddev_mi2s_close(struct msm_snddev_info *dev_info)
+{
+ struct snddev_mi2s_drv_state *drv = &snddev_mi2s_drv;
+ int dir;
+ struct snddev_mi2s_data *snddev_mi2s_data = dev_info->private_data;
+
+ if (!dev_info) {
+ MM_ERR("%s: msm_snddev_info is null \n", __func__);
+ return -EINVAL;
+ }
+
+ if (!dev_info->opened) {
+ MM_ERR(" %s: calling close device with out opening the"
+ " device \n", __func__);
+ return -EIO;
+ }
+
+ mutex_lock(&drv->lock);
+
+ drv->sd_lines_used &= ~snddev_mi2s_data->sd_lines;
+
+ MM_DBG("%s: sd_lines in use = 0x%x\n", __func__, drv->sd_lines_used);
+
+ if (snddev_mi2s_data->capability & SNDDEV_CAP_RX) {
+ dir = DIR_RX;
+ afe_disable(AFE_HW_PATH_MI2S_RX);
+ } else {
+ dir = DIR_TX;
+ afe_disable(AFE_HW_PATH_MI2S_TX);
+ }
+
+ mi2s_unconfig_data_gpio(dir, snddev_mi2s_data->sd_lines);
+
+ if (!drv->sd_lines_used) {
+ clk_disable(drv->sclk);
+ clk_disable(drv->mclk);
+ drv->clocks_enabled = 0;
+ mi2s_unconfig_clk_gpio();
+ }
+
+ /* Disable audio path */
+ if (snddev_mi2s_data->deroute)
+ snddev_mi2s_data->deroute();
+
+ mutex_unlock(&drv->lock);
+
+ return 0;
+}
+
+static int snddev_mi2s_set_freq(struct msm_snddev_info *dev_info, u32 req_freq)
+{
+ if (req_freq != 48000) {
+ MM_DBG("%s: Unsupported Frequency:%d\n", __func__, req_freq);
+ return -EINVAL;
+ }
+ return 48000;
+}
+
+static int snddev_mi2s_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct snddev_mi2s_data *pdata;
+ struct msm_snddev_info *dev_info;
+
+ if (!pdev || !pdev->dev.platform_data) {
+ printk(KERN_ALERT "Invalid caller \n");
+ return -ENODEV;
+ }
+
+ pdata = pdev->dev.platform_data;
+ if ((pdata->capability & SNDDEV_CAP_RX) &&
+ (pdata->capability & SNDDEV_CAP_TX)) {
+ MM_ERR("%s: invalid device data either RX or TX\n", __func__);
+ return -ENODEV;
+ }
+
+ dev_info = kzalloc(sizeof(struct msm_snddev_info), GFP_KERNEL);
+ if (!dev_info) {
+ MM_ERR("%s: uneable to allocate memeory for msm_snddev_info \n",
+ __func__);
+
+ return -ENOMEM;
+ }
+
+ dev_info->name = pdata->name;
+ dev_info->copp_id = pdata->copp_id;
+ dev_info->acdb_id = pdata->acdb_id;
+ dev_info->private_data = (void *)pdata;
+ dev_info->dev_ops.open = snddev_mi2s_open;
+ dev_info->dev_ops.close = snddev_mi2s_close;
+ dev_info->dev_ops.set_freq = snddev_mi2s_set_freq;
+ dev_info->capability = pdata->capability;
+ dev_info->opened = 0;
+ msm_snddev_register(dev_info);
+ dev_info->sample_rate = pdata->default_sample_rate;
+
+ MM_DBG("%s: probe done for %s\n", __func__, pdata->name);
+ return rc;
+}
+
+static int snddev_mi2s_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct platform_driver snddev_mi2s_driver = {
+ .probe = snddev_mi2s_probe,
+ .remove = snddev_mi2s_remove,
+ .driver = {.name = "snddev_mi2s"}
+};
+
+static int __init snddev_mi2s_init(void)
+{
+ s32 rc;
+ struct snddev_mi2s_drv_state *drv = &snddev_mi2s_drv;
+
+ rc = platform_driver_register(&snddev_mi2s_driver);
+ if (IS_ERR_VALUE(rc)) {
+
+ MM_ERR("%s: platform_driver_register failed \n", __func__);
+ goto error_platform_driver;
+ }
+
+ drv->mclk = clk_get(NULL, "mi2s_m_clk");
+ if (IS_ERR(drv->mclk)) {
+ MM_ERR("%s: clk_get mi2s_mclk failed \n", __func__);
+ goto error_mclk;
+ }
+
+ drv->sclk = clk_get(NULL, "mi2s_s_clk");
+ if (IS_ERR(drv->sclk)) {
+ MM_ERR("%s: clk_get mi2s_sclk failed \n", __func__);
+
+ goto error_sclk;
+ }
+
+ mutex_init(&drv->lock);
+
+ MM_DBG("snddev_mi2s_init : done \n");
+
+ return 0;
+
+error_sclk:
+ clk_put(drv->mclk);
+error_mclk:
+ platform_driver_unregister(&snddev_mi2s_driver);
+error_platform_driver:
+
+ MM_ERR("%s: encounter error\n", __func__);
+ return -ENODEV;
+}
+
+static void __exit snddev_mi2s_exit(void)
+{
+ struct snddev_mi2s_drv_state *drv = &snddev_mi2s_drv;
+
+ platform_driver_unregister(&snddev_mi2s_driver);
+
+ clk_put(drv->sclk);
+ clk_put(drv->mclk);
+ return;
+}
+
+module_init(snddev_mi2s_init);
+module_exit(snddev_mi2s_exit);
+
+MODULE_DESCRIPTION("mi2s Sound Device driver");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/snddev_virtual.c b/arch/arm/mach-msm/qdsp5v2/snddev_virtual.c
new file mode 100644
index 0000000..cd93345
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/snddev_virtual.c
@@ -0,0 +1,122 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <asm/uaccess.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/qdsp5v2/snddev_virtual.h>
+#include <mach/debug_mm.h>
+#include <linux/slab.h>
+
+static int snddev_virtual_open(struct msm_snddev_info *dev_info)
+{
+ int rc = 0;
+
+ if (!dev_info)
+ rc = -EINVAL;
+ return rc;
+}
+
+static int snddev_virtual_close(struct msm_snddev_info *dev_info)
+{
+ int rc = 0;
+
+ if (!dev_info)
+ rc = -EINVAL;
+ return rc;
+}
+
+static int snddev_virtual_set_freq(struct msm_snddev_info *dev_info, u32 rate)
+{
+ int rc = 0;
+
+ if (!dev_info)
+ rc = -EINVAL;
+ return rate;
+}
+
+static int snddev_virtual_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct snddev_virtual_data *pdata;
+ struct msm_snddev_info *dev_info;
+
+ if (!pdev || !pdev->dev.platform_data) {
+ MM_ERR("Invalid caller\n");
+ rc = -EPERM;
+ goto error;
+ }
+ pdata = pdev->dev.platform_data;
+
+ dev_info = kmalloc(sizeof(struct msm_snddev_info), GFP_KERNEL);
+ if (!dev_info) {
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ dev_info->name = pdata->name;
+ dev_info->copp_id = pdata->copp_id;
+ dev_info->acdb_id = pdata->acdb_id;
+ dev_info->private_data = (void *) NULL;
+ dev_info->dev_ops.open = snddev_virtual_open;
+ dev_info->dev_ops.close = snddev_virtual_close;
+ dev_info->dev_ops.set_freq = snddev_virtual_set_freq;
+ dev_info->capability = pdata->capability;
+ dev_info->sample_rate = 8000;
+ dev_info->opened = 0;
+ dev_info->sessions = 0;
+
+ msm_snddev_register(dev_info);
+
+error:
+ return rc;
+}
+
+static int snddev_virtual_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct platform_driver snddev_virtual_driver = {
+ .probe = snddev_virtual_probe,
+ .remove = snddev_virtual_remove,
+ .driver = { .name = "snddev_virtual" }
+};
+
+static int __init snddev_virtual_init(void)
+{
+ int rc = 0;
+
+ MM_DBG(" snddev_virtual_init \n");
+ rc = platform_driver_register(&snddev_virtual_driver);
+ if (IS_ERR_VALUE(rc)) {
+ MM_ERR("platform driver register failure\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static void __exit snddev_virtual_exit(void)
+{
+ platform_driver_unregister(&snddev_virtual_driver);
+
+ return;
+}
+
+module_init(snddev_virtual_init);
+module_exit(snddev_virtual_exit);
+
+MODULE_DESCRIPTION("Virtual Sound Device driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/timpani_profile_7x30.h b/arch/arm/mach-msm/qdsp5v2/timpani_profile_7x30.h
new file mode 100644
index 0000000..f336597
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/timpani_profile_7x30.h
@@ -0,0 +1,622 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __MACH_QDSP5_V2_TIMPANI_PROFILE_H__
+#define __MACH_QDSP5_V2_MTIMPANI_PROFILE_H__
+
+/*
+ * TX Device Profiles
+ */
+
+/* Analog MIC */
+/* AMIC Primary mono */
+#define AMIC_PRI_MONO_8000_OSR_256 \
+ {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xD0)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \
+ {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x04, 0x04)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8b, 0xff, 0xE6)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8c, 0x03, 0x02)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x09, 0x09)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} }
+
+/* Headset MIC */
+#define AMIC1_HEADSET_TX_MONO_PRIMARY_OSR256 \
+ {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xC8)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \
+ {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x04, 0x04)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8b, 0xff, 0xE7)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8c, 0x03, 0x02)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)} }
+
+/*
+ * RX Device Profiles
+ */
+
+/* RX EAR */
+#define EAR_PRI_MONO_8000_OSR_256 \
+ {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0F)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x97, 0xFF, 0x01)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0xFF, 0x4C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x01, 0x01)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x03, 0x03)}, \
+ {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x01, 0x01)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0E)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x03, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x01, 0x00)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} }
+
+/* RX SPEAKER */
+#define SPEAKER_PRI_STEREO_48000_OSR_256 \
+ {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x08)}, \
+ {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x48)}, \
+ {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x24)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x10)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x10)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFC, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFC, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x05, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x00)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} };
+
+/*
+ * RX HPH PRIMARY
+ */
+
+/* RX HPH CLASS AB CAPLESS */
+
+#define HEADSET_AB_CPLS_48000_OSR_256 \
+ {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x55)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFF, 0x29)}, \
+ {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xF5)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFE, 0xC8)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x24)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} }
+
+/* AMIC dual */
+#define AMIC_DUAL_8000_OSR_256 \
+ {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x04)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xD0)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xFF, 0xC2)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \
+ {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8b, 0xff, 0xCE)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8c, 0x03, 0x02)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} }
+
+/* TTY RX */
+#define TTY_HEADSET_MONO_RX_8000_OSR_256 \
+ {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x97, 0xFF, 0x01)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x06)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x01)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x4C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x45)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFF, 0x29)}, \
+ {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xC5)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFE, 0xC8)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x20)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x10)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x08)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} }
+
+/* TTY TX */
+#define TTY_HEADSET_MONO_TX_8000_OSR_256 \
+ {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xA8)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \
+ {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x04, 0x04)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} }
+
+#define HEADSET_RX_CAPLESS_48000_OSR_256 \
+ {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x4e)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x04, 0xff, 0xBC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x64)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x25, 0x0F, 0x0B)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xfc, 0xfc)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xff, 0xa2)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0xab)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x01, 0x01)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xf0)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xff, 0x20)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x04)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFF, 0x04)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFF, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFF, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0x00)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} }
+
+#define HEADSET_STEREO_SPEAKER_STEREO_RX_CAPLESS_48000_OSR_256 \
+ {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x55)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x08)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFF, 0x29)}, \
+ {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xF5)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x48)}, \
+ {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x24)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x10)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x10)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x24)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFC, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFC, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x00)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} }
+
+#define HS_DMIC2_STEREO_8000_OSR_256 \
+ {{ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0x1F, 0x1E)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x92, 0x3F, 0x19)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0x3F, 0x24)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x39, 0x01)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA8, 0x0F, 0x0E)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x3F, 0x00)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0xC0, 0x00)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x92, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} }
+
+#define HPH_PRI_AB_LEG_STEREO \
+ {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x09)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x59)}, \
+ {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xF9)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x27)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x10)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x10)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} }
+
+#define HPH_PRI_D_LEG_STEREO \
+ {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x21, 0xFF, 0x60)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x22, 0xFF, 0xE1)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xFF, 0xD0)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2D, 0xFF, 0x6F)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2E, 0xFF, 0x55)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3F, 0xFF, 0x0F)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x40, 0xFF, 0x08)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x41, 0x08, 0x08)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x42, 0xFF, 0xBB)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x43, 0xFF, 0xF2)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x44, 0xF7, 0x37)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x45, 0xFF, 0xFF)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x46, 0xFF, 0x77)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x47, 0xFF, 0xF2)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x48, 0xF7, 0x37)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x49, 0xFF, 0xFF)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4A, 0xFF, 0x77)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x05)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0xFF, 0x8C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0F, 0x0A)}, \
+ {ADIE_CODEC_ACTION_DELAY_WAIT, 300000}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0F, 0x0F)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x10)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x10)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x10)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x10)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0F, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} }
+
+#define LB_AUXPGA_HPH_AB_CPLS_STEREO \
+ {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2F, 0xFF, 0x44)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x30, 0xFF, 0x92)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0x05)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0x55)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x30)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xAA)}, \
+ {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0xF5)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFF, 0x29)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x90, 0x90)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0x00)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} }
+
+#define LB_AUXPGA_LO_STEREO \
+ {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2F, 0xFF, 0x44)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x30, 0xFF, 0x92)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x08)}, \
+ {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x58)}, \
+ {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0xF0, 0xF0)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xAA)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x90, 0x90)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0xF0, 0x30)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x10)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x10)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0xF0, 0xF0)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFC, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFC, 0xAC)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0xF0, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x00)}, \
+ {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x90, 0x00)}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \
+ {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} }
+
+#endif
diff --git a/arch/arm/mach-msm/qdsp5v2/voice.c b/arch/arm/mach-msm/qdsp5v2/voice.c
new file mode 100644
index 0000000..c4560ed
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/voice.c
@@ -0,0 +1,748 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/msm_audio.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/dal.h>
+#include <linux/kthread.h>
+#include <linux/completion.h>
+#include <linux/wait.h>
+#include <mach/qdsp5v2/voice.h>
+#include <mach/debug_mm.h>
+
+struct voice_data {
+ void *handle; /* DALRPC handle */
+ void *cb_handle; /* DALRPC callback handle */
+ int network; /* Network information */
+ int dev_state;/*READY, CHANGE, REL_DONE,INIT*/
+ int voc_state;/*INIT, CHANGE, RELEASE, ACQUIRE */
+ struct mutex voc_lock;
+ struct mutex vol_lock;
+ int voc_event;
+ int dev_event;
+ atomic_t rel_start_flag;
+ atomic_t acq_start_flag;
+ atomic_t chg_start_flag;
+ struct task_struct *task;
+ struct completion complete;
+ wait_queue_head_t dev_wait;
+ wait_queue_head_t voc_wait;
+ uint32_t device_events;
+ /* cache the values related to Rx and Tx */
+ struct device_data dev_rx;
+ struct device_data dev_tx;
+ /* these default values are for all devices */
+ uint32_t default_mute_val;
+ uint32_t default_vol_val;
+ uint32_t default_sample_val;
+ /* call status */
+ int v_call_status; /* Start or End */
+ s32 max_rx_vol[VOC_RX_VOL_ARRAY_NUM]; /* [0] is for NB, [1] for WB */
+ s32 min_rx_vol[VOC_RX_VOL_ARRAY_NUM];
+};
+
+static struct voice_data voice;
+
+static int voice_cmd_device_info(struct voice_data *);
+static int voice_cmd_acquire_done(struct voice_data *);
+static void voice_auddev_cb_function(u32 evt_id,
+ union auddev_evt_data *evt_payload,
+ void *private_data);
+
+static int voice_cmd_change(void)
+{
+
+ struct voice_header hdr;
+ struct voice_data *v = &voice;
+ int err;
+
+ hdr.id = CMD_DEVICE_CHANGE;
+ hdr.data_len = 0;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+
+ err = dalrpc_fcn_5(VOICE_DALRPC_CMD, v->handle, &hdr,
+ sizeof(struct voice_header));
+
+ if (err)
+ MM_ERR("Voice change command failed\n");
+ return err;
+}
+
+static void voice_auddev_cb_function(u32 evt_id,
+ union auddev_evt_data *evt_payload,
+ void *private_data)
+{
+ struct voice_data *v = &voice;
+ int rc = 0, i;
+
+ MM_INFO("auddev_cb_function, evt_id=%d, dev_state=%d, voc_state=%d\n",
+ evt_id, v->dev_state, v->voc_state);
+ if ((evt_id != AUDDEV_EVT_START_VOICE) ||
+ (evt_id != AUDDEV_EVT_END_VOICE)) {
+ if (evt_payload == NULL) {
+ MM_ERR(" evt_payload is NULL pointer\n");
+ return;
+ }
+ }
+ switch (evt_id) {
+ case AUDDEV_EVT_START_VOICE:
+ if ((v->dev_state == DEV_INIT) ||
+ (v->dev_state == DEV_REL_DONE)) {
+ v->v_call_status = VOICE_CALL_START;
+ if ((v->dev_rx.enabled == VOICE_DEV_ENABLED)
+ && (v->dev_tx.enabled == VOICE_DEV_ENABLED)) {
+ v->dev_state = DEV_READY;
+ MM_DBG("dev_state into ready\n");
+ wake_up(&v->dev_wait);
+ }
+ }
+ break;
+ case AUDDEV_EVT_DEV_CHG_VOICE:
+ if (v->dev_state == DEV_READY) {
+ v->dev_rx.enabled = VOICE_DEV_DISABLED;
+ v->dev_tx.enabled = VOICE_DEV_DISABLED;
+ v->dev_state = DEV_CHANGE;
+ mutex_lock(&voice.voc_lock);
+ if (v->voc_state == VOICE_ACQUIRE) {
+ /* send device change to modem */
+ voice_cmd_change();
+ mutex_unlock(&voice.voc_lock);
+ msm_snddev_enable_sidetone(v->dev_rx.dev_id,
+ 0);
+ /* block to wait for CHANGE_START */
+ rc = wait_event_interruptible(
+ v->voc_wait, (v->voc_state == VOICE_CHANGE)
+ || (atomic_read(&v->chg_start_flag) == 1)
+ || (atomic_read(&v->rel_start_flag) == 1));
+ } else {
+ mutex_unlock(&voice.voc_lock);
+ MM_ERR(" Voice is not at ACQUIRE state\n");
+ }
+ } else if ((v->dev_state == DEV_INIT) ||
+ (v->dev_state == DEV_REL_DONE)) {
+ v->dev_rx.enabled = VOICE_DEV_DISABLED;
+ v->dev_tx.enabled = VOICE_DEV_DISABLED;
+ } else
+ MM_ERR(" device is not at proper state\n");
+ break;
+ case AUDDEV_EVT_DEV_RDY:
+ /* update the dev info */
+ if (evt_payload->voc_devinfo.dev_type == DIR_RX) {
+ for (i = 0; i < VOC_RX_VOL_ARRAY_NUM; i++) {
+ v->max_rx_vol[i] =
+ evt_payload->voc_devinfo.max_rx_vol[i];
+ v->min_rx_vol[i] =
+ evt_payload->voc_devinfo.min_rx_vol[i];
+ }
+ }
+ if (v->dev_state == DEV_CHANGE) {
+ if (evt_payload->voc_devinfo.dev_type == DIR_RX) {
+ v->dev_rx.dev_acdb_id =
+ evt_payload->voc_devinfo.acdb_dev_id;
+ v->dev_rx.sample =
+ evt_payload->voc_devinfo.dev_sample;
+ v->dev_rx.dev_id =
+ evt_payload->voc_devinfo.dev_id;
+ v->dev_rx.enabled = VOICE_DEV_ENABLED;
+ } else {
+ v->dev_tx.dev_acdb_id =
+ evt_payload->voc_devinfo.acdb_dev_id;
+ v->dev_tx.sample =
+ evt_payload->voc_devinfo.dev_sample;
+ v->dev_tx.enabled = VOICE_DEV_ENABLED;
+ v->dev_tx.dev_id =
+ evt_payload->voc_devinfo.dev_id;
+ }
+ if ((v->dev_rx.enabled == VOICE_DEV_ENABLED) &&
+ (v->dev_tx.enabled == VOICE_DEV_ENABLED)) {
+ v->dev_state = DEV_READY;
+ MM_DBG("dev state into ready\n");
+ voice_cmd_device_info(v);
+ wake_up(&v->dev_wait);
+ mutex_lock(&voice.voc_lock);
+ if (v->voc_state == VOICE_CHANGE) {
+ v->dev_event = DEV_CHANGE_READY;
+ complete(&v->complete);
+ }
+ mutex_unlock(&voice.voc_lock);
+ }
+ } else if ((v->dev_state == DEV_INIT) ||
+ (v->dev_state == DEV_REL_DONE)) {
+ if (evt_payload->voc_devinfo.dev_type == DIR_RX) {
+ v->dev_rx.dev_acdb_id =
+ evt_payload->voc_devinfo.acdb_dev_id;
+ v->dev_rx.sample =
+ evt_payload->voc_devinfo.dev_sample;
+ v->dev_rx.dev_id =
+ evt_payload->voc_devinfo.dev_id;
+ v->dev_rx.enabled = VOICE_DEV_ENABLED;
+ } else {
+ v->dev_tx.dev_acdb_id =
+ evt_payload->voc_devinfo.acdb_dev_id;
+ v->dev_tx.sample =
+ evt_payload->voc_devinfo.dev_sample;
+ v->dev_tx.dev_id =
+ evt_payload->voc_devinfo.dev_id;
+ v->dev_tx.enabled = VOICE_DEV_ENABLED;
+ }
+ if ((v->dev_rx.enabled == VOICE_DEV_ENABLED) &&
+ (v->dev_tx.enabled == VOICE_DEV_ENABLED) &&
+ (v->v_call_status == VOICE_CALL_START)) {
+ v->dev_state = DEV_READY;
+ MM_DBG("dev state into ready\n");
+ voice_cmd_device_info(v);
+ wake_up(&v->dev_wait);
+ mutex_lock(&voice.voc_lock);
+ if (v->voc_state == VOICE_CHANGE) {
+ v->dev_event = DEV_CHANGE_READY;
+ complete(&v->complete);
+ }
+ mutex_unlock(&voice.voc_lock);
+ }
+ } else
+ MM_ERR("Receive READY not at the proper state =%d\n",
+ v->dev_state);
+ break;
+ case AUDDEV_EVT_DEVICE_VOL_MUTE_CHG:
+ if (evt_payload->voc_devinfo.dev_type == DIR_TX)
+ v->dev_tx.mute =
+ evt_payload->voc_vm_info.dev_vm_val.mute;
+ else
+ v->dev_rx.volume = evt_payload->
+ voc_vm_info.dev_vm_val.vol;
+ /* send device info */
+ voice_cmd_device_info(v);
+ break;
+ case AUDDEV_EVT_REL_PENDING:
+ /* recover the tx mute and rx volume to the default values */
+ if (v->dev_state == DEV_READY) {
+ if (atomic_read(&v->rel_start_flag)) {
+ atomic_dec(&v->rel_start_flag);
+ if (evt_payload->voc_devinfo.dev_type == DIR_RX)
+ v->dev_rx.enabled = VOICE_DEV_DISABLED;
+ else
+ v->dev_tx.enabled = VOICE_DEV_DISABLED;
+ v->dev_state = DEV_REL_DONE;
+ wake_up(&v->dev_wait);
+ break;
+ }
+ mutex_lock(&voice.voc_lock);
+ if ((v->voc_state == VOICE_RELEASE) ||
+ (v->voc_state == VOICE_INIT)) {
+ if (evt_payload->voc_devinfo.dev_type
+ == DIR_RX) {
+ v->dev_rx.enabled = VOICE_DEV_DISABLED;
+ } else {
+ v->dev_tx.enabled = VOICE_DEV_DISABLED;
+ }
+ v->dev_state = DEV_REL_DONE;
+ mutex_unlock(&voice.voc_lock);
+ wake_up(&v->dev_wait);
+ } else {
+ /* send device change to modem */
+ voice_cmd_change();
+ mutex_unlock(&voice.voc_lock);
+ rc = wait_event_interruptible(
+ v->voc_wait, (v->voc_state == VOICE_CHANGE)
+ || (atomic_read(&v->chg_start_flag) == 1)
+ || (atomic_read(&v->rel_start_flag) == 1));
+ if (atomic_read(&v->rel_start_flag) == 1)
+ atomic_dec(&v->rel_start_flag);
+ /* clear Rx/Tx to Disable */
+ if (evt_payload->voc_devinfo.dev_type == DIR_RX)
+ v->dev_rx.enabled = VOICE_DEV_DISABLED;
+ else
+ v->dev_tx.enabled = VOICE_DEV_DISABLED;
+ v->dev_state = DEV_REL_DONE;
+ wake_up(&v->dev_wait);
+ }
+ } else if ((v->dev_state == DEV_INIT) ||
+ (v->dev_state == DEV_REL_DONE)) {
+ if (evt_payload->voc_devinfo.dev_type == DIR_RX)
+ v->dev_rx.enabled = VOICE_DEV_DISABLED;
+ else
+ v->dev_tx.enabled = VOICE_DEV_DISABLED;
+ }
+ break;
+ case AUDDEV_EVT_END_VOICE:
+ /* recover the tx mute and rx volume to the default values */
+ v->dev_tx.mute = v->default_mute_val;
+ v->dev_rx.volume = v->default_vol_val;
+
+ if (v->dev_rx.enabled == VOICE_DEV_ENABLED)
+ msm_snddev_enable_sidetone(v->dev_rx.dev_id, 0);
+
+ if ((v->dev_state == DEV_READY) ||
+ (v->dev_state == DEV_CHANGE)) {
+ if (atomic_read(&v->rel_start_flag)) {
+ atomic_dec(&v->rel_start_flag);
+ v->v_call_status = VOICE_CALL_END;
+ v->dev_state = DEV_REL_DONE;
+ wake_up(&v->dev_wait);
+ break;
+ }
+ mutex_lock(&voice.voc_lock);
+ if ((v->voc_state == VOICE_RELEASE) ||
+ (v->voc_state == VOICE_INIT)) {
+ v->v_call_status = VOICE_CALL_END;
+ v->dev_state = DEV_REL_DONE;
+ mutex_unlock(&voice.voc_lock);
+ wake_up(&v->dev_wait);
+ } else {
+ /* send mute and default volume value to MCAD */
+ voice_cmd_device_info(v);
+ /* send device change to modem */
+ voice_cmd_change();
+ mutex_unlock(&voice.voc_lock);
+ /* block to wait for RELEASE_START
+ or CHANGE_START */
+ rc = wait_event_interruptible(
+ v->voc_wait, (v->voc_state == VOICE_CHANGE)
+ || (atomic_read(&v->chg_start_flag) == 1)
+ || (atomic_read(&v->rel_start_flag) == 1));
+ if (atomic_read(&v->rel_start_flag) == 1)
+ atomic_dec(&v->rel_start_flag);
+ /* set voice call to END state */
+ v->v_call_status = VOICE_CALL_END;
+ v->dev_state = DEV_REL_DONE;
+ wake_up(&v->dev_wait);
+ }
+ } else
+ v->v_call_status = VOICE_CALL_END;
+ break;
+ case AUDDEV_EVT_FREQ_CHG:
+ MM_DBG("Voice Driver got sample rate change Event\n");
+ MM_DBG("sample rate %d\n", evt_payload->freq_info.sample_rate);
+ MM_DBG("dev_type %d\n", evt_payload->freq_info.dev_type);
+ MM_DBG("acdb_dev_id %d\n", evt_payload->freq_info.acdb_dev_id);
+ if (v->dev_state == DEV_READY) {
+ v->dev_tx.enabled = VOICE_DEV_DISABLED;
+ v->dev_state = DEV_CHANGE;
+ mutex_lock(&voice.voc_lock);
+ if (v->voc_state == VOICE_ACQUIRE) {
+ msm_snddev_enable_sidetone(v->dev_rx.dev_id,
+ 0);
+ /* send device change to modem */
+ voice_cmd_change();
+ mutex_unlock(&voice.voc_lock);
+ /* block to wait for CHANGE_START */
+ rc = wait_event_interruptible(
+ v->voc_wait, (v->voc_state == VOICE_CHANGE)
+ || (atomic_read(&v->chg_start_flag) == 1)
+ || (atomic_read(&v->rel_start_flag) == 1));
+ } else {
+ mutex_unlock(&voice.voc_lock);
+ MM_ERR(" Voice is not at ACQUIRE state\n");
+ }
+ } else if ((v->dev_state == DEV_INIT) ||
+ (v->dev_state == DEV_REL_DONE)) {
+ v->dev_tx.enabled = VOICE_DEV_DISABLED;
+ } else
+ MM_ERR("Event not at the proper state =%d\n",
+ v->dev_state);
+ break;
+ default:
+ MM_ERR("UNKNOWN EVENT\n");
+ }
+ return;
+}
+EXPORT_SYMBOL(voice_auddev_cb_function);
+
+static void remote_cb_function(void *context, u32 param,
+ void *evt_buf, u32 len)
+{
+ struct voice_header *hdr;
+ struct voice_data *v = context;
+
+ hdr = (struct voice_header *)evt_buf;
+
+ MM_INFO("len=%d id=%d\n", len, hdr->id);
+
+ if (len <= 0) {
+ MM_ERR("unexpected event with length %d \n", len);
+ return;
+ }
+
+ switch (hdr->id) {
+ case EVENT_ACQUIRE_START:
+ atomic_inc(&v->acq_start_flag);
+ wake_up(&v->dev_wait);
+ v->voc_event = VOICE_ACQUIRE_START;
+ v->network = ((struct voice_network *)evt_buf)->network_info;
+ complete(&v->complete);
+ break;
+ case EVENT_RELEASE_START:
+ /* If ACQUIRED come in before the RELEASE,
+ * will only services the RELEASE */
+ atomic_inc(&v->rel_start_flag);
+ wake_up(&v->voc_wait);
+ wake_up(&v->dev_wait);
+ v->voc_event = VOICE_RELEASE_START;
+ complete(&v->complete);
+ break;
+ case EVENT_CHANGE_START:
+ atomic_inc(&v->chg_start_flag);
+ wake_up(&v->voc_wait);
+ v->voc_event = VOICE_CHANGE_START;
+ complete(&v->complete);
+ break;
+ case EVENT_NETWORK_RECONFIG:
+ /* send network change to audio_dev,
+ if sample rate is less than 16k,
+ otherwise, send acquire done */
+ v->voc_event = VOICE_NETWORK_RECONFIG;
+ v->network = ((struct voice_network *)evt_buf)->network_info;
+ complete(&v->complete);
+ break;
+ default:
+ MM_ERR("Undefined event %d \n", hdr->id);
+ }
+
+}
+
+static int voice_cmd_init(struct voice_data *v)
+{
+
+ struct voice_init cmd;
+ int err;
+
+ MM_DBG("\n"); /* Macro prints the file name and function */
+
+ cmd.hdr.id = CMD_VOICE_INIT;
+ cmd.hdr.data_len = sizeof(struct voice_init) -
+ sizeof(struct voice_header);
+ cmd.cb_handle = v->cb_handle;
+
+ err = dalrpc_fcn_5(VOICE_DALRPC_CMD, v->handle, &cmd,
+ sizeof(struct voice_init));
+
+ if (err)
+ MM_ERR("Voice init command failed\n");
+ return err;
+}
+
+static int voice_cmd_acquire_done(struct voice_data *v)
+{
+ struct voice_header hdr;
+ int err;
+
+ hdr.id = CMD_ACQUIRE_DONE;
+ hdr.data_len = 0;
+
+ MM_INFO("\n"); /* Macro prints the file name and function */
+
+ /* Enable HW sidetone if device supports it */
+ msm_snddev_enable_sidetone(v->dev_rx.dev_id, 1);
+
+ err = dalrpc_fcn_5(VOICE_DALRPC_CMD, v->handle, &hdr,
+ sizeof(struct voice_header));
+
+ if (err)
+ MM_ERR("Voice acquire done command failed\n");
+ return err;
+}
+
+static int voice_cmd_device_info(struct voice_data *v)
+{
+ struct voice_device cmd;
+ int err, vol;
+
+ MM_INFO("tx_dev=%d, rx_dev=%d, tx_sample=%d, tx_mute=%d\n",
+ v->dev_tx.dev_acdb_id, v->dev_rx.dev_acdb_id,
+ v->dev_tx.sample, v->dev_tx.mute);
+
+ mutex_lock(&voice.vol_lock);
+
+ cmd.hdr.id = CMD_DEVICE_INFO;
+ cmd.hdr.data_len = sizeof(struct voice_device) -
+ sizeof(struct voice_header);
+ cmd.tx_device = v->dev_tx.dev_acdb_id;
+ cmd.rx_device = v->dev_rx.dev_acdb_id;
+ if (v->network == NETWORK_WCDMA_WB)
+ vol = v->min_rx_vol[VOC_WB_INDEX] +
+ ((v->max_rx_vol[VOC_WB_INDEX] -
+ v->min_rx_vol[VOC_WB_INDEX]) * v->dev_rx.volume)/100;
+ else
+ vol = v->min_rx_vol[VOC_NB_INDEX] +
+ ((v->max_rx_vol[VOC_NB_INDEX] -
+ v->min_rx_vol[VOC_NB_INDEX]) * v->dev_rx.volume)/100;
+ cmd.rx_volume = (u32)vol; /* in mb */
+ cmd.rx_mute = 0;
+ cmd.tx_mute = v->dev_tx.mute;
+ cmd.rx_sample = v->dev_rx.sample/1000;
+ cmd.tx_sample = v->dev_tx.sample/1000;
+
+ MM_DBG("rx_vol=%d, rx_sample=%d\n", cmd.rx_volume, v->dev_rx.sample);
+
+ err = dalrpc_fcn_5(VOICE_DALRPC_CMD, v->handle, &cmd,
+ sizeof(struct voice_device));
+
+ mutex_unlock(&voice.vol_lock);
+
+ if (err)
+ MM_ERR("Voice device command failed\n");
+ return err;
+}
+EXPORT_SYMBOL(voice_cmd_device_info);
+
+void voice_change_sample_rate(struct voice_data *v)
+{
+ int freq = 48000;
+ int rc = 0;
+
+ MM_DBG("network =%d, vote freq=%d\n", v->network, freq);
+ if (freq != v->dev_tx.sample) {
+ rc = msm_snddev_request_freq(&freq, 0,
+ SNDDEV_CAP_TX, AUDDEV_CLNT_VOC);
+ if (rc >= 0) {
+ v->dev_tx.sample = freq;
+ MM_DBG(" vote for freq=%d successfully \n", freq);
+ } else
+ MM_ERR(" voting for freq=%d failed.\n", freq);
+ }
+}
+
+static int voice_thread(void *data)
+{
+ struct voice_data *v = (struct voice_data *)data;
+ int rc = 0;
+
+ MM_INFO("voice_thread() start\n");
+
+ while (!kthread_should_stop()) {
+ wait_for_completion(&v->complete);
+ init_completion(&v->complete);
+
+ MM_DBG(" voc_event=%d, voice state =%d, dev_event=%d\n",
+ v->voc_event, v->voc_state, v->dev_event);
+ switch (v->voc_event) {
+ case VOICE_ACQUIRE_START:
+ /* check if dev_state = READY */
+ /* if ready, send device_info and acquire_done */
+ /* if not ready, block to wait the dev_state = READY */
+ if ((v->voc_state == VOICE_INIT) ||
+ (v->voc_state == VOICE_RELEASE)) {
+ if (v->dev_state == DEV_READY) {
+ mutex_lock(&voice.voc_lock);
+ voice_change_sample_rate(v);
+ rc = voice_cmd_device_info(v);
+ rc = voice_cmd_acquire_done(v);
+ v->voc_state = VOICE_ACQUIRE;
+ mutex_unlock(&voice.voc_lock);
+ broadcast_event(
+ AUDDEV_EVT_VOICE_STATE_CHG,
+ VOICE_STATE_INCALL, SESSION_IGNORE);
+ } else {
+ rc = wait_event_interruptible(
+ v->dev_wait,
+ (v->dev_state == DEV_READY)
+ || (atomic_read(&v->rel_start_flag)
+ == 1));
+ if (atomic_read(&v->rel_start_flag)
+ == 1) {
+ v->voc_state = VOICE_RELEASE;
+ atomic_dec(&v->rel_start_flag);
+ msm_snddev_withdraw_freq(0,
+ SNDDEV_CAP_TX, AUDDEV_CLNT_VOC);
+ broadcast_event(
+ AUDDEV_EVT_VOICE_STATE_CHG,
+ VOICE_STATE_OFFCALL,
+ SESSION_IGNORE);
+ } else {
+ mutex_lock(&voice.voc_lock);
+ voice_change_sample_rate(v);
+ rc = voice_cmd_device_info(v);
+ rc = voice_cmd_acquire_done(v);
+ v->voc_state = VOICE_ACQUIRE;
+ mutex_unlock(&voice.voc_lock);
+ broadcast_event(
+ AUDDEV_EVT_VOICE_STATE_CHG,
+ VOICE_STATE_INCALL,
+ SESSION_IGNORE);
+ }
+ }
+ } else
+ MM_ERR("Get this event at the wrong state\n");
+ if (atomic_read(&v->acq_start_flag))
+ atomic_dec(&v->acq_start_flag);
+ break;
+ case VOICE_RELEASE_START:
+ MM_DBG("broadcast voice call end\n");
+ broadcast_event(AUDDEV_EVT_VOICE_STATE_CHG,
+ VOICE_STATE_OFFCALL, SESSION_IGNORE);
+ if ((v->dev_state == DEV_REL_DONE) ||
+ (v->dev_state == DEV_INIT)) {
+ v->voc_state = VOICE_RELEASE;
+ msm_snddev_withdraw_freq(0, SNDDEV_CAP_TX,
+ AUDDEV_CLNT_VOC);
+ } else {
+ /* wait for the dev_state = RELEASE */
+ rc = wait_event_interruptible(v->dev_wait,
+ (v->dev_state == DEV_REL_DONE)
+ || (atomic_read(&v->acq_start_flag) == 1));
+ if (atomic_read(&v->acq_start_flag) == 1)
+ atomic_dec(&v->acq_start_flag);
+ v->voc_state = VOICE_RELEASE;
+ msm_snddev_withdraw_freq(0, SNDDEV_CAP_TX,
+ AUDDEV_CLNT_VOC);
+ }
+ if (atomic_read(&v->rel_start_flag))
+ atomic_dec(&v->rel_start_flag);
+ break;
+ case VOICE_CHANGE_START:
+ if (v->voc_state == VOICE_ACQUIRE)
+ v->voc_state = VOICE_CHANGE;
+ else
+ MM_ERR("Get this event at the wrong state\n");
+ wake_up(&v->voc_wait);
+ if (atomic_read(&v->chg_start_flag))
+ atomic_dec(&v->chg_start_flag);
+ break;
+ case VOICE_NETWORK_RECONFIG:
+ if ((v->voc_state == VOICE_ACQUIRE)
+ || (v->voc_state == VOICE_CHANGE)) {
+ voice_change_sample_rate(v);
+ rc = voice_cmd_device_info(v);
+ rc = voice_cmd_acquire_done(v);
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch (v->dev_event) {
+ case DEV_CHANGE_READY:
+ if (v->voc_state == VOICE_CHANGE) {
+ mutex_lock(&voice.voc_lock);
+ msm_snddev_enable_sidetone(v->dev_rx.dev_id,
+ 1);
+ /* update voice state */
+ v->voc_state = VOICE_ACQUIRE;
+ v->dev_event = 0;
+ mutex_unlock(&voice.voc_lock);
+ broadcast_event(AUDDEV_EVT_VOICE_STATE_CHG,
+ VOICE_STATE_INCALL, SESSION_IGNORE);
+ } else {
+ mutex_lock(&voice.voc_lock);
+ v->dev_event = 0;
+ mutex_unlock(&voice.voc_lock);
+ MM_ERR("Get this event at the wrong state\n");
+ }
+ break;
+ default:
+ mutex_lock(&voice.voc_lock);
+ v->dev_event = 0;
+ mutex_unlock(&voice.voc_lock);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int __init voice_init(void)
+{
+ int rc, i;
+ struct voice_data *v = &voice;
+ MM_INFO("\n"); /* Macro prints the file name and function */
+
+ mutex_init(&voice.voc_lock);
+ mutex_init(&voice.vol_lock);
+ v->handle = NULL;
+ v->cb_handle = NULL;
+
+ /* set default value */
+ v->default_mute_val = 1; /* default is mute */
+ v->default_vol_val = 0;
+ v->default_sample_val = 8000;
+ for (i = 0; i < VOC_RX_VOL_ARRAY_NUM; i++) {
+ v->max_rx_vol[i] = 0;
+ v->min_rx_vol[i] = 0;
+ }
+ v->network = NETWORK_GSM;
+
+ /* initialize dev_rx and dev_tx */
+ memset(&v->dev_tx, 0, sizeof(struct device_data));
+ memset(&v->dev_rx, 0, sizeof(struct device_data));
+ v->dev_rx.volume = v->default_vol_val;
+ v->dev_tx.mute = v->default_mute_val;
+
+ v->dev_state = DEV_INIT;
+ v->voc_state = VOICE_INIT;
+ atomic_set(&v->rel_start_flag, 0);
+ atomic_set(&v->acq_start_flag, 0);
+ v->dev_event = 0;
+ v->voc_event = 0;
+ init_completion(&voice.complete);
+ init_waitqueue_head(&v->dev_wait);
+ init_waitqueue_head(&v->voc_wait);
+
+ /* get device handle */
+ rc = daldevice_attach(VOICE_DALRPC_DEVICEID,
+ VOICE_DALRPC_PORT_NAME,
+ VOICE_DALRPC_CPU,
+ &v->handle);
+ if (rc) {
+ MM_ERR("Voc DALRPC call to Modem attach failed\n");
+ goto done;
+ }
+
+ /* Allocate the callback handle */
+ v->cb_handle = dalrpc_alloc_cb(v->handle, remote_cb_function, v);
+ if (v->cb_handle == NULL) {
+ MM_ERR("Allocate Callback failure\n");
+ goto err;
+ }
+
+ /* setup the callback */
+ rc = voice_cmd_init(v);
+ if (rc)
+ goto err1;
+
+ v->device_events = AUDDEV_EVT_DEV_CHG_VOICE |
+ AUDDEV_EVT_DEV_RDY |
+ AUDDEV_EVT_REL_PENDING |
+ AUDDEV_EVT_START_VOICE |
+ AUDDEV_EVT_END_VOICE |
+ AUDDEV_EVT_DEVICE_VOL_MUTE_CHG |
+ AUDDEV_EVT_FREQ_CHG;
+
+ MM_DBG(" to register call back \n");
+ /* register callback to auddev */
+ auddev_register_evt_listner(v->device_events, AUDDEV_CLNT_VOC,
+ 0, voice_auddev_cb_function, v);
+
+ /* create and start thread */
+ v->task = kthread_run(voice_thread, v, "voice");
+ if (IS_ERR(v->task)) {
+ rc = PTR_ERR(v->task);
+ v->task = NULL;
+ } else
+ goto done;
+
+err1: dalrpc_dealloc_cb(v->handle, v->cb_handle);
+err:
+ daldevice_detach(v->handle);
+ v->handle = NULL;
+done:
+ return rc;
+}
+
+late_initcall(voice_init);