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/qdsp6/Makefile b/arch/arm/mach-msm/qdsp6/Makefile
new file mode 100644
index 0000000..9a55612
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/Makefile
@@ -0,0 +1,19 @@
+obj-y += dal.o
+obj-y += q6audio.o
+obj-y += analog_audio.o
+obj-y += pcm_out.o
+obj-y += pcm_in.o
+obj-y += auxpcm_lb_out.o
+obj-y += auxpcm_lb_in.o
+obj-y += aac_in.o
+obj-y += qcelp_in.o
+obj-y += evrc_in.o
+obj-y += amrnb_in.o
+obj-y += mp3.o
+obj-y += dtmf.o
+obj-y += routing.o
+obj-y += audio_ctl.o
+obj-y += msm_q6vdec.o
+obj-y += msm_q6venc.o
+obj-y += dsp_debug.o
+obj-$(CONFIG_QSD_AUDIO) += audiov2/
diff --git a/arch/arm/mach-msm/qdsp6/aac_in.c b/arch/arm/mach-msm/qdsp6/aac_in.c
new file mode 100644
index 0000000..9e1d5b6
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/aac_in.c
@@ -0,0 +1,470 @@
+/*
+ * Copyright (C) 2009 Google, Inc.
+ * Copyright (C) 2009 HTC Corporation
+ * Copyright (c) 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/slab.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+
+#include <linux/msm_audio.h>
+#include <linux/msm_audio_aac.h>
+#include <mach/msm_qdsp6_audio.h>
+#include <mach/debug_mm.h>
+
+#define AAC_FC_BUFF_CNT 10
+#define AAC_READ_TIMEOUT 2000
+struct aac_fc_buff {
+ struct mutex lock;
+ int empty;
+ void *data;
+ int size;
+ int actual_size;
+};
+
+struct aac_fc {
+ struct task_struct *task;
+ wait_queue_head_t fc_wq;
+ struct aac_fc_buff fc_buff[AAC_FC_BUFF_CNT];
+ int buff_index;
+};
+struct aac {
+ struct mutex lock;
+ struct msm_audio_aac_enc_config cfg;
+ struct msm_audio_stream_config str_cfg;
+ struct audio_client *audio_client;
+ struct msm_voicerec_mode voicerec_mode;
+ struct aac_fc *aac_fc;
+};
+
+static int q6_aac_flowcontrol(void *data)
+{
+ struct audio_client *ac;
+ struct audio_buffer *ab;
+ struct aac *aac = data;
+ int buff_index = 0;
+ int xfer = 0;
+ struct aac_fc *fc;
+
+
+ ac = aac->audio_client;
+ fc = aac->aac_fc;
+ if (!ac) {
+ pr_err("[%s:%s] audio_client is NULL\n", __MM_FILE__, __func__);
+ return 0;
+ }
+
+ while (!kthread_should_stop()) {
+ ab = ac->buf + ac->cpu_buf;
+ if (ab->used)
+ wait_event(ac->wait, (ab->used == 0));
+ pr_debug("[%s:%s] ab->data = %p, cpu_buf = %d\n", __MM_FILE__,
+ __func__, ab->data, ac->cpu_buf);
+ xfer = ab->actual_size;
+
+ mutex_lock(&(fc->fc_buff[buff_index].lock));
+ if (!fc->fc_buff[buff_index].empty) {
+ pr_err("[%s:%s] flow control buffer[%d] not read!\n",
+ __MM_FILE__, __func__, buff_index);
+ }
+
+ if (fc->fc_buff[buff_index].size < xfer) {
+ pr_err("[%s:%s] buffer %d too small\n", __MM_FILE__,
+ __func__, buff_index);
+ memcpy(fc->fc_buff[buff_index].data,
+ ab->data, fc->fc_buff[buff_index].size);
+ fc->fc_buff[buff_index].empty = 0;
+ fc->fc_buff[buff_index].actual_size =
+ fc->fc_buff[buff_index].size;
+ } else {
+ memcpy(fc->fc_buff[buff_index].data, ab->data, xfer);
+ fc->fc_buff[buff_index].empty = 0;
+ fc->fc_buff[buff_index].actual_size = xfer;
+ }
+ mutex_unlock(&(fc->fc_buff[buff_index].lock));
+ /*wake up client, if any*/
+ wake_up(&fc->fc_wq);
+
+ buff_index++;
+ if (buff_index >= AAC_FC_BUFF_CNT)
+ buff_index = 0;
+
+ ab->used = 1;
+
+ q6audio_read(ac, ab);
+ ac->cpu_buf ^= 1;
+ }
+
+ return 0;
+}
+static long q6_aac_in_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct aac *aac = file->private_data;
+ int rc = 0;
+ int i = 0;
+ struct aac_fc *fc;
+ int size = 0;
+
+ mutex_lock(&aac->lock);
+ switch (cmd) {
+ case AUDIO_SET_VOLUME:
+ break;
+ case AUDIO_GET_STATS:
+ {
+ struct msm_audio_stats stats;
+ pr_debug("[%s:%s] GET_STATS\n", __MM_FILE__, __func__);
+ memset(&stats, 0, sizeof(stats));
+ if (copy_to_user((void *) arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ }
+ case AUDIO_START:
+ {
+ uint32_t acdb_id;
+ pr_debug("[%s:%s] AUDIO_START\n", __MM_FILE__, __func__);
+ if (arg == 0) {
+ acdb_id = 0;
+ } else {
+ if (copy_from_user(&acdb_id, (void *) arg,
+ sizeof(acdb_id))) {
+ rc = -EFAULT;
+ break;
+ }
+ }
+ if (aac->audio_client) {
+ rc = -EBUSY;
+ pr_err("[%s:%s] active session already existing\n",
+ __MM_FILE__, __func__);
+ break;
+ } else {
+ aac->audio_client = q6audio_open_aac(
+ aac->str_cfg.buffer_size,
+ aac->cfg.sample_rate,
+ aac->cfg.channels,
+ aac->cfg.bit_rate,
+ aac->cfg.stream_format,
+ aac->voicerec_mode.rec_mode, acdb_id);
+
+ if (aac->audio_client < 0) {
+ pr_err("[%s:%s] aac open session failed\n",
+ __MM_FILE__, __func__);
+ rc = -ENOMEM;
+ break;
+ }
+ }
+
+ /*allocate flow control buffers*/
+ fc = aac->aac_fc;
+ size = ((aac->str_cfg.buffer_size < 1543) ? 1543 :
+ aac->str_cfg.buffer_size);
+ for (i = 0; i < AAC_FC_BUFF_CNT; ++i) {
+ mutex_init(&(fc->fc_buff[i].lock));
+ fc->fc_buff[i].empty = 1;
+ fc->fc_buff[i].data = kmalloc(size, GFP_KERNEL);
+ if (fc->fc_buff[i].data == NULL) {
+ pr_err("[%s:%s] No memory for FC buffers\n",
+ __MM_FILE__, __func__);
+ rc = -ENOMEM;
+ goto fc_fail;
+ }
+ fc->fc_buff[i].size = size;
+ fc->fc_buff[i].actual_size = 0;
+ }
+
+ /*create flow control thread*/
+ fc->task = kthread_run(q6_aac_flowcontrol,
+ aac, "aac_flowcontrol");
+ if (IS_ERR(fc->task)) {
+ rc = PTR_ERR(fc->task);
+ pr_err("[%s:%s] error creating flow control thread\n",
+ __MM_FILE__, __func__);
+ goto fc_fail;
+ }
+ break;
+fc_fail:
+ /*free flow control buffers*/
+ --i;
+ for (; i >= 0; i--) {
+ kfree(fc->fc_buff[i].data);
+ fc->fc_buff[i].data = NULL;
+ }
+ break;
+ }
+ case AUDIO_STOP:
+ pr_debug("[%s:%s] AUDIO_STOP\n", __MM_FILE__, __func__);
+ break;
+ case AUDIO_FLUSH:
+ break;
+ case AUDIO_SET_INCALL: {
+ pr_debug("[%s:%s] SET_INCALL\n", __MM_FILE__, __func__);
+ if (copy_from_user(&aac->voicerec_mode,
+ (void *)arg, sizeof(struct msm_voicerec_mode)))
+ rc = -EFAULT;
+
+ if (aac->voicerec_mode.rec_mode != AUDIO_FLAG_READ
+ && aac->voicerec_mode.rec_mode !=
+ AUDIO_FLAG_INCALL_MIXED) {
+ aac->voicerec_mode.rec_mode = AUDIO_FLAG_READ;
+ pr_err("[%s:%s] Invalid rec_mode\n", __MM_FILE__,
+ __func__);
+ rc = -EINVAL;
+ }
+ break;
+ }
+ case AUDIO_GET_STREAM_CONFIG:
+ if (copy_to_user((void *)arg, &aac->str_cfg,
+ sizeof(struct msm_audio_stream_config)))
+ rc = -EFAULT;
+ pr_debug("[%s:%s] GET_STREAM_CONFIG: buffsz=%d, buffcnt=%d\n",
+ __MM_FILE__, __func__, aac->str_cfg.buffer_size,
+ aac->str_cfg.buffer_count);
+ break;
+ case AUDIO_SET_STREAM_CONFIG:
+ if (copy_from_user(&aac->str_cfg, (void *)arg,
+ sizeof(struct msm_audio_stream_config))) {
+ rc = -EFAULT;
+ break;
+ }
+ pr_debug("[%s:%s] SET_STREAM_CONFIG: buffsz=%d, buffcnt=%d\n",
+ __MM_FILE__, __func__, aac->str_cfg.buffer_size,
+ aac->str_cfg.buffer_count);
+ if (aac->str_cfg.buffer_size < 1543) {
+ pr_err("[%s:%s] Buffer size too small\n", __MM_FILE__,
+ __func__);
+ rc = -EINVAL;
+ break;
+ }
+ if (aac->str_cfg.buffer_count != 2)
+ pr_info("[%s:%s] Buffer count set to 2\n", __MM_FILE__,
+ __func__);
+
+ break;
+ case AUDIO_SET_AAC_ENC_CONFIG:
+ if (copy_from_user(&aac->cfg, (void *) arg,
+ sizeof(struct msm_audio_aac_enc_config))) {
+ rc = -EFAULT;
+ }
+ pr_debug("[%s:%s] SET_AAC_ENC_CONFIG: channels=%d, rate=%d\n",
+ __MM_FILE__, __func__, aac->cfg.channels,
+ aac->cfg.sample_rate);
+ if (aac->cfg.channels < 1 || aac->cfg.channels > 2) {
+ pr_err("[%s:%s]invalid number of channels\n",
+ __MM_FILE__, __func__);
+ rc = -EINVAL;
+ }
+ if (aac->cfg.sample_rate != 48000) {
+ pr_err("[%s:%s] only 48KHz is supported\n",
+ __MM_FILE__, __func__);
+ rc = -EINVAL;
+ }
+ if (aac->cfg.stream_format != AUDIO_AAC_FORMAT_RAW &&
+ aac->cfg.stream_format != AUDIO_AAC_FORMAT_ADTS) {
+ pr_err("[%s:%s] unsupported AAC format\n", __MM_FILE__,
+ __func__);
+ rc = -EINVAL;
+ }
+ break;
+ case AUDIO_GET_AAC_ENC_CONFIG:
+ if (copy_to_user((void *) arg, &aac->cfg,
+ sizeof(struct msm_audio_aac_enc_config))) {
+ rc = -EFAULT;
+ }
+ pr_debug("[%s:%s] GET_AAC_ENC_CONFIG: channels=%d, rate=%d\n",
+ __MM_FILE__, __func__, aac->cfg.channels,
+ aac->cfg.sample_rate);
+ break;
+ default:
+ rc = -EINVAL;
+ }
+
+ mutex_unlock(&aac->lock);
+ pr_debug("[%s:%s] rc = %d\n", __MM_FILE__, __func__, rc);
+ return rc;
+}
+
+static int q6_aac_in_open(struct inode *inode, struct file *file)
+{
+
+ struct aac *aac;
+ struct aac_fc *fc;
+ int i;
+ pr_info("[%s:%s] open\n", __MM_FILE__, __func__);
+ aac = kmalloc(sizeof(struct aac), GFP_KERNEL);
+ if (aac == NULL) {
+ pr_err("[%s:%s] Could not allocate memory for aac driver\n",
+ __MM_FILE__, __func__);
+ return -ENOMEM;
+ }
+
+ mutex_init(&aac->lock);
+ file->private_data = aac;
+ aac->audio_client = NULL;
+ aac->str_cfg.buffer_size = 1543;
+ aac->str_cfg.buffer_count = 2;
+ aac->cfg.channels = 1;
+ aac->cfg.bit_rate = 192000;
+ aac->cfg.stream_format = AUDIO_AAC_FORMAT_ADTS;
+ aac->cfg.sample_rate = 48000;
+ aac->voicerec_mode.rec_mode = AUDIO_FLAG_READ;
+
+ aac->aac_fc = kmalloc(sizeof(struct aac_fc), GFP_KERNEL);
+ if (aac->aac_fc == NULL) {
+ pr_err("[%s:%s] Could not allocate memory for aac_fc\n",
+ __MM_FILE__, __func__);
+ kfree(aac);
+ return -ENOMEM;
+ }
+ fc = aac->aac_fc;
+ fc->task = NULL;
+ fc->buff_index = 0;
+ for (i = 0; i < AAC_FC_BUFF_CNT; ++i) {
+ fc->fc_buff[i].data = NULL;
+ fc->fc_buff[i].size = 0;
+ fc->fc_buff[i].actual_size = 0;
+ }
+ /*initialize wait queue head*/
+ init_waitqueue_head(&fc->fc_wq);
+ return 0;
+}
+
+static ssize_t q6_aac_in_read(struct file *file, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio_client *ac;
+ const char __user *start = buf;
+ struct aac *aac = file->private_data;
+ struct aac_fc *fc;
+ int xfer = 0;
+ int res = 0;
+
+ pr_debug("[%s:%s] count = %d\n", __MM_FILE__, __func__, count);
+ mutex_lock(&aac->lock);
+ ac = aac->audio_client;
+
+ if (!ac) {
+ res = -ENODEV;
+ goto fail;
+ }
+ fc = aac->aac_fc;
+
+ /*wait for buffer to full*/
+ if (fc->fc_buff[fc->buff_index].empty != 0) {
+ res = wait_event_interruptible_timeout(fc->fc_wq,
+ (fc->fc_buff[fc->buff_index].empty == 0),
+ msecs_to_jiffies(AAC_READ_TIMEOUT));
+
+ pr_debug("[%s:%s] buff_index = %d\n", __MM_FILE__,
+ __func__, fc->buff_index);
+ if (res == 0) {
+ pr_err("[%s:%s] Timeout!\n", __MM_FILE__, __func__);
+ res = -ETIMEDOUT;
+ goto fail;
+ } else if (res < 0) {
+ pr_err("[%s:%s] Returning on Interrupt\n", __MM_FILE__,
+ __func__);
+ goto fail;
+ }
+ }
+ /*lock the buffer*/
+ mutex_lock(&(fc->fc_buff[fc->buff_index].lock));
+ xfer = fc->fc_buff[fc->buff_index].actual_size;
+
+ if (xfer > count) {
+ mutex_unlock(&(fc->fc_buff[fc->buff_index].lock));
+ pr_err("[%s:%s] read failed! byte count too small\n",
+ __MM_FILE__, __func__);
+ res = -EINVAL;
+ goto fail;
+ }
+
+ if (copy_to_user(buf, fc->fc_buff[fc->buff_index].data, xfer)) {
+ mutex_unlock(&(fc->fc_buff[fc->buff_index].lock));
+ pr_err("[%s:%s] copy_to_user failed at index %d\n",
+ __MM_FILE__, __func__, fc->buff_index);
+ res = -EFAULT;
+ goto fail;
+ }
+
+ buf += xfer;
+
+ fc->fc_buff[fc->buff_index].empty = 1;
+ fc->fc_buff[fc->buff_index].actual_size = 0;
+
+ mutex_unlock(&(fc->fc_buff[fc->buff_index].lock));
+ ++(fc->buff_index);
+ if (fc->buff_index >= AAC_FC_BUFF_CNT)
+ fc->buff_index = 0;
+
+ res = buf - start;
+fail:
+ mutex_unlock(&aac->lock);
+
+ return res;
+}
+
+static int q6_aac_in_release(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+ struct aac *aac = file->private_data;
+ int i = 0;
+ struct aac_fc *fc;
+
+ mutex_lock(&aac->lock);
+ fc = aac->aac_fc;
+ kthread_stop(fc->task);
+ fc->task = NULL;
+
+ /*free flow control buffers*/
+ for (i = 0; i < AAC_FC_BUFF_CNT; ++i) {
+ kfree(fc->fc_buff[i].data);
+ fc->fc_buff[i].data = NULL;
+ }
+ kfree(fc);
+ if (aac->audio_client)
+ rc = q6audio_close(aac->audio_client);
+ mutex_unlock(&aac->lock);
+ kfree(aac);
+ pr_info("[%s:%s] release\n", __MM_FILE__, __func__);
+ return rc;
+}
+
+static const struct file_operations q6_aac_in_fops = {
+ .owner = THIS_MODULE,
+ .open = q6_aac_in_open,
+ .read = q6_aac_in_read,
+ .release = q6_aac_in_release,
+ .unlocked_ioctl = q6_aac_in_ioctl,
+};
+
+struct miscdevice q6_aac_in_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_aac_in",
+ .fops = &q6_aac_in_fops,
+};
+
+static int __init q6_aac_in_init(void)
+{
+ return misc_register(&q6_aac_in_misc);
+}
+
+device_initcall(q6_aac_in_init);
diff --git a/arch/arm/mach-msm/qdsp6/amrnb_in.c b/arch/arm/mach-msm/qdsp6/amrnb_in.c
new file mode 100644
index 0000000..e7756e1
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/amrnb_in.c
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2009 Google, Inc.
+ * Copyright (C) 2009 HTC Corporation
+ * Copyright (c) 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/slab.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+
+#include <linux/msm_audio.h>
+#include <linux/msm_audio_amrnb.h>
+#include <mach/msm_qdsp6_audio.h>
+#include "dal_audio_format.h"
+#include <mach/debug_mm.h>
+
+struct amrnb {
+ struct mutex lock;
+ struct msm_audio_amrnb_enc_config_v2 cfg;
+ struct msm_audio_stream_config str_cfg;
+ struct audio_client *audio_client;
+ struct msm_voicerec_mode voicerec_mode;
+};
+
+
+static long q6_amrnb_in_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct amrnb *amrnb = file->private_data;
+ int rc = 0;
+
+ mutex_lock(&amrnb->lock);
+ switch (cmd) {
+ case AUDIO_SET_VOLUME:
+ pr_debug("[%s:%s] SET_VOLUME\n", __MM_FILE__, __func__);
+ break;
+ case AUDIO_GET_STATS:
+ {
+ struct msm_audio_stats stats;
+ pr_debug("[%s:%s] GET_STATS\n", __MM_FILE__, __func__);
+ memset(&stats, 0, sizeof(stats));
+ if (copy_to_user((void *) arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ }
+ case AUDIO_START:
+ {
+ uint32_t acdb_id;
+ pr_debug("[%s:%s] AUDIO_START\n", __MM_FILE__, __func__);
+ if (arg == 0) {
+ acdb_id = 0;
+ } else {
+ if (copy_from_user(&acdb_id, (void *) arg,
+ sizeof(acdb_id))) {
+ rc = -EFAULT;
+ break;
+ }
+ }
+ if (amrnb->audio_client) {
+ rc = -EBUSY;
+ pr_err("[%s:%s] active session already existing\n",
+ __MM_FILE__, __func__);
+ break;
+ } else {
+ amrnb->audio_client = q6audio_open_amrnb(
+ amrnb->str_cfg.buffer_size,
+ amrnb->cfg.band_mode,
+ amrnb->cfg.dtx_enable,
+ amrnb->voicerec_mode.rec_mode,
+ acdb_id);
+ if (!amrnb->audio_client) {
+ pr_err("[%s:%s] amrnb open session failed\n",
+ __MM_FILE__, __func__);
+ kfree(amrnb);
+ rc = -ENOMEM;
+ break;
+ }
+ }
+ break;
+ }
+ case AUDIO_STOP:
+ pr_debug("[%s:%s] AUDIO_STOP\n", __MM_FILE__, __func__);
+ break;
+ case AUDIO_FLUSH:
+ break;
+ case AUDIO_SET_INCALL: {
+ pr_debug("[%s:%s] SET_INCALL\n", __MM_FILE__, __func__);
+ if (copy_from_user(&amrnb->voicerec_mode,
+ (void *)arg, sizeof(struct msm_voicerec_mode)))
+ rc = -EFAULT;
+
+ if (amrnb->voicerec_mode.rec_mode != AUDIO_FLAG_READ
+ && amrnb->voicerec_mode.rec_mode !=
+ AUDIO_FLAG_INCALL_MIXED) {
+ amrnb->voicerec_mode.rec_mode = AUDIO_FLAG_READ;
+ pr_err("[%s:%s] Invalid rec_mode\n", __MM_FILE__,
+ __func__);
+ rc = -EINVAL;
+ }
+ break;
+ }
+ case AUDIO_GET_STREAM_CONFIG:
+ if (copy_to_user((void *)arg, &amrnb->str_cfg,
+ sizeof(struct msm_audio_stream_config)))
+ rc = -EFAULT;
+ pr_debug("[%s:%s] GET_STREAM_CONFIG: buffsz=%d, buffcnt = %d\n",
+ __MM_FILE__, __func__, amrnb->str_cfg.buffer_size,
+ amrnb->str_cfg.buffer_count);
+ break;
+ case AUDIO_SET_STREAM_CONFIG:
+ if (copy_from_user(&amrnb->str_cfg, (void *)arg,
+ sizeof(struct msm_audio_stream_config))) {
+ rc = -EFAULT;
+ break;
+ }
+ pr_debug("[%s:%s] SET_STREAM_CONFIG: buffsz=%d, buffcnt = %d\n",
+ __MM_FILE__, __func__, amrnb->str_cfg.buffer_size,
+ amrnb->str_cfg.buffer_count);
+
+ if (amrnb->str_cfg.buffer_size < 768) {
+ pr_err("[%s:%s] Buffer size too small\n", __MM_FILE__,
+ __func__);
+ rc = -EINVAL;
+ break;
+ }
+
+ if (amrnb->str_cfg.buffer_count != 2)
+ pr_info("[%s:%s] Buffer count set to 2\n", __MM_FILE__,
+ __func__);
+ break;
+ case AUDIO_SET_AMRNB_ENC_CONFIG:
+ if (copy_from_user(&amrnb->cfg, (void *) arg,
+ sizeof(struct msm_audio_amrnb_enc_config_v2)))
+ rc = -EFAULT;
+ pr_debug("[%s:%s] SET_AMRNB_ENC_CONFIG\n", __MM_FILE__,
+ __func__);
+ break;
+ case AUDIO_GET_AMRNB_ENC_CONFIG:
+ if (copy_to_user((void *) arg, &amrnb->cfg,
+ sizeof(struct msm_audio_amrnb_enc_config_v2)))
+ rc = -EFAULT;
+ pr_debug("[%s:%s] GET_AMRNB_ENC_CONFIG\n", __MM_FILE__,
+ __func__);
+ break;
+
+ default:
+ rc = -EINVAL;
+ }
+
+ mutex_unlock(&amrnb->lock);
+ pr_debug("[%s:%s] rc= %d\n", __MM_FILE__, __func__, rc);
+ return rc;
+}
+
+static int q6_amrnb_in_open(struct inode *inode, struct file *file)
+{
+ struct amrnb *amrnb;
+
+ pr_info("[%s:%s] open\n", __MM_FILE__, __func__);
+ amrnb = kmalloc(sizeof(struct amrnb), GFP_KERNEL);
+ if (amrnb == NULL) {
+ pr_err("[%s:%s] Could not allocate memory for amrnb driver\n",
+ __MM_FILE__, __func__);
+ return -ENOMEM;
+ }
+
+ mutex_init(&amrnb->lock);
+ file->private_data = amrnb;
+ amrnb->audio_client = NULL;
+ amrnb->str_cfg.buffer_size = 768;
+ amrnb->str_cfg.buffer_count = 2;
+ amrnb->cfg.band_mode = 7;
+ amrnb->cfg.dtx_enable = 3;
+ amrnb->cfg.frame_format = ADSP_AUDIO_FORMAT_AMRNB_FS;
+ amrnb->voicerec_mode.rec_mode = AUDIO_FLAG_READ;
+
+ return 0;
+}
+
+static ssize_t q6_amrnb_in_read(struct file *file, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio_client *ac;
+ struct audio_buffer *ab;
+ const char __user *start = buf;
+ struct amrnb *amrnb = file->private_data;
+ int xfer = 0;
+ int res;
+
+ pr_debug("[%s:%s] count = %d\n", __MM_FILE__, __func__, count);
+ mutex_lock(&amrnb->lock);
+ ac = amrnb->audio_client;
+ if (!ac) {
+ res = -ENODEV;
+ goto fail;
+ }
+ while (count > xfer) {
+ ab = ac->buf + ac->cpu_buf;
+
+ if (ab->used)
+ wait_event(ac->wait, (ab->used == 0));
+
+ pr_debug("[%s:%s] ab->data = %p, cpu_buf = %d\n", __MM_FILE__,
+ __func__, ab->data, ac->cpu_buf);
+ xfer = ab->actual_size;
+
+ if (copy_to_user(buf, ab->data, xfer)) {
+ pr_err("[%s:%s] copy_to_user failed\n",
+ __MM_FILE__, __func__);
+ res = -EFAULT;
+ goto fail;
+ }
+
+ buf += xfer;
+ count -= xfer;
+
+ ab->used = 1;
+ q6audio_read(ac, ab);
+ ac->cpu_buf ^= 1;
+ }
+
+ res = buf - start;
+fail:
+ mutex_unlock(&amrnb->lock);
+
+ return res;
+}
+
+static int q6_amrnb_in_release(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+ struct amrnb *amrnb = file->private_data;
+
+ mutex_lock(&amrnb->lock);
+ if (amrnb->audio_client)
+ rc = q6audio_close(amrnb->audio_client);
+ mutex_unlock(&amrnb->lock);
+ kfree(amrnb);
+ pr_info("[%s:%s] release\n", __MM_FILE__, __func__);
+ return rc;
+}
+
+static const struct file_operations q6_amrnb_in_fops = {
+ .owner = THIS_MODULE,
+ .open = q6_amrnb_in_open,
+ .read = q6_amrnb_in_read,
+ .release = q6_amrnb_in_release,
+ .unlocked_ioctl = q6_amrnb_in_ioctl,
+};
+
+struct miscdevice q6_amrnb_in_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_amr_in",
+ .fops = &q6_amrnb_in_fops,
+};
+
+static int __init q6_amrnb_in_init(void)
+{
+ return misc_register(&q6_amrnb_in_misc);
+}
+
+device_initcall(q6_amrnb_in_init);
diff --git a/arch/arm/mach-msm/qdsp6/analog_audio.c b/arch/arm/mach-msm/qdsp6/analog_audio.c
new file mode 100644
index 0000000..688f57e
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/analog_audio.c
@@ -0,0 +1,94 @@
+/* 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/init.h>
+#include <linux/wait.h>
+#include <linux/gpio.h>
+#include <mach/pmic.h>
+#include <mach/msm_qdsp6_audio.h>
+#include <asm/string.h>
+#include <asm/mach-types.h>
+#include <mach/debug_mm.h>
+
+#define GPIO_HEADSET_AMP 157
+#define GPIO_SPEAKER_AMP 39
+#define GPIO_HEADSET_SHDN_N 48
+
+void analog_init(void)
+{
+ /* stereo pmic init */
+ pmic_spkr_set_gain(LEFT_SPKR, SPKR_GAIN_PLUS12DB);
+ pmic_spkr_set_gain(RIGHT_SPKR, SPKR_GAIN_PLUS12DB);
+ pmic_mic_set_volt(MIC_VOLT_1_80V);
+ gpio_direction_output(GPIO_HEADSET_AMP, 1);
+ gpio_set_value(GPIO_HEADSET_AMP, 0);
+}
+
+void analog_headset_enable(int en)
+{
+ pr_debug("[%s:%s] en = %d\n", __MM_FILE__, __func__, en);
+ /* enable audio amp */
+ gpio_set_value(GPIO_HEADSET_AMP, !!en);
+}
+
+void analog_speaker_enable(int en)
+{
+ struct spkr_config_mode scm;
+ memset(&scm, 0, sizeof(scm));
+
+ pr_debug("[%s:%s] en = %d\n", __MM_FILE__, __func__, en);
+ if (en) {
+ scm.is_right_chan_en = 1;
+ scm.is_left_chan_en = 1;
+ scm.is_stereo_en = 1;
+ scm.is_hpf_en = 1;
+ pmic_spkr_en_mute(LEFT_SPKR, 0);
+ pmic_spkr_en_mute(RIGHT_SPKR, 0);
+ pmic_set_spkr_configuration(&scm);
+ pmic_spkr_en(LEFT_SPKR, 1);
+ pmic_spkr_en(RIGHT_SPKR, 1);
+
+ /* unmute */
+ pmic_spkr_en_mute(LEFT_SPKR, 1);
+ pmic_spkr_en_mute(RIGHT_SPKR, 1);
+ } else {
+ pmic_spkr_en_mute(LEFT_SPKR, 0);
+ pmic_spkr_en_mute(RIGHT_SPKR, 0);
+
+ pmic_spkr_en(LEFT_SPKR, 0);
+ pmic_spkr_en(RIGHT_SPKR, 0);
+
+ pmic_set_spkr_configuration(&scm);
+ }
+}
+
+void analog_mic_enable(int en)
+{
+ pr_debug("[%s:%s] en = %d\n", __MM_FILE__, __func__, en);
+ pmic_mic_en(en);
+}
+
+static struct q6audio_analog_ops ops = {
+ .init = analog_init,
+ .speaker_enable = analog_speaker_enable,
+ .headset_enable = analog_headset_enable,
+ .int_mic_enable = analog_mic_enable,
+ .ext_mic_enable = analog_mic_enable,
+};
+
+static int __init init(void)
+{
+ q6audio_register_analog_ops(&ops);
+ return 0;
+}
+
+device_initcall(init);
diff --git a/arch/arm/mach-msm/qdsp6/audio_ctl.c b/arch/arm/mach-msm/qdsp6/audio_ctl.c
new file mode 100644
index 0000000..ab1df39
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/audio_ctl.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2009 Google, Inc.
+ * Copyright (C) 2009 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.
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/msm_audio.h>
+
+#include <mach/msm_qdsp6_audio.h>
+#include <mach/debug_mm.h>
+
+#define BUFSZ (0)
+
+static DEFINE_MUTEX(voice_lock);
+static int voice_started;
+
+static struct audio_client *voc_tx_clnt;
+static struct audio_client *voc_rx_clnt;
+
+static int q6_voice_start(void)
+{
+ int rc = 0;
+
+ mutex_lock(&voice_lock);
+
+ if (voice_started) {
+ pr_err("[%s:%s] busy\n", __MM_FILE__, __func__);
+ rc = -EBUSY;
+ goto done;
+ }
+
+ voc_tx_clnt = q6voice_open(AUDIO_FLAG_WRITE);
+ if (!voc_tx_clnt) {
+ pr_err("[%s:%s] open voice tx failed.\n", __MM_FILE__,
+ __func__);
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ voc_rx_clnt = q6voice_open(AUDIO_FLAG_READ);
+ if (!voc_rx_clnt) {
+ pr_err("[%s:%s] open voice rx failed.\n", __MM_FILE__,
+ __func__);
+ q6voice_close(voc_tx_clnt);
+ rc = -ENOMEM;
+ }
+
+ voice_started = 1;
+done:
+ mutex_unlock(&voice_lock);
+ return rc;
+}
+
+static int q6_voice_stop(void)
+{
+ mutex_lock(&voice_lock);
+ if (voice_started) {
+ q6voice_close(voc_tx_clnt);
+ q6voice_close(voc_rx_clnt);
+ voice_started = 0;
+ }
+ mutex_unlock(&voice_lock);
+ return 0;
+}
+
+static int q6_open(struct inode *inode, struct file *file)
+{
+ pr_debug("[%s:%s]\n", __MM_FILE__, __func__);
+ return 0;
+}
+
+static long q6_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int rc;
+ uint32_t n;
+ uint32_t id[2];
+ uint32_t mute_status;
+
+ switch (cmd) {
+ case AUDIO_SWITCH_DEVICE:
+ rc = copy_from_user(&id, (void *)arg, sizeof(id));
+ pr_info("[%s:%s] SWITCH_DEV: id[0] = 0x%x, id[1] = 0x%x",
+ __MM_FILE__, __func__, id[0], id[1]);
+ if (!rc)
+ rc = q6audio_do_routing(id[0], id[1]);
+ break;
+ case AUDIO_SET_VOLUME:
+ rc = copy_from_user(&n, (void *)arg, sizeof(n));
+ pr_debug("[%s:%s] SET_VOLUME: vol = %d\n", __MM_FILE__,
+ __func__, n);
+ if (!rc)
+ rc = q6audio_set_rx_volume(n);
+ break;
+ case AUDIO_SET_MUTE:
+ rc = copy_from_user(&n, (void *)arg, sizeof(n));
+ if (!rc) {
+ if (voice_started) {
+ if (n == 1)
+ mute_status = STREAM_MUTE;
+ else
+ mute_status = STREAM_UNMUTE;
+ } else {
+ if (n == 1)
+ mute_status = DEVICE_MUTE;
+ else
+ mute_status = DEVICE_UNMUTE;
+ }
+
+ pr_debug("[%s:%s] SET_MUTE: mute_status = %d\n",
+ __MM_FILE__, __func__, mute_status);
+ rc = q6audio_set_tx_mute(mute_status);
+ }
+ break;
+ case AUDIO_UPDATE_ACDB:
+ rc = copy_from_user(&id, (void *)arg, sizeof(id));
+ pr_debug("[%s:%s] UPDATE_ACDB: id[0] = 0x%x, id[1] = 0x%x\n",
+ __MM_FILE__, __func__, id[0], id[1]);
+ if (!rc)
+ rc = q6audio_update_acdb(id[0], 0);
+ break;
+ case AUDIO_START_VOICE:
+ pr_debug("[%s:%s] START_VOICE\n", __MM_FILE__, __func__);
+ rc = q6_voice_start();
+ break;
+ case AUDIO_STOP_VOICE:
+ pr_debug("[%s:%s] STOP_VOICE\n", __MM_FILE__, __func__);
+ rc = q6_voice_stop();
+ break;
+ case AUDIO_REINIT_ACDB:
+ pr_debug("[%s:%s] REINIT_ACDB\n", __MM_FILE__, __func__);
+ rc = 0;
+ break;
+ default:
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+
+static int q6_release(struct inode *inode, struct file *file)
+{
+ pr_debug("[%s:%s]\n", __MM_FILE__, __func__);
+ return 0;
+}
+
+static struct file_operations q6_dev_fops = {
+ .owner = THIS_MODULE,
+ .open = q6_open,
+ .unlocked_ioctl = q6_ioctl,
+ .release = q6_release,
+};
+
+struct miscdevice q6_control_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_audio_ctl",
+ .fops = &q6_dev_fops,
+};
+
+
+static int __init q6_audio_ctl_init(void) {
+ return misc_register(&q6_control_device);
+}
+
+device_initcall(q6_audio_ctl_init);
diff --git a/arch/arm/mach-msm/qdsp6/audiov2/Makefile b/arch/arm/mach-msm/qdsp6/audiov2/Makefile
new file mode 100644
index 0000000..86ab9ae
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/audiov2/Makefile
@@ -0,0 +1,12 @@
+obj-y += q6audio.o
+obj-y += aac_in.o
+obj-y += voice.o
+obj-y += pcm_out.o
+obj-y += pcm_in.o
+obj-y += mp3.o
+obj-y += audio_ctl.o
+obj-y += analog_audio.o
+obj-y += routing.o
+obj-y += evrc_in.o
+obj-y += qcelp_in.o
+obj-y += amrnb_in.o
diff --git a/arch/arm/mach-msm/qdsp6/audiov2/aac_in.c b/arch/arm/mach-msm/qdsp6/audiov2/aac_in.c
new file mode 100644
index 0000000..fe6c049
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/audiov2/aac_in.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2009 Google, Inc.
+ * Copyright (C) 2009 HTC Corporation
+ * Copyright (c) 2009, 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/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+
+#include <linux/msm_audio_aac.h>
+
+#include <mach/msm_qdsp6_audiov2.h>
+#include "dal_audio.h"
+#include "dal_audio_format.h"
+
+struct aac {
+ struct mutex lock;
+ struct msm_audio_aac_enc_config cfg;
+ struct msm_audio_stream_config str_cfg;
+ struct audio_client *audio_client;
+};
+
+static long q6_aac_in_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct aac *aac = file->private_data;
+ struct adsp_open_command rpc;
+
+ int sample_rate;
+ int audio_object_type;
+ int index = sizeof(u32);
+ int rc = 0;
+ u32 *aac_type = NULL;
+
+
+ mutex_lock(&aac->lock);
+ switch (cmd) {
+
+ case AUDIO_START:
+ if (aac->audio_client) {
+ rc = -EBUSY;
+ break;
+ } else {
+ tx_clk_freq = 48000;
+ aac->audio_client = q6audio_open(AUDIO_FLAG_READ,
+ aac->str_cfg.buffer_size);
+
+ if (aac->audio_client < 0) {
+
+ tx_clk_freq = 8000;
+ rc = -ENOMEM;
+ break;
+ }
+ }
+ memset(&rpc, 0, sizeof(rpc));
+
+ rpc.format_block.binary.format = ADSP_AUDIO_FORMAT_MPEG4_AAC;
+ /* only 48k sample rate is supported */
+ sample_rate = 3;
+
+ /* AAC OBJECT LC */
+ audio_object_type = 2;
+
+ aac_type = (u32 *)rpc.format_block.binary.data;
+ switch (aac->cfg.stream_format) {
+
+ case AUDIO_AAC_FORMAT_ADTS:
+ /* AAC Encoder expect MPEG4_ADTS media type */
+ *aac_type = ADSP_AUDIO_AAC_MPEG4_ADTS;
+ break;
+ case AUDIO_AAC_FORMAT_RAW:
+ /* for ADIF recording */
+ *aac_type = ADSP_AUDIO_AAC_RAW;
+ break;
+ }
+
+ rpc.format_block.binary.data[index++] = (u8)(
+ ((audio_object_type & 0x1F) << 3) |
+ ((sample_rate >> 1) & 0x7));
+ rpc.format_block.binary.data[index] = (u8)(
+ ((sample_rate & 0x1) << 7) |
+ ((aac->cfg.channels & 0x7) << 3));
+
+ rpc.format_block.binary.num_bytes = index + 1;
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_READ;
+ rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT;
+ rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD;
+ rpc.buf_max_size = aac->str_cfg.buffer_size;
+ rpc.config.aac.bit_rate = aac->cfg.bit_rate;
+ rpc.config.aac.encoder_mode = ADSP_AUDIO_ENC_AAC_LC_ONLY_MODE;
+ q6audio_start(aac->audio_client, &rpc, sizeof(rpc));
+ break;
+ case AUDIO_STOP:
+ break;
+ case AUDIO_FLUSH:
+ break;
+ case AUDIO_SET_VOLUME:
+ break;
+ case AUDIO_GET_STREAM_CONFIG:
+ if (copy_to_user((void *)arg, &aac->str_cfg,
+ sizeof(struct msm_audio_stream_config)))
+ rc = -EFAULT;
+ break;
+ case AUDIO_SET_STREAM_CONFIG:
+ if (copy_from_user(&aac->str_cfg, (void *)arg,
+ sizeof(struct msm_audio_stream_config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (aac->str_cfg.buffer_size < 519) {
+ pr_err("Buffer size too small\n");
+ rc = -EINVAL;
+ break;
+ }
+ if (aac->str_cfg.buffer_count != 2)
+ pr_info("Buffer count set to 2\n");
+
+ break;
+ case AUDIO_SET_AAC_ENC_CONFIG:
+ if (copy_from_user(&aac->cfg, (void *) arg,
+ sizeof(struct msm_audio_aac_enc_config))) {
+ rc = -EFAULT;
+ }
+ if (aac->cfg.channels != 1) {
+ pr_err("only mono is supported\n");
+ rc = -EINVAL;
+ }
+ if (aac->cfg.sample_rate != 48000) {
+ pr_err("only 48KHz is supported\n");
+ rc = -EINVAL;
+ }
+ if (aac->cfg.stream_format != AUDIO_AAC_FORMAT_RAW &&
+ aac->cfg.stream_format != AUDIO_AAC_FORMAT_ADTS) {
+ pr_err("unsupported AAC format\n");
+ rc = -EINVAL;
+ }
+ break;
+ case AUDIO_GET_AAC_ENC_CONFIG:
+ if (copy_to_user((void *) arg, &aac->cfg,
+ sizeof(struct msm_audio_aac_enc_config))) {
+ rc = -EFAULT;
+ }
+ break;
+ default:
+ rc = -EINVAL;
+ }
+
+ mutex_unlock(&aac->lock);
+ return rc;
+}
+
+static int q6_aac_in_open(struct inode *inode, struct file *file)
+{
+
+ struct aac *aac;
+ aac = kmalloc(sizeof(struct aac), GFP_KERNEL);
+ if (aac == NULL) {
+ pr_err("Could not allocate memory for aac driver\n");
+ return -ENOMEM;
+ }
+
+ mutex_init(&aac->lock);
+ file->private_data = aac;
+ aac->audio_client = NULL;
+ aac->str_cfg.buffer_size = 519;
+ aac->str_cfg.buffer_count = 2;
+ aac->cfg.channels = 1;
+ aac->cfg.bit_rate = 192000;
+ aac->cfg.stream_format = AUDIO_AAC_FORMAT_ADTS;
+ aac->cfg.sample_rate = 48000;
+
+ return 0;
+}
+
+static ssize_t q6_aac_in_read(struct file *file, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio_client *ac;
+ struct audio_buffer *ab;
+ const char __user *start = buf;
+ struct aac *aac = file->private_data;
+ int xfer = 0;
+ int res;
+
+ mutex_lock(&aac->lock);
+ ac = aac->audio_client;
+ if (!ac) {
+ res = -ENODEV;
+ goto fail;
+ }
+ while (count > xfer) {
+ ab = ac->buf + ac->cpu_buf;
+
+ if (ab->used)
+ wait_event(ac->wait, (ab->used == 0));
+
+ xfer = ab->actual_size;
+
+ if (copy_to_user(buf, ab->data, xfer)) {
+ res = -EFAULT;
+ goto fail;
+ }
+
+ buf += xfer;
+ count -= xfer;
+
+ ab->used = 1;
+ q6audio_read(ac, ab);
+ ac->cpu_buf ^= 1;
+ }
+ res = buf - start;
+fail:
+ mutex_unlock(&aac->lock);
+
+ return res;
+}
+
+static int q6_aac_in_release(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+ struct aac *aac = file->private_data;
+
+ mutex_lock(&aac->lock);
+ if (aac->audio_client)
+ rc = q6audio_close(aac->audio_client);
+ mutex_unlock(&aac->lock);
+ kfree(aac);
+ tx_clk_freq = 8000;
+ return rc;
+}
+
+static const struct file_operations q6_aac_in_fops = {
+ .owner = THIS_MODULE,
+ .open = q6_aac_in_open,
+ .read = q6_aac_in_read,
+ .release = q6_aac_in_release,
+ .unlocked_ioctl = q6_aac_in_ioctl,
+};
+
+struct miscdevice q6_aac_in_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_aac_in",
+ .fops = &q6_aac_in_fops,
+};
+
+static int __init q6_aac_in_init(void)
+{
+ return misc_register(&q6_aac_in_misc);
+}
+
+device_initcall(q6_aac_in_init);
diff --git a/arch/arm/mach-msm/qdsp6/audiov2/amrnb_in.c b/arch/arm/mach-msm/qdsp6/audiov2/amrnb_in.c
new file mode 100644
index 0000000..b877977
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/audiov2/amrnb_in.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2009 Google, Inc.
+ * Copyright (C) 2009 HTC Corporation
+ * Copyright (c) 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/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+
+#include <linux/msm_audio_amrnb.h>
+#include <mach/msm_qdsp6_audiov2.h>
+#include "dal_audio.h"
+#include "dal_audio_format.h"
+#include <mach/debug_mm.h>
+
+
+struct amrnb {
+ struct mutex lock;
+ struct msm_audio_amrnb_enc_config_v2 cfg;
+ struct msm_audio_stream_config str_cfg;
+ struct audio_client *audio_client;
+};
+
+
+static long q6_amrnb_in_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct amrnb *amrnb = file->private_data;
+ struct adsp_open_command rpc;
+ int rc = 0;
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ memset(&stats, 0, sizeof(stats));
+ if (copy_to_user((void *) arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ }
+
+ mutex_lock(&amrnb->lock);
+ switch (cmd) {
+ case AUDIO_START:
+ if (amrnb->audio_client) {
+ rc = -EBUSY;
+ break;
+ } else {
+ amrnb->audio_client = q6audio_open(AUDIO_FLAG_READ,
+ amrnb->str_cfg.buffer_size);
+
+ if (!amrnb->audio_client) {
+ kfree(amrnb);
+ rc = -ENOMEM;
+ break;
+ }
+ }
+
+ tx_clk_freq = 8000;
+
+ memset(&rpc, 0, sizeof(rpc));
+
+ rpc.format_block.standard.format = ADSP_AUDIO_FORMAT_AMRNB_FS;
+ rpc.format_block.standard.channels = 1;
+ rpc.format_block.standard.bits_per_sample = 16;
+ rpc.format_block.standard.sampling_rate = 8000;
+ rpc.format_block.standard.is_signed = 1;
+ rpc.format_block.standard.is_interleaved = 0;
+
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_READ;
+ rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT;
+ rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD;
+ rpc.buf_max_size = amrnb->str_cfg.buffer_size;
+ rpc.config.amr.mode = amrnb->cfg.band_mode;
+ rpc.config.amr.dtx_mode = amrnb->cfg.dtx_enable;
+ rpc.config.amr.enable = 1;
+ q6audio_start(amrnb->audio_client, &rpc, sizeof(rpc));
+ break;
+ case AUDIO_STOP:
+ break;
+ case AUDIO_FLUSH:
+ break;
+ case AUDIO_SET_VOLUME:
+ break;
+ case AUDIO_GET_STREAM_CONFIG:
+ if (copy_to_user((void *)arg, &amrnb->str_cfg,
+ sizeof(struct msm_audio_stream_config)))
+ rc = -EFAULT;
+ break;
+ case AUDIO_SET_STREAM_CONFIG:
+ if (copy_from_user(&amrnb->str_cfg, (void *)arg,
+ sizeof(struct msm_audio_stream_config))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ if (amrnb->str_cfg.buffer_size < 768) {
+ pr_err("[%s:%s] Buffer size too small\n", __MM_FILE__,
+ __func__);
+ rc = -EINVAL;
+ break;
+ }
+
+ if (amrnb->str_cfg.buffer_count != 2)
+ pr_info("[%s:%s] Buffer count set to 2\n", __MM_FILE__,
+ __func__);
+ break;
+ case AUDIO_SET_AMRNB_ENC_CONFIG:
+ if (copy_from_user(&amrnb->cfg, (void *) arg,
+ sizeof(struct msm_audio_amrnb_enc_config_v2)))
+ rc = -EFAULT;
+ break;
+ case AUDIO_GET_AMRNB_ENC_CONFIG:
+ if (copy_to_user((void *) arg, &amrnb->cfg,
+ sizeof(struct msm_audio_amrnb_enc_config_v2)))
+ rc = -EFAULT;
+ break;
+
+ default:
+ rc = -EINVAL;
+ }
+
+ mutex_unlock(&amrnb->lock);
+ return rc;
+}
+
+static int q6_amrnb_in_open(struct inode *inode, struct file *file)
+{
+ struct amrnb *amrnb;
+ amrnb = kmalloc(sizeof(struct amrnb), GFP_KERNEL);
+ if (amrnb == NULL) {
+ pr_err("[%s:%s] Could not allocate memory for amrnb driver\n",
+ __MM_FILE__, __func__);
+ return -ENOMEM;
+ }
+
+ mutex_init(&amrnb->lock);
+ file->private_data = amrnb;
+ amrnb->audio_client = NULL;
+ amrnb->str_cfg.buffer_size = 768;
+ amrnb->str_cfg.buffer_count = 2;
+ amrnb->cfg.band_mode = ADSP_AUDIO_AMR_MR475;
+ amrnb->cfg.dtx_enable = ADSP_AUDIO_AMR_DTX_MODE_ON_AUTO;
+ amrnb->cfg.frame_format = ADSP_AUDIO_FORMAT_AMRNB_FS;
+ return 0;
+}
+
+static ssize_t q6_amrnb_in_read(struct file *file, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio_client *ac;
+ struct audio_buffer *ab;
+ const char __user *start = buf;
+ struct amrnb *amrnb = file->private_data;
+ int xfer = 0;
+ int res;
+
+ mutex_lock(&amrnb->lock);
+ ac = amrnb->audio_client;
+ if (!ac) {
+ res = -ENODEV;
+ goto fail;
+ }
+ while (count > xfer) {
+ ab = ac->buf + ac->cpu_buf;
+
+ if (ab->used)
+ wait_event(ac->wait, (ab->used == 0));
+
+ xfer = ab->actual_size;
+
+ if (copy_to_user(buf, ab->data, xfer)) {
+ res = -EFAULT;
+ goto fail;
+ }
+
+ buf += xfer;
+ count -= xfer;
+
+ ab->used = 1;
+ q6audio_read(ac, ab);
+ ac->cpu_buf ^= 1;
+ }
+
+ res = buf - start;
+fail:
+ mutex_unlock(&amrnb->lock);
+
+ return res;
+}
+
+static int q6_amrnb_in_release(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+ struct amrnb *amrnb = file->private_data;
+
+ mutex_lock(&amrnb->lock);
+ if (amrnb->audio_client)
+ rc = q6audio_close(amrnb->audio_client);
+ mutex_unlock(&amrnb->lock);
+ kfree(amrnb);
+ return rc;
+}
+
+static const struct file_operations q6_amrnb_in_fops = {
+ .owner = THIS_MODULE,
+ .open = q6_amrnb_in_open,
+ .read = q6_amrnb_in_read,
+ .release = q6_amrnb_in_release,
+ .unlocked_ioctl = q6_amrnb_in_ioctl,
+};
+
+struct miscdevice q6_amrnb_in_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_amr_in",
+ .fops = &q6_amrnb_in_fops,
+};
+
+static int __init q6_amrnb_in_init(void)
+{
+ return misc_register(&q6_amrnb_in_misc);
+}
+
+device_initcall(q6_amrnb_in_init);
diff --git a/arch/arm/mach-msm/qdsp6/audiov2/analog_audio.c b/arch/arm/mach-msm/qdsp6/audiov2/analog_audio.c
new file mode 100644
index 0000000..1df4f5d
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/audiov2/analog_audio.c
@@ -0,0 +1,85 @@
+/* Copyright (c) 2009, 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/gpio.h>
+#include <mach/pmic.h>
+#include <mach/msm_qdsp6_audiov2.h>
+
+#define GPIO_HEADSET_AMP 157
+
+void analog_init(void)
+{
+ /* stereo pmic init */
+ pmic_spkr_set_gain(LEFT_SPKR, SPKR_GAIN_PLUS12DB);
+ pmic_spkr_set_gain(RIGHT_SPKR, SPKR_GAIN_PLUS12DB);
+ pmic_mic_set_volt(MIC_VOLT_1_80V);
+
+ gpio_direction_output(GPIO_HEADSET_AMP, 1);
+ gpio_set_value(GPIO_HEADSET_AMP, 0);
+}
+
+void analog_headset_enable(int en)
+{
+ /* enable audio amp */
+ gpio_set_value(GPIO_HEADSET_AMP, !!en);
+}
+
+void analog_speaker_enable(int en)
+{
+ struct spkr_config_mode scm;
+ memset(&scm, 0, sizeof(scm));
+
+ if (en) {
+ scm.is_right_chan_en = 1;
+ scm.is_left_chan_en = 1;
+ scm.is_stereo_en = 1;
+ scm.is_hpf_en = 1;
+ pmic_spkr_en_mute(LEFT_SPKR, 0);
+ pmic_spkr_en_mute(RIGHT_SPKR, 0);
+ pmic_set_spkr_configuration(&scm);
+ pmic_spkr_en(LEFT_SPKR, 1);
+ pmic_spkr_en(RIGHT_SPKR, 1);
+
+ /* unmute */
+ pmic_spkr_en_mute(LEFT_SPKR, 1);
+ pmic_spkr_en_mute(RIGHT_SPKR, 1);
+ } else {
+ pmic_spkr_en_mute(LEFT_SPKR, 0);
+ pmic_spkr_en_mute(RIGHT_SPKR, 0);
+
+ pmic_spkr_en(LEFT_SPKR, 0);
+ pmic_spkr_en(RIGHT_SPKR, 0);
+
+ pmic_set_spkr_configuration(&scm);
+ }
+}
+
+void analog_mic_enable(int en)
+{
+ pmic_mic_en(en);
+}
+
+static struct q6audio_analog_ops ops = {
+ .init = analog_init,
+ .speaker_enable = analog_speaker_enable,
+ .headset_enable = analog_headset_enable,
+ .int_mic_enable = analog_mic_enable,
+};
+
+static int __init init(void)
+{
+ q6audio_register_analog_ops(&ops);
+ return 0;
+}
+
+device_initcall(init);
diff --git a/arch/arm/mach-msm/qdsp6/audiov2/audio_ctl.c b/arch/arm/mach-msm/qdsp6/audiov2/audio_ctl.c
new file mode 100644
index 0000000..286d85d
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/audiov2/audio_ctl.c
@@ -0,0 +1,140 @@
+/* arch/arm/mach-msm/qdsp6/audiov2/audio_ctrl.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Copyright (C) 2009 HTC Corporation
+ * Copyright (c) 2009, 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/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/msm_audio.h>
+
+#include <mach/msm_qdsp6_audiov2.h>
+
+#define BUFSZ (0)
+
+static DEFINE_MUTEX(voice_lock);
+static int voice_started;
+
+static struct audio_client *voc_clnt;
+
+static int q6_voice_start(void)
+{
+ int rc = 0;
+
+ mutex_lock(&voice_lock);
+
+ if (voice_started) {
+ pr_err("voice: busy\n");
+ rc = -EBUSY;
+ goto done;
+ }
+
+ voc_clnt = q6voice_open();
+ if (!voc_clnt) {
+ pr_err("voice: open voice failed.\n");
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ voice_started = 1;
+done:
+ mutex_unlock(&voice_lock);
+ return rc;
+}
+
+static int q6_voice_stop(void)
+{
+ mutex_lock(&voice_lock);
+ if (voice_started) {
+ q6voice_close(voc_clnt);
+ voice_started = 0;
+ }
+ mutex_unlock(&voice_lock);
+ return 0;
+}
+
+static int q6_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static int q6_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int rc;
+ uint32_t n;
+ uint32_t id[2];
+
+ switch (cmd) {
+ case AUDIO_SWITCH_DEVICE:
+ rc = copy_from_user(&n, (void *)arg, sizeof(n));
+ if (!rc)
+ rc = q6audio_do_routing(n);
+ break;
+ case AUDIO_SET_VOLUME:
+ rc = copy_from_user(&n, (void *)arg, sizeof(n));
+ if (!rc)
+ rc = q6audio_set_rx_volume(n);
+ break;
+ case AUDIO_SET_MUTE:
+ rc = copy_from_user(&n, (void *)arg, sizeof(n));
+ if (!rc)
+ rc = q6audio_set_tx_mute(n);
+ break;
+ case AUDIO_UPDATE_ACDB:
+ rc = copy_from_user(&id, (void *)arg, sizeof(id));
+ if (!rc)
+ rc = q6audio_update_acdb(id[0], id[1]);
+ break;
+ case AUDIO_START_VOICE:
+ rc = q6_voice_start();
+ break;
+ case AUDIO_STOP_VOICE:
+ rc = q6_voice_stop();
+ break;
+ default:
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+
+static int q6_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static const struct file_operations q6_dev_fops = {
+ .owner = THIS_MODULE,
+ .open = q6_open,
+ .ioctl = q6_ioctl,
+ .release = q6_release,
+};
+
+struct miscdevice q6_control_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_audio_ctl",
+ .fops = &q6_dev_fops,
+};
+
+
+static int __init q6_audio_ctl_init(void)
+{
+ return misc_register(&q6_control_device);
+}
+
+device_initcall(q6_audio_ctl_init);
diff --git a/arch/arm/mach-msm/qdsp6/audiov2/dal_acdb.h b/arch/arm/mach-msm/qdsp6/audiov2/dal_acdb.h
new file mode 100644
index 0000000..d88b7ad
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/audiov2/dal_acdb.h
@@ -0,0 +1,71 @@
+/* Copyright (c) 2009, 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.
+ *
+ */
+
+#define ACDB_DAL_DEVICE 0x02000069
+#define ACDB_DAL_PORT "DAL_AM_AUD"
+#define ACDB_DAL_VERSION 0x00010000
+
+#define ACDB_OP_IOCTL DAL_OP_FIRST_DEVICE_API
+
+/* ioctls */
+#define ACDB_GET_DEVICE 0x0108bb92
+#define ACDB_SET_DEVICE 0x0108bb93
+#define ACDB_GET_STREAM 0x0108bb95
+#define ACDB_SET_STREAM 0x0108bb96
+#define ACDB_GET_DEVICE_TABLE 0x0108bb97
+#define ACDB_GET_STREAM_TABLE 0x0108bb98
+
+#define ACDB_RES_SUCCESS 0
+#define ACDB_RES_FAILURE -1
+#define ACDB_RES_BADPARM -2
+#define ACDB_RES_BADSTATE -3
+
+struct acdb_cmd_device {
+ uint32_t size;
+
+ uint32_t command_id;
+ uint32_t device_id;
+ uint32_t network_id;
+ uint32_t sample_rate_id;
+ uint32_t interface_id;
+ uint32_t algorithm_block_id;
+
+ /* physical page aligned buffer */
+ uint32_t total_bytes;
+ uint32_t unmapped_buf;
+} __attribute__((packed));
+
+struct acdb_cmd_device_table {
+ uint32_t size;
+
+ uint32_t command_id;
+ uint32_t device_id;
+ uint32_t network_id;
+ uint32_t sample_rate_id;
+
+ /* physical page aligned buffer */
+ uint32_t total_bytes;
+ uint32_t unmapped_buf;
+
+ uint32_t res_size;
+} __attribute__((packed));
+
+struct acdb_result {
+ uint32_t dal_status;
+ uint32_t size;
+
+ uint32_t total_devices;
+ uint32_t unmapped_buf;
+ uint32_t used_bytes;
+ uint32_t result;
+} __attribute__((packed));
diff --git a/arch/arm/mach-msm/qdsp6/audiov2/dal_adie.h b/arch/arm/mach-msm/qdsp6/audiov2/dal_adie.h
new file mode 100644
index 0000000..e828e9c
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/audiov2/dal_adie.h
@@ -0,0 +1,89 @@
+/* Copyright (c) 2009, 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_MSM_QDSP6_ADIE_
+#define _MACH_MSM_QDSP6_ADIE_
+
+#include "../dal.h"
+
+#define ADIE_DAL_DEVICE 0x02000029
+#define ADIE_DAL_PORT "DAL_AM_AUD"
+#define ADIE_DAL_VERSION 0x00010000
+
+enum {
+ ADIE_OP_SET_PATH = DAL_OP_FIRST_DEVICE_API,
+ ADIE_OP_PROCEED_TO_STAGE,
+ ADIE_OP_IOCTL
+};
+
+/* Path IDs for normal operation. */
+#define ADIE_PATH_HANDSET_TX 0x010740f6
+#define ADIE_PATH_HANDSET_RX 0x010740f7
+#define ADIE_PATH_HEADSET_MONO_TX 0x010740f8
+#define ADIE_PATH_HEADSET_STEREO_TX 0x010740f9
+#define ADIE_PATH_HEADSET_MONO_RX 0x010740fa
+#define ADIE_PATH_HEADSET_STEREO_RX 0x010740fb
+#define ADIE_PATH_SPEAKER_TX 0x010740fc
+#define ADIE_PATH_SPEAKER_RX 0x010740fd
+#define ADIE_PATH_SPEAKER_STEREO_RX 0x01074101
+
+/* Path IDs used for TTY */
+#define ADIE_PATH_TTY_HEADSET_TX 0x010740fe
+#define ADIE_PATH_TTY_HEADSET_RX 0x010740ff
+
+/* Path IDs used by Factory Test Mode. */
+#define ADIE_PATH_FTM_MIC1_TX 0x01074108
+#define ADIE_PATH_FTM_MIC2_TX 0x01074107
+#define ADIE_PATH_FTM_HPH_L_RX 0x01074106
+#define ADIE_PATH_FTM_HPH_R_RX 0x01074104
+#define ADIE_PATH_FTM_EAR_RX 0x01074103
+#define ADIE_PATH_FTM_SPKR_RX 0x01074102
+
+/* Path IDs for Loopback */
+/* Path IDs used for Line in -> AuxPGA -> Line Out Stereo Mode*/
+#define ADIE_PATH_AUXPGA_LINEOUT_STEREO_LB 0x01074100
+/* Line in -> AuxPGA -> LineOut Mono */
+#define ADIE_PATH_AUXPGA_LINEOUT_MONO_LB 0x01073d82
+/* Line in -> AuxPGA -> Stereo Headphone */
+#define ADIE_PATH_AUXPGA_HDPH_STEREO_LB 0x01074109
+/* Line in -> AuxPGA -> Mono Headphone */
+#define ADIE_PATH_AUXPGA_HDPH_MONO_LB 0x01073d85
+/* Line in -> AuxPGA -> Earpiece */
+#define ADIE_PATH_AUXPGA_EAP_LB 0x01073d81
+/* Line in -> AuxPGA -> AuxOut */
+#define ADIE_PATH_AUXPGA_AUXOUT_LB 0x01073d86
+
+/* Concurrency Profiles */
+#define ADIE_PATH_SPKR_STEREO_HDPH_MONO_RX 0x01073d83
+#define ADIE_PATH_SPKR_MONO_HDPH_MONO_RX 0x01073d84
+#define ADIE_PATH_SPKR_MONO_HDPH_STEREO_RX 0x01073d88
+#define ADIE_PATH_SPKR_STEREO_HDPH_STEREO_RX 0x01073d89
+
+/* stages */
+#define ADIE_STAGE_PATH_OFF 0x0050
+#define ADIE_STAGE_DIGITAL_READY 0x0100
+#define ADIE_STAGE_DIGITAL_ANALOG_READY 0x1000
+#define ADIE_STAGE_ANALOG_OFF 0x0750
+#define ADIE_STAGE_DIGITAL_OFF 0x0600
+
+/* path types */
+#define ADIE_PATH_RX 0
+#define ADIE_PATH_TX 1
+#define ADIE_PATH_LOOPBACK 2
+
+/* mute states */
+#define ADIE_MUTE_OFF 0
+#define ADIE_MUTE_ON 1
+
+
+#endif
diff --git a/arch/arm/mach-msm/qdsp6/audiov2/dal_audio.h b/arch/arm/mach-msm/qdsp6/audiov2/dal_audio.h
new file mode 100644
index 0000000..52de785
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/audiov2/dal_audio.h
@@ -0,0 +1,546 @@
+/* Copyright (c) 2009, 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 __DAL_AUDIO_H__
+#define __DAL_AUDIO_H__
+
+#include "../dal.h"
+#include "dal_audio_format.h"
+
+#define AUDIO_DAL_DEVICE 0x02000028
+#define AUDIO_DAL_PORT "DAL_AQ_AUD"
+#define AUDIO_DAL_VERSION 0x00030001
+
+enum {
+ AUDIO_OP_CONTROL = DAL_OP_FIRST_DEVICE_API,
+ AUDIO_OP_DATA,
+ AUDIO_OP_INIT,
+};
+
+/* ---- common audio structures ---- */
+
+/* This flag, if set, indicates that the beginning of the data in the*/
+/* buffer is a synchronization point or key frame, meaning no data */
+/* before it in the stream is required in order to render the stream */
+/* from this point onward. */
+#define ADSP_AUDIO_BUFFER_FLAG_SYNC_POINT 0x01
+
+/* This flag, if set, indicates that the buffer object is using valid */
+/* physical address used to store the media data */
+#define ADSP_AUDIO_BUFFER_FLAG_PHYS_ADDR 0x04
+
+/* This flag, if set, indicates that a media start timestamp has been */
+/* set for a buffer. */
+#define ADSP_AUDIO_BUFFER_FLAG_START_SET 0x08
+
+/* This flag, if set, indicates that a media stop timestamp has been set */
+/* for a buffer. */
+#define ADSP_AUDIO_BUFFER_FLAG_STOP_SET 0x10
+
+/* This flag, if set, indicates that a preroll timestamp has been set */
+/* for a buffer. */
+#define ADSP_AUDIO_BUFFER_FLAG_PREROLL_SET 0x20
+
+/* This flag, if set, indicates that the data in the buffer is a fragment of */
+/* a larger block of data, and will be continued by the data in the next */
+/* buffer to be delivered. */
+#define ADSP_AUDIO_BUFFER_FLAG_CONTINUATION 0x40
+
+struct adsp_audio_buffer {
+ u32 addr; /* Physical Address of buffer */
+ u32 max_size; /* Maximum size of buffer */
+ u32 actual_size; /* Actual size of valid data in the buffer */
+ u32 offset; /* Offset to the first valid byte */
+ u32 flags; /* ADSP_AUDIO_BUFFER_FLAGs that has been set */
+ s64 start; /* Start timestamp, if any */
+ s64 stop; /* Stop timestamp, if any */
+ s64 preroll; /* Preroll timestamp, if any */
+} __attribute__ ((packed));
+
+
+
+/* ---- audio commands ---- */
+
+/* Command/event response types */
+#define ADSP_AUDIO_RESPONSE_COMMAND 0
+#define ADSP_AUDIO_RESPONSE_ASYNC 1
+
+struct adsp_command_hdr {
+ u32 size; /* sizeof(cmd) - sizeof(u32) */
+
+ u32 dest;
+ u32 src;
+ u32 opcode;
+ u32 response_type;
+ u32 seq_number;
+
+ u32 context; /* opaque to DSP */
+ u32 data;
+ u32 padding;
+} __attribute__ ((packed));
+
+
+#define DOMAIN_APP 0
+#define DOMAIN_MODEM 1
+#define DOMAIN_DSP 2
+
+
+/* adsp audio addresses are (byte order) major, minor, domain */
+#define AUDIO_ADDR(dmn, maj, min) (((maj & 0xff) << 16) \
+ | ((min & 0xff) << 24) | (dmn & 0xff))
+
+/* AAC Encoder modes */
+#define ADSP_AUDIO_ENC_AAC_LC_ONLY_MODE 0
+#define ADSP_AUDIO_ENC_AAC_PLUS_MODE 1
+#define ADSP_AUDIO_ENC_ENHANCED_AAC_PLUS_MODE 2
+
+struct adsp_audio_aac_enc_cfg {
+ u32 bit_rate; /* bits per second */
+ u32 encoder_mode; /* ADSP_AUDIO_ENC_* */
+} __attribute__ ((packed));
+
+#define ADSP_AUDIO_ENC_SBC_ALLOCATION_METHOD_LOUNDNESS 0
+#define ADSP_AUDIO_ENC_SBC_ALLOCATION_METHOD_SNR 1
+
+#define ADSP_AUDIO_ENC_SBC_CHANNEL_MODE_MONO 1
+#define ADSP_AUDIO_ENC_SBC_CHANNEL_MODE_STEREO 2
+#define ADSP_AUDIO_ENC_SBC_CHANNEL_MODE_DUAL 8
+#define ADSP_AUDIO_ENC_SBC_CHANNEL_MODE_JOINT_STEREO 9
+
+struct adsp_audio_sbc_encoder_cfg {
+ u32 num_subbands;
+ u32 block_len;
+ u32 channel_mode;
+ u32 allocation_method;
+ u32 bit_rate;
+} __attribute__ ((packed));
+
+/* AMR NB encoder modes */
+#define ADSP_AUDIO_AMR_MR475 0
+#define ADSP_AUDIO_AMR_MR515 1
+#define ADSP_AUDIO_AMR_MMR59 2
+#define ADSP_AUDIO_AMR_MMR67 3
+#define ADSP_AUDIO_AMR_MMR74 4
+#define ADSP_AUDIO_AMR_MMR795 5
+#define ADSP_AUDIO_AMR_MMR102 6
+#define ADSP_AUDIO_AMR_MMR122 7
+
+/* The following are valid AMR NB DTX modes */
+#define ADSP_AUDIO_AMR_DTX_MODE_OFF 0
+#define ADSP_AUDIO_AMR_DTX_MODE_ON_VAD1 1
+#define ADSP_AUDIO_AMR_DTX_MODE_ON_VAD2 2
+#define ADSP_AUDIO_AMR_DTX_MODE_ON_AUTO 3
+
+/* AMR Encoder configuration */
+struct adsp_audio_amr_enc_cfg {
+ u32 mode; /* ADSP_AUDIO_AMR_MR* */
+ u32 dtx_mode; /* ADSP_AUDIO_AMR_DTX_MODE* */
+ u32 enable; /* 1 = enable, 0 = disable */
+} __attribute__ ((packed));
+
+struct adsp_audio_qcelp13k_enc_cfg {
+ u16 min_rate;
+ u16 max_rate;
+} __attribute__ ((packed));
+
+struct adsp_audio_evrc_enc_cfg {
+ u16 min_rate;
+ u16 max_rate;
+} __attribute__ ((packed));
+
+union adsp_audio_codec_config {
+ struct adsp_audio_amr_enc_cfg amr;
+ struct adsp_audio_aac_enc_cfg aac;
+ struct adsp_audio_qcelp13k_enc_cfg qcelp13k;
+ struct adsp_audio_evrc_enc_cfg evrc;
+ struct adsp_audio_sbc_encoder_cfg sbc;
+} __attribute__ ((packed));
+
+
+/* This is the default value. */
+#define ADSP_AUDIO_OPEN_STREAM_MODE_NONE 0x0000
+
+/* This bit, if set, indicates that the AVSync mode is activated. */
+#define ADSP_AUDIO_OPEN_STREAM_MODE_AVSYNC 0x0001
+
+/* This bit, if set, indicates that the Sample Rate/Channel Mode */
+/* Change Notification mode is activated. */
+#define ADSP_AUDIO_OPEN_STREAM_MODE_SR_CM_NOTIFY 0x0002
+
+#define ADSP_AUDIO_OPEN_STREAM_MODE_ENABLE_SYNC_CLOCK 0x0004
+
+#define ADSP_AUDIO_MAX_DEVICES 1
+
+struct adsp_open_command {
+ struct adsp_command_hdr hdr;
+ u32 device;
+ u32 end_point;
+ u32 stream_context;
+ u32 mode;
+ u32 buf_max_size;
+ union adsp_audio_format format_block;
+ union adsp_audio_codec_config config;
+
+} __attribute__ ((packed));
+
+
+/* --- audio control and stream session ioctls ---- */
+
+/* Opcode to open a device stream session to capture audio */
+#define ADSP_AUDIO_IOCTL_CMD_OPEN_READ 0x0108dd79
+
+/* Opcode to open a device stream session to render audio */
+#define ADSP_AUDIO_IOCTL_CMD_OPEN_WRITE 0x0108dd7a
+
+/* Opcode to open a device session, must open a device */
+#define ADSP_AUDIO_IOCTL_CMD_OPEN_DEVICE 0x0108dd7b
+
+/* Close an existing stream or device */
+#define ADSP_AUDIO_IOCTL_CMD_CLOSE 0x0108d8bc
+
+
+
+/* A device switch requires three IOCTL */
+/* commands in the following sequence: PREPARE, STANDBY, COMMIT */
+
+/* adsp_audio_device_switch_command structure is needed for */
+/* DEVICE_SWITCH_PREPARE */
+
+/* Device switch protocol step #1. Pause old device and */
+/* generate silence for the old device. */
+#define ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_PREPARE 0x010815c4
+
+/* Device switch protocol step #2. Release old device, */
+/* create new device and generate silence for the new device. */
+
+/* When client receives ack for this IOCTL, the client can */
+/* start sending IOCTL commands to configure, calibrate and */
+/* change filter settings on the new device. */
+#define ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_STANDBY 0x010815c5
+
+/* Device switch protocol step #3. Start normal operations on new device */
+#define ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_COMMIT 0x01075ee7
+
+struct adsp_device_switch_command {
+ struct adsp_command_hdr hdr;
+ u32 old_device;
+ u32 new_device;
+ u8 device_class; /* 0 = i.rx, 1 = i.tx, 2 = e.rx, 3 = e.tx */
+ u8 device_type; /* 0 = rx, 1 = tx, 2 = both */
+} __attribute__ ((packed));
+
+
+
+/* --- audio control session ioctls ---- */
+
+#define ADSP_PATH_RX 0
+#define ADSP_PATH_TX 1
+#define ADSP_PATH_BOTH 2
+
+/* These commands will affect a logical device and all its associated */
+/* streams. */
+
+
+/* Set device volume. */
+#define ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_VOL 0x0107605c
+
+struct adsp_set_dev_volume_command {
+ struct adsp_command_hdr hdr;
+ u32 device_id;
+ u32 path; /* 0 = rx, 1 = tx, 2 = both */
+ s32 volume;
+} __attribute__ ((packed));
+
+/* Set Device stereo volume. This command has data payload, */
+/* struct adsp_audio_set_dev_stereo_volume_command. */
+#define ADSP_AUDIO_IOCTL_SET_DEVICE_STEREO_VOL 0x0108df3e
+
+/* Set L, R cross channel gain for a Device. This command has */
+/* data payload, struct adsp_audio_set_dev_x_chan_gain_command. */
+#define ADSP_AUDIO_IOCTL_SET_DEVICE_XCHAN_GAIN 0x0108df40
+
+/* Set device mute state. */
+#define ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_MUTE 0x0107605f
+
+struct adsp_set_dev_mute_command {
+ struct adsp_command_hdr hdr;
+ u32 device_id;
+ u32 path; /* 0 = rx, 1 = tx, 2 = both */
+ u32 mute; /* 1 = mute */
+} __attribute__ ((packed));
+
+/* Configure Equalizer for a device. */
+/* This command has payload struct adsp_audio_set_dev_equalizer_command. */
+#define ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_EQ_CONFIG 0x0108b10e
+
+/* Set configuration data for an algorithm aspect of a device. */
+/* This command has payload struct adsp_audio_set_dev_cfg_command. */
+#define ADSP_AUDIO_IOCTL_SET_DEVICE_CONFIG 0x0108b6cb
+
+struct adsp_set_dev_cfg_command {
+ struct adsp_command_hdr hdr;
+ u32 device_id;
+ u32 block_id;
+ u32 interface_id;
+ u32 phys_addr;
+ u32 phys_size;
+ u32 phys_used;
+} __attribute__ ((packed));
+
+/* Set configuration data for all interfaces of a device. */
+#define ADSP_AUDIO_IOCTL_SET_DEVICE_CONFIG_TABLE 0x0108b6bf
+
+struct adsp_set_dev_cfg_table_command {
+ struct adsp_command_hdr hdr;
+ u32 device_id;
+ u32 phys_addr;
+ u32 phys_size;
+ u32 phys_used;
+} __attribute__ ((packed));
+
+/* ---- audio stream data commands ---- */
+
+#define ADSP_AUDIO_IOCTL_CMD_DATA_TX 0x0108dd7f
+#define ADSP_AUDIO_IOCTL_CMD_DATA_RX 0x0108dd80
+
+struct adsp_buffer_command {
+ struct adsp_command_hdr hdr;
+ struct adsp_audio_buffer buffer;
+} __attribute__ ((packed));
+
+
+
+/* ---- audio stream ioctls (only affect a single stream in a session) ---- */
+
+/* Stop stream for audio device. */
+#define ADSP_AUDIO_IOCTL_CMD_STREAM_STOP 0x01075c54
+
+/* End of stream reached. Client will not send any more data. */
+#define ADSP_AUDIO_IOCTL_CMD_STREAM_EOS 0x0108b150
+
+/* Do sample slipping/stuffing on AAC outputs. The payload of */
+/* this command is struct adsp_audio_slip_sample_command. */
+#define ADSP_AUDIO_IOCTL_CMD_STREAM_SLIPSAMPLE 0x0108d40e
+
+/* Set stream volume. */
+/* This command has data payload, struct adsp_audio_set_volume_command. */
+#define ADSP_AUDIO_IOCTL_CMD_SET_STREAM_VOL 0x0108c0de
+
+/* Set stream stereo volume. This command has data payload, */
+/* struct adsp_audio_set_stereo_volume_command. */
+#define ADSP_AUDIO_IOCTL_SET_STREAM_STEREO_VOL 0x0108dd7c
+
+/* Set L, R cross channel gain for a Stream. This command has */
+/* data payload, struct adsp_audio_set_x_chan_gain_command. */
+#define ADSP_AUDIO_IOCTL_SET_STREAM_XCHAN_GAIN 0x0108dd7d
+
+/* Set stream mute state. */
+/* This command has data payload, struct adsp_audio_set_stream_mute. */
+#define ADSP_AUDIO_IOCTL_CMD_SET_STREAM_MUTE 0x0108c0df
+
+/* Reconfigure bit rate information. This command has data */
+/* payload, struct adsp_audio_set_bit_rate_command */
+#define ADSP_AUDIO_IOCTL_SET_STREAM_BITRATE 0x0108ccf1
+
+/* Set Channel Mapping. This command has data payload, struct */
+/* This command has data payload struct adsp_audio_set_channel_map_command. */
+#define ADSP_AUDIO_IOCTL_SET_STREAM_CHANNELMAP 0x0108d32a
+
+/* Enable/disable AACPlus SBR. */
+/* This command has data payload struct adsp_audio_set_sbr_command */
+#define ADSP_AUDIO_IOCTL_SET_STREAM_SBR 0x0108d416
+
+/* Enable/disable WMA Pro Chex and Fex. This command has data payload */
+/* struct adsp_audio_stream_set_wma_command. */
+#define ADSP_AUDIO_IOCTL_SET_STREAM_WMAPRO 0x0108d417
+
+
+/* ---- audio session ioctls (affect all streams in a session) --- */
+
+/* Start stream for audio device. */
+#define ADSP_AUDIO_IOCTL_CMD_SESSION_START 0x010815c6
+
+/* Stop all stream(s) for audio session as indicated by major id. */
+#define ADSP_AUDIO_IOCTL_CMD_SESSION_STOP 0x0108dd7e
+
+/* Pause the data flow for a session as indicated by major id. */
+#define ADSP_AUDIO_IOCTL_CMD_SESSION_PAUSE 0x01075ee8
+
+/* Resume the data flow for a session as indicated by major id. */
+#define ADSP_AUDIO_IOCTL_CMD_SESSION_RESUME 0x01075ee9
+
+/* Drop any unprocessed data buffers for a session as indicated by major id. */
+#define ADSP_AUDIO_IOCTL_CMD_SESSION_FLUSH 0x01075eea
+
+/* Start Stream DTMF tone */
+#define ADSP_AUDIO_IOCTL_CMD_SESSION_DTMF_START 0x0108c0dd
+
+/* Stop Stream DTMF tone */
+#define ADSP_AUDIO_IOCTL_CMD_SESSION_DTMF_STOP 0x01087554
+
+/* Set Session volume. */
+/* This command has data payload, struct adsp_audio_set_volume_command. */
+#define ADSP_AUDIO_IOCTL_SET_SESSION_VOL 0x0108d8bd
+
+/* Set session stereo volume. This command has data payload, */
+/* struct adsp_audio_set_stereo_volume_command. */
+#define ADSP_AUDIO_IOCTL_SET_SESSION_STEREO_VOL 0x0108df3d
+
+/* Set L, R cross channel gain for a session. This command has */
+/* data payload, struct adsp_audio_set_x_chan_gain_command. */
+#define ADSP_AUDIO_IOCTL_SET_SESSION_XCHAN_GAIN 0x0108df3f
+
+/* Set Session mute state. */
+/* This command has data payload, struct adsp_audio_set_mute_command. */
+#define ADSP_AUDIO_IOCTL_SET_SESSION_MUTE 0x0108d8be
+
+/* Configure Equalizer for a stream. */
+/* This command has payload struct adsp_audio_set_equalizer_command. */
+#define ADSP_AUDIO_IOCTL_SET_SESSION_EQ_CONFIG 0x0108c0e0
+
+/* Set Audio Video sync information. */
+/* This command has data payload, struct adsp_audio_set_av_sync_command. */
+#define ADSP_AUDIO_IOCTL_SET_SESSION_AVSYNC 0x0108d1e2
+
+/* Get Audio Media Session time. */
+/* This command returns the audioTime in adsp_audio_unsigned64_event */
+#define ADSP_AUDIO_IOCTL_CMD_GET_AUDIO_TIME 0x0108c26c
+
+
+/* these command structures are used for both STREAM and SESSION ioctls */
+
+struct adsp_set_volume_command {
+ struct adsp_command_hdr hdr;
+ s32 volume;
+} __attribute__ ((packed));
+
+struct adsp_set_mute_command {
+ struct adsp_command_hdr hdr;
+ u32 mute; /* 1 == mute */
+} __attribute__ ((packed));
+
+
+
+/* ---- audio events ---- */
+
+/* All IOCTL commands generate an event with the IOCTL opcode as the */
+/* event id after the IOCTL command has been executed. */
+
+/* This event is generated after a media stream session is opened. */
+#define ADSP_AUDIO_EVT_STATUS_OPEN 0x0108c0d6
+
+/* This event is generated after a media stream session is closed. */
+#define ADSP_AUDIO_EVT_STATUS_CLOSE 0x0108c0d7
+
+/* Asyncronous buffer consumption. This event is generated after a */
+/* recived buffer is consumed during rendering or filled during */
+/* capture opeartion. */
+#define ADSP_AUDIO_EVT_STATUS_BUF_DONE 0x0108c0d8
+
+/* This event is generated when rendering operation is starving for */
+/* data. In order to avoid audio loss at the end of a plauback, the */
+/* client should wait for this event before issuing the close command. */
+#define ADSP_AUDIO_EVT_STATUS_BUF_UNDERRUN 0x0108c0d9
+
+/* This event is generated during capture operation when there are no */
+/* buffers available to copy the captured audio data */
+#define ADSP_AUDIO_EVT_STATUS_BUF_OVERFLOW 0x0108c0da
+
+/* This asynchronous event is generated as a result of an input */
+/* sample rate change and/or channel mode change detected by the */
+/* decoder. The event payload data is an array of 2 uint32 */
+/* values containing the sample rate in Hz and channel mode. */
+#define ADSP_AUDIO_EVT_SR_CM_CHANGE 0x0108d329
+
+struct adsp_event_hdr {
+ u32 evt_handle; /* DAL common header */
+ u32 evt_cookie;
+ u32 evt_length;
+
+ u32 dest;
+ u32 src;
+
+ u32 event_id;
+ u32 response_type;
+ u32 seq_number;
+
+ u32 context; /* opaque to DSP */
+ u32 data;
+
+ u32 status;
+} __attribute__ ((packed));
+
+struct adsp_buffer_event {
+ struct adsp_event_hdr hdr;
+ struct adsp_audio_buffer buffer;
+} __attribute__ ((packed));
+
+
+/* ---- audio device IDs ---- */
+
+/* Device direction Rx/Tx flag */
+#define ADSP_AUDIO_RX_DEVICE 0x00
+#define ADSP_AUDIO_TX_DEVICE 0x01
+
+#define ADSP_AUDIO_DEVICE_ID_DEFAULT 0x1081679
+
+/* Default RX or TX device */
+
+#define ADSP_AUDIO_DEVICE_ID_HANDSET_MIC 0x107ac8d
+#define ADSP_AUDIO_DEVICE_ID_HANDSET_DUAL_MIC 0x108f9c3
+#define ADSP_AUDIO_DEVICE_ID_HEADSET_MIC 0x1081510
+#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MIC 0x1081512
+#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_DUAL_MIC 0x108f9c5
+#define ADSP_AUDIO_DEVICE_ID_BT_SCO_MIC 0x1081518
+#define ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_MIC 0x108151b
+#define ADSP_AUDIO_DEVICE_ID_I2S_MIC 0x1089bf3
+
+/* Special loopback pseudo device to be paired with an RX device */
+/* with usage ADSP_AUDIO_DEVICE_USAGE_MIXED_PCM_LOOPBACK */
+#define ADSP_AUDIO_DEVICE_ID_MIXED_PCM_LOOPBACK_TX 0x1089bf2
+
+/* Sink (RX) devices */
+#define ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR 0x107ac88
+#define ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_MONO 0x1081511
+#define ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_STEREO 0x107ac8a
+#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO 0x1081513
+#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_MONO_HEADSET 0x108c508
+#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_STEREO_HEADSET 0x108c894
+#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO 0x1081514
+#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_MONO_HEADSET 0x108c895
+#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_STEREO_HEADSET 0x108c509
+#define ADSP_AUDIO_DEVICE_ID_BT_SCO_SPKR 0x1081519
+#define ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_SPKR 0x108151c
+#define ADSP_AUDIO_DEVICE_ID_I2S_SPKR 0x1089bf4
+#define ADSP_AUDIO_DEVICE_ID_NULL_SINK 0x108e512
+
+/* BT A2DP playback device. */
+/* This device must be paired with */
+/* ADSP_AUDIO_DEVICE_ID_MIXED_PCM_LOOPBACK_TX using */
+/* ADSP_AUDIO_DEVICE_USAGE_MIXED_PCM_LOOPBACK mode */
+#define ADSP_AUDIO_DEVICE_ID_BT_A2DP_SPKR 0x108151a
+
+/* Voice Destination identifier - specifically used for */
+/* controlling Voice module from the Device Control Session */
+#define ADSP_AUDIO_DEVICE_ID_VOICE 0x0108df3c
+
+/* Audio device usage types. */
+/* This is a bit mask to determine which topology to use in the */
+/* device session */
+#define ADSP_AUDIO_DEVICE_CONTEXT_VOICE 0x01
+#define ADSP_AUDIO_DEVICE_CONTEXT_PLAYBACK 0x02
+#define ADSP_AUDIO_DEVICE_CONTEXT_MIXED_RECORD 0x10
+#define ADSP_AUDIO_DEVICE_CONTEXT_RECORD 0x20
+#define ADSP_AUDIO_DEVICE_CONTEXT_PCM_LOOPBACK 0x40
+
+#endif
diff --git a/arch/arm/mach-msm/qdsp6/audiov2/dal_audio_format.h b/arch/arm/mach-msm/qdsp6/audiov2/dal_audio_format.h
new file mode 100644
index 0000000..348aad1
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/audiov2/dal_audio_format.h
@@ -0,0 +1,284 @@
+/* Copyright (c) 2009, 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 __ADSP_AUDIO_MEDIA_FORMAT_H
+#define __ADSP_AUDIO_MEDIA_FORMAT_H
+
+/* Supported audio media formats */
+
+/* format block in shmem */
+#define ADSP_AUDIO_FORMAT_SHAREDMEMORY 0x01091a78
+
+/* adsp_audio_format_raw_pcm type */
+#define ADSP_AUDIO_FORMAT_PCM 0x0103d2fd
+
+/* adsp_audio_format_raw_pcm type */
+#define ADSP_AUDIO_FORMAT_DTMF 0x01087725
+
+/* adsp_audio_format_adpcm type */
+#define ADSP_AUDIO_FORMAT_ADPCM 0x0103d2ff
+
+/* Yamaha PCM format */
+#define ADSP_AUDIO_FORMAT_YADPCM 0x0108dc07
+
+/* ISO/IEC 11172 */
+#define ADSP_AUDIO_FORMAT_MP3 0x0103d308
+
+/* ISO/IEC 14496 */
+#define ADSP_AUDIO_FORMAT_MPEG4_AAC 0x010422f1
+
+/* AMR-NB audio in FS format */
+#define ADSP_AUDIO_FORMAT_AMRNB_FS 0x0105c16c
+
+/* AMR-WB audio in FS format */
+#define ADSP_AUDIO_FORMAT_AMRWB_FS 0x0105c16e
+
+/* QCELP 13k, IS733 */
+#define ADSP_AUDIO_FORMAT_V13K_FS 0x01080b8a
+
+/* EVRC 8k, IS127 */
+#define ADSP_AUDIO_FORMAT_EVRC_FS 0x01080b89
+
+/* EVRC-B 8k, 4GV */
+#define ADSP_AUDIO_FORMAT_EVRCB_FS 0x0108f2a3
+
+/* MIDI command stream */
+#define ADSP_AUDIO_FORMAT_MIDI 0x0103d300
+
+/* A2DP SBC stream */
+#define ADSP_AUDIO_FORMAT_SBC 0x0108c4d8
+
+/* Version 10 Professional */
+#define ADSP_AUDIO_FORMAT_WMA_V10PRO 0x0108aa92
+
+/* Version 9 Starndard */
+#define ADSP_AUDIO_FORMAT_WMA_V9 0x0108d430
+
+/* AMR WideBand Plus */
+#define ADSP_AUDIO_FORMAT_AMR_WB_PLUS 0x0108f3da
+
+/* AC3 Decoder */
+#define ADSP_AUDIO_FORMAT_AC3_DECODER 0x0108d5f9
+
+/* Not yet supported audio media formats */
+
+/* ISO/IEC 13818 */
+#define ADSP_AUDIO_FORMAT_MPEG2_AAC 0x0103d309
+
+/* 3GPP TS 26.101 Sec 4.0 */
+#define ADSP_AUDIO_FORMAT_AMRNB_IF1 0x0103d305
+
+/* 3GPP TS 26.101 Annex A */
+#define ADSP_AUDIO_FORMAT_AMRNB_IF2 0x01057b31
+
+/* 3GPP TS 26.201 */
+#define ADSP_AUDIO_FORMAT_AMRWB_IF1 0x0103d306
+
+/* 3GPP TS 26.201 */
+#define ADSP_AUDIO_FORMAT_AMRWB_IF2 0x0105c16d
+
+/* G.711 */
+#define ADSP_AUDIO_FORMAT_G711 0x0106201d
+
+/* QCELP 8k, IS96A */
+#define ADSP_AUDIO_FORMAT_V8K_FS 0x01081d29
+
+/* Version 1 codec */
+#define ADSP_AUDIO_FORMAT_WMA_V1 0x01055b2b
+
+/* Version 2, 7 & 8 codec */
+#define ADSP_AUDIO_FORMAT_WMA_V8 0x01055b2c
+
+/* Version 9 Professional codec */
+#define ADSP_AUDIO_FORMAT_WMA_V9PRO 0x01055b2d
+
+/* Version 9 Voice codec */
+#define ADSP_AUDIO_FORMAT_WMA_SP1 0x01055b2e
+
+/* Version 9 Lossless codec */
+#define ADSP_AUDIO_FORMAT_WMA_LOSSLESS 0x01055b2f
+
+/* Real Media content, low-bitrate */
+#define ADSP_AUDIO_FORMAT_RA_SIPR 0x01042a0f
+
+/* Real Media content */
+#define ADSP_AUDIO_FORMAT_RA_COOK 0x01042a0e
+
+
+/* For all of the audio formats, unless specified otherwise, */
+/* the following apply: */
+/* Format block bits are arranged in bytes and words in little-endian */
+/* order, i.e., least-significant bit first and least-significant */
+/* byte first. */
+
+
+/* AAC Format Block. */
+
+/* AAC format block consist of a format identifier followed by */
+/* AudioSpecificConfig formatted according to ISO/IEC 14496-3 */
+
+/* The following AAC format identifiers are supported */
+#define ADSP_AUDIO_AAC_ADTS 0x010619cf
+#define ADSP_AUDIO_AAC_MPEG4_ADTS 0x010619d0
+#define ADSP_AUDIO_AAC_LOAS 0x010619d1
+#define ADSP_AUDIO_AAC_ADIF 0x010619d2
+#define ADSP_AUDIO_AAC_RAW 0x010619d3
+#define ADSP_AUDIO_AAC_FRAMED_RAW 0x0108c1fb
+
+struct adsp_audio_no_payload_format {
+ /* Media Format Code (must always be first element) */
+ u32 format;
+ /* no payload for this format type */
+} __attribute__ ((packed));
+
+/* Maxmum number of bytes allowed in a format block */
+#define ADSP_AUDIO_FORMAT_DATA_MAX 16
+
+/* For convenience, to be used as a standard format block */
+/* for various media types that don't need a unique format block */
+/* ie. PCM, DTMF, etc. */
+struct adsp_audio_standard_format {
+ /* Media Format Code (must always be first element) */
+ u32 format;
+
+ /* payload */
+ u16 channels;
+ u16 bits_per_sample;
+ u32 sampling_rate;
+ u8 is_signed;
+ u8 is_interleaved;
+} __attribute__ ((packed));
+
+/* ADPCM format block */
+struct adsp_audio_adpcm_format {
+ /* Media Format Code (must always be first element) */
+ u32 format;
+
+ /* payload */
+ u16 channels;
+ u16 bits_per_sample;
+ u32 sampling_rate;
+ u8 is_signed;
+ u8 is_interleaved;
+ u32 block_size;
+} __attribute__ ((packed));
+
+/* MIDI format block */
+struct adsp_audio_midi_format {
+ /* Media Format Code (must always be first element) */
+ u32 format;
+
+ /* payload */
+ u32 sampling_rate;
+ u16 channels;
+ u16 mode;
+} __attribute__ ((packed));
+
+#define ADSP_AUDIO_COMPANDING_ALAW 0x10619cd
+#define ADSP_AUDIO_COMPANDING_MLAW 0x10619ce
+
+/* G711 format block */
+struct adsp_audio_g711_format {
+ /* Media Format Code (must always be first element) */
+ u32 format;
+
+ /* payload */
+ u32 companding;
+} __attribute__ ((packed));
+
+
+struct adsp_audio_wma_pro_format {
+ /* Media Format Code (must always be first element) */
+ u32 format;
+
+ /* payload */
+ u16 format_tag;
+ u16 channels;
+ u32 samples_per_sec;
+ u32 avg_bytes_per_sec;
+ u16 block_align;
+ u16 valid_bits_per_sample;
+ u32 channel_mask;
+ u16 encode_opt;
+ u16 advanced_encode_opt;
+ u32 advanced_encode_opt2;
+ u32 drc_peak_reference;
+ u32 drc_peak_target;
+ u32 drc_average_reference;
+ u32 drc_average_target;
+} __attribute__ ((packed));
+
+struct adsp_audio_amrwb_plus_format {
+ /* Media Format Code (must always be first element) */
+ u32 format;
+
+ /* payload */
+ u32 size;
+ u32 version;
+ u32 channels;
+ u32 amr_band_mode;
+ u32 amr_dtx_mode;
+ u32 amr_frame_format;
+ u32 amr_isf_index;
+} __attribute__ ((packed));
+
+/* Binary Byte Stream Format */
+/* Binary format type that defines a byte stream, */
+/* can be used to specify any format (ie. AAC) */
+struct adsp_audio_binary_format {
+ /* Media Format Code (must always be first element) */
+ u32 format;
+
+ /* payload */
+ /* number of bytes set in byte stream */
+ u32 num_bytes;
+ /* Byte stream binary data */
+ u8 data[ADSP_AUDIO_FORMAT_DATA_MAX];
+} __attribute__ ((packed));
+
+struct adsp_audio_shared_memory_format {
+ /* Media Format Code (must always be first element) */
+ u32 format;
+
+ /* Number of bytes in shared memory */
+ u32 len;
+ /* Phyisical address to data in shared memory */
+ u32 address;
+} __attribute__ ((packed));
+
+
+/* Union of all format types */
+union adsp_audio_format {
+ /* Basic format block with no payload */
+ struct adsp_audio_no_payload_format no_payload;
+ /* Generic format block PCM, DTMF */
+ struct adsp_audio_standard_format standard;
+ /* ADPCM format block */
+ struct adsp_audio_adpcm_format adpcm;
+ /* MIDI format block */
+ struct adsp_audio_midi_format midi;
+ /* G711 format block */
+ struct adsp_audio_g711_format g711;
+ /* WmaPro format block */
+ struct adsp_audio_wma_pro_format wma_pro;
+ /* WmaPro format block */
+ struct adsp_audio_amrwb_plus_format amrwb_plus;
+ /* binary (byte stream) format block, used for AAC */
+ struct adsp_audio_binary_format binary;
+ /* format block in shared memory */
+ struct adsp_audio_shared_memory_format shared_mem;
+};
+
+#endif
+
+
diff --git a/arch/arm/mach-msm/qdsp6/audiov2/dal_voice.h b/arch/arm/mach-msm/qdsp6/audiov2/dal_voice.h
new file mode 100644
index 0000000..62c1122
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/audiov2/dal_voice.h
@@ -0,0 +1,69 @@
+/* Copyright (c) 2009, 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 __DAL_VOICE_H__
+#define __DAL_VOICE_H__
+
+#define VOICE_DAL_DEVICE 0x02000075
+#define VOICE_DAL_PORT "DAL_AM_AUD"
+#define VOICE_DAL_VERSION 0x00010000
+
+#define APR_PKTV1_TYPE_EVENT_V 0
+#define APR_UNDEFINED -1
+#define APR_PKTV1_TYPE_MASK 0x00000010
+#define APR_PKTV1_TYPE_SHFT 4
+
+#define APR_SET_BITMASK(mask, shift, value) \
+ (((value) << (shift)) & (mask))
+
+#define APR_SET_FIELD(field, value) \
+ APR_SET_BITMASK((field##_MASK), (field##_SHFT), (value))
+
+
+enum {
+ VOICE_OP_INIT = DAL_OP_FIRST_DEVICE_API,
+ VOICE_OP_CONTROL,
+};
+
+struct apr_command_pkt {
+ uint32_t size;
+ uint32_t header;
+ uint16_t reserved1;
+ uint16_t src_addr;
+ uint16_t dst_addr;
+ uint16_t ret_addr;
+ uint32_t src_token;
+ uint32_t dst_token;
+ uint32_t ret_token;
+ uint32_t context;
+ uint32_t opcode;
+} __attribute__ ((packed));
+
+
+#define APR_IBASIC_RSP_RESULT 0x00010000
+
+#define APR_OP_CMD_CREATE 0x0001001B
+
+#define APR_OP_CMD_DESTROY 0x0001001C
+
+#define VOICE_OP_CMD_BRINGUP 0x0001001E
+
+#define VOICE_OP_CMD_TEARDOWN 0x0001001F
+
+#define VOICE_OP_CMD_SET_NETWORK 0x0001001D
+
+#define VOICE_OP_CMD_STREAM_SETUP 0x00010027
+
+#define VOICE_OP_CMD_STREAM_TEARDOWN 0x00010028
+
+#endif
diff --git a/arch/arm/mach-msm/qdsp6/audiov2/evrc_in.c b/arch/arm/mach-msm/qdsp6/audiov2/evrc_in.c
new file mode 100644
index 0000000..88f19b7
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/audiov2/evrc_in.c
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2009 Google, Inc.
+ * Copyright (C) 2009 HTC Corporation
+ * Copyright (c) 2009, 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/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+
+#include <linux/msm_audio_qcp.h>
+#include <mach/msm_qdsp6_audiov2.h>
+#include "dal_audio.h"
+#include "dal_audio_format.h"
+#include <mach/debug_mm.h>
+
+
+struct evrc {
+ struct mutex lock;
+ struct msm_audio_evrc_enc_config cfg;
+ struct msm_audio_stream_config str_cfg;
+ struct audio_client *audio_client;
+};
+
+
+static long q6_evrc_in_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct evrc *evrc = file->private_data;
+ struct adsp_open_command rpc;
+ int rc = 0;
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ memset(&stats, 0, sizeof(stats));
+ if (copy_to_user((void *) arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ }
+
+ mutex_lock(&evrc->lock);
+ switch (cmd) {
+ case AUDIO_START:
+ if (evrc->audio_client) {
+ rc = -EBUSY;
+ break;
+ } else {
+ evrc->audio_client = q6audio_open(AUDIO_FLAG_READ,
+ evrc->str_cfg.buffer_size);
+
+ if (!evrc->audio_client) {
+ kfree(evrc);
+ rc = -ENOMEM;
+ break;
+ }
+ }
+
+ tx_clk_freq = 8000;
+
+ memset(&rpc, 0, sizeof(rpc));
+
+ rpc.format_block.standard.format = ADSP_AUDIO_FORMAT_EVRC_FS;
+ rpc.format_block.standard.channels = 1;
+ rpc.format_block.standard.bits_per_sample = 16;
+ rpc.format_block.standard.sampling_rate = 8000;
+ rpc.format_block.standard.is_signed = 1;
+ rpc.format_block.standard.is_interleaved = 0;
+
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_READ;
+ rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT;
+ rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD;
+ rpc.buf_max_size = evrc->str_cfg.buffer_size;
+ rpc.config.evrc.min_rate = evrc->cfg.min_bit_rate;
+ rpc.config.evrc.max_rate = evrc->cfg.max_bit_rate;
+
+ q6audio_start(evrc->audio_client, &rpc, sizeof(rpc));
+ break;
+ case AUDIO_STOP:
+ break;
+ case AUDIO_FLUSH:
+ break;
+ case AUDIO_SET_VOLUME:
+ break;
+ case AUDIO_GET_STREAM_CONFIG:
+ if (copy_to_user((void *)arg, &evrc->str_cfg,
+ sizeof(struct msm_audio_stream_config)))
+ rc = -EFAULT;
+ break;
+ case AUDIO_SET_STREAM_CONFIG:
+ if (copy_from_user(&evrc->str_cfg, (void *)arg,
+ sizeof(struct msm_audio_stream_config))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ if (evrc->str_cfg.buffer_size < 23) {
+ pr_err("[%s:%s] Buffer size too small\n", __MM_FILE__,
+ __func__);
+ rc = -EINVAL;
+ break;
+ }
+
+ if (evrc->str_cfg.buffer_count != 2)
+ pr_info("[%s:%s] Buffer count set to 2\n", __MM_FILE__,
+ __func__);
+ break;
+ case AUDIO_SET_EVRC_ENC_CONFIG:
+ if (copy_from_user(&evrc->cfg, (void *) arg,
+ sizeof(struct msm_audio_evrc_enc_config)))
+ rc = -EFAULT;
+
+ if (evrc->cfg.min_bit_rate > 4 || evrc->cfg.min_bit_rate < 1) {
+ pr_err("[%s:%s] invalid min bitrate\n", __MM_FILE__,
+ __func__);
+ rc = -EINVAL;
+ }
+ if (evrc->cfg.max_bit_rate > 4 || evrc->cfg.max_bit_rate < 1) {
+ pr_err("[%s:%s] invalid max bitrate\n", __MM_FILE__,
+ __func__);
+ rc = -EINVAL;
+ }
+ break;
+ case AUDIO_GET_EVRC_ENC_CONFIG:
+ if (copy_to_user((void *) arg, &evrc->cfg,
+ sizeof(struct msm_audio_evrc_enc_config)))
+ rc = -EFAULT;
+ break;
+
+ default:
+ rc = -EINVAL;
+ }
+
+ mutex_unlock(&evrc->lock);
+ return rc;
+}
+
+static int q6_evrc_in_open(struct inode *inode, struct file *file)
+{
+ struct evrc *evrc;
+ evrc = kmalloc(sizeof(struct evrc), GFP_KERNEL);
+ if (evrc == NULL) {
+ pr_err("[%s:%s] Could not allocate memory for evrc driver\n",
+ __MM_FILE__, __func__);
+ return -ENOMEM;
+ }
+
+ mutex_init(&evrc->lock);
+ file->private_data = evrc;
+ evrc->audio_client = NULL;
+ evrc->str_cfg.buffer_size = 23;
+ evrc->str_cfg.buffer_count = 2;
+ evrc->cfg.cdma_rate = CDMA_RATE_FULL;
+ evrc->cfg.min_bit_rate = 1;
+ evrc->cfg.max_bit_rate = 4;
+
+ return 0;
+}
+
+static ssize_t q6_evrc_in_read(struct file *file, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio_client *ac;
+ struct audio_buffer *ab;
+ const char __user *start = buf;
+ struct evrc *evrc = file->private_data;
+ int xfer = 0;
+ int res;
+
+ mutex_lock(&evrc->lock);
+ ac = evrc->audio_client;
+ if (!ac) {
+ res = -ENODEV;
+ goto fail;
+ }
+ while (count > xfer) {
+ ab = ac->buf + ac->cpu_buf;
+
+ if (ab->used)
+ wait_event(ac->wait, (ab->used == 0));
+
+ xfer = ab->actual_size;
+
+ if (copy_to_user(buf, ab->data, xfer)) {
+ res = -EFAULT;
+ goto fail;
+ }
+
+ buf += xfer;
+ count -= xfer;
+
+ ab->used = 1;
+ q6audio_read(ac, ab);
+ ac->cpu_buf ^= 1;
+ }
+
+ res = buf - start;
+
+fail:
+ mutex_unlock(&evrc->lock);
+
+ return res;
+}
+
+static int q6_evrc_in_release(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+ struct evrc *evrc = file->private_data;
+
+ mutex_lock(&evrc->lock);
+ if (evrc->audio_client)
+ rc = q6audio_close(evrc->audio_client);
+ mutex_unlock(&evrc->lock);
+ kfree(evrc);
+ return rc;
+}
+
+static const struct file_operations q6_evrc_in_fops = {
+ .owner = THIS_MODULE,
+ .open = q6_evrc_in_open,
+ .read = q6_evrc_in_read,
+ .release = q6_evrc_in_release,
+ .unlocked_ioctl = q6_evrc_in_ioctl,
+};
+
+struct miscdevice q6_evrc_in_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_evrc_in",
+ .fops = &q6_evrc_in_fops,
+};
+
+static int __init q6_evrc_in_init(void)
+{
+ return misc_register(&q6_evrc_in_misc);
+}
+
+device_initcall(q6_evrc_in_init);
diff --git a/arch/arm/mach-msm/qdsp6/audiov2/mp3.c b/arch/arm/mach-msm/qdsp6/audiov2/mp3.c
new file mode 100644
index 0000000..0781eda
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/audiov2/mp3.c
@@ -0,0 +1,205 @@
+/* arch/arm/mach-msm/qdsp6/audiov2/mp3.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Copyright (C) 2009 HTC Corporation
+ * Copyright (c) 2009, 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/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+
+#include <linux/msm_audio.h>
+
+#include <mach/msm_qdsp6_audiov2.h>
+#include "dal_audio.h"
+#include "dal_audio_format.h"
+
+#define BUFSZ (8192)
+#define DMASZ (BUFSZ * 2)
+
+struct mp3 {
+ struct mutex lock;
+ struct audio_client *ac;
+ struct msm_audio_config cfg;
+};
+
+static long mp3_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct mp3 *mp3 = file->private_data;
+ struct adsp_open_command rpc;
+ int rc = 0;
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ memset(&stats, 0, sizeof(stats));
+ if (copy_to_user((void *) arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ }
+
+ mutex_lock(&mp3->lock);
+ switch (cmd) {
+ case AUDIO_SET_VOLUME:
+ break;
+ case AUDIO_START:
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_WRITE;
+ rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_PLAYBACK;
+ rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT;
+ rpc.format_block.standard.format = ADSP_AUDIO_FORMAT_MP3;
+ rpc.format_block.standard.channels = mp3->cfg.channel_count;
+ rpc.format_block.standard.bits_per_sample = 16;
+ rpc.format_block.standard.sampling_rate = mp3->cfg.sample_rate;
+ rpc.format_block.standard.is_signed = 1;
+ rpc.format_block.standard.is_interleaved = 0;
+ rpc.buf_max_size = BUFSZ;
+ q6audio_start(mp3->ac, (void *) &rpc, sizeof(rpc));
+ break;
+ case AUDIO_STOP:
+ break;
+ case AUDIO_FLUSH:
+ break;
+ case AUDIO_SET_CONFIG:
+ if (copy_from_user(&mp3->cfg, (void *) arg,
+ sizeof(struct msm_audio_config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (mp3->cfg.channel_count < 1 || mp3->cfg.channel_count > 2) {
+ rc = -EINVAL;
+ break;
+ }
+ break;
+ case AUDIO_GET_CONFIG:
+ if (copy_to_user((void *) arg, &mp3->cfg,
+ sizeof(struct msm_audio_config))) {
+ rc = -EFAULT;
+ }
+ break;
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&mp3->lock);
+ return rc;
+}
+
+static int mp3_open(struct inode *inode, struct file *file)
+{
+
+ struct mp3 *mp3;
+ mp3 = kzalloc(sizeof(struct mp3), GFP_KERNEL);
+
+ if (!mp3)
+ return -ENOMEM;
+
+ mutex_init(&mp3->lock);
+ file->private_data = mp3;
+ mp3->ac = q6audio_open(AUDIO_FLAG_WRITE, BUFSZ);
+ if (!mp3->ac) {
+ kfree(mp3);
+ return -ENOMEM;
+ }
+ mp3->cfg.channel_count = 2;
+ mp3->cfg.buffer_count = 2;
+ mp3->cfg.buffer_size = BUFSZ;
+ mp3->cfg.unused[0] = 0;
+ mp3->cfg.unused[1] = 0;
+ mp3->cfg.unused[2] = 0;
+ mp3->cfg.sample_rate = 48000;
+
+ return 0;
+}
+
+static ssize_t mp3_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct mp3 *mp3 = file->private_data;
+ struct audio_client *ac;
+ struct audio_buffer *ab;
+ const char __user *start = buf;
+ int xfer;
+
+ if (!mp3->ac)
+ mp3_ioctl(file, AUDIO_START, 0);
+
+ ac = mp3->ac;
+ if (!ac)
+ return -ENODEV;
+
+ while (count > 0) {
+ ab = ac->buf + ac->cpu_buf;
+
+ if (ab->used)
+ wait_event(ac->wait, (ab->used == 0));
+
+ xfer = count;
+ if (xfer > ab->size)
+ xfer = ab->size;
+
+ if (copy_from_user(ab->data, buf, xfer))
+ return -EFAULT;
+
+ buf += xfer;
+ count -= xfer;
+
+ ab->used = xfer;
+ q6audio_write(ac, ab);
+ ac->cpu_buf ^= 1;
+ }
+
+ return buf - start;
+}
+
+static int mp3_fsync(struct file *f, int datasync)
+{
+ struct mp3 *mp3 = f->private_data;
+ if (mp3->ac)
+ return q6audio_async(mp3->ac);
+ return -ENODEV;
+}
+
+static int mp3_release(struct inode *inode, struct file *file)
+{
+ struct mp3 *mp3 = file->private_data;
+ if (mp3->ac)
+ q6audio_close(mp3->ac);
+ kfree(mp3);
+ return 0;
+}
+
+static const struct file_operations mp3_fops = {
+ .owner = THIS_MODULE,
+ .open = mp3_open,
+ .write = mp3_write,
+ .fsync = mp3_fsync,
+ .release = mp3_release,
+ .unlocked_ioctl = mp3_ioctl,
+};
+
+struct miscdevice mp3_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_mp3",
+ .fops = &mp3_fops,
+};
+
+static int __init mp3_init(void)
+{
+ return misc_register(&mp3_misc);
+}
+
+device_initcall(mp3_init);
diff --git a/arch/arm/mach-msm/qdsp6/audiov2/pcm_in.c b/arch/arm/mach-msm/qdsp6/audiov2/pcm_in.c
new file mode 100644
index 0000000..6ef2195
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/audiov2/pcm_in.c
@@ -0,0 +1,208 @@
+/* arch/arm/mach-msm/qdsp6/audiov2/pcm_in.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Copyright (C) 2009 HTC Corporation
+ * Copyright (c) 2009, 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/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+
+#include <linux/msm_audio.h>
+
+#include <mach/msm_qdsp6_audiov2.h>
+#include "dal_audio.h"
+#include "dal_audio_format.h"
+
+#define BUFSZ (4096)
+#define DMASZ (BUFSZ * 2)
+
+
+struct pcm {
+ struct mutex lock;
+ struct msm_audio_config cfg;
+ struct audio_client *audio_client;
+};
+
+static long q6_in_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct pcm *pcm = file->private_data;
+ struct adsp_open_command rpc;
+ int rc = 0;
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ memset(&stats, 0, sizeof(stats));
+ if (copy_to_user((void *) arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ }
+
+ mutex_lock(&pcm->lock);
+ switch (cmd) {
+
+ case AUDIO_START:
+ tx_clk_freq = pcm->cfg.sample_rate;
+
+ memset(&rpc, 0, sizeof(rpc));
+
+ rpc.format_block.standard.format = ADSP_AUDIO_FORMAT_PCM;
+ rpc.format_block.standard.channels = pcm->cfg.channel_count;
+ rpc.format_block.standard.bits_per_sample = 16;
+ rpc.format_block.standard.sampling_rate = pcm->cfg.sample_rate;
+ rpc.format_block.standard.is_signed = 1;
+ rpc.format_block.standard.is_interleaved = 1;
+
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_READ;
+ rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT;
+ rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD;
+ rpc.buf_max_size = BUFSZ;
+ q6audio_start(pcm->audio_client, &rpc, sizeof(rpc));
+ break;
+ case AUDIO_STOP:
+ break;
+ case AUDIO_FLUSH:
+ break;
+ case AUDIO_SET_VOLUME:
+ break;
+ case AUDIO_SET_CONFIG:
+ if (copy_from_user(&pcm->cfg, (void *) arg,
+ sizeof(struct msm_audio_config))) {
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ case AUDIO_GET_CONFIG:
+ if (copy_to_user((void *) arg, &pcm->cfg,
+ sizeof(struct msm_audio_config))) {
+ rc = -EFAULT;
+ }
+ break;
+ default:
+ rc = -EINVAL;
+ }
+
+ mutex_unlock(&pcm->lock);
+ return rc;
+}
+
+static int q6_in_open(struct inode *inode, struct file *file)
+{
+
+ struct pcm *pcm;
+ pcm = kmalloc(sizeof(struct pcm), GFP_KERNEL);
+ if (pcm == NULL) {
+ pr_err("Could not allocate memory for pcm driver\n");
+ return -ENOMEM;
+ }
+ mutex_init(&pcm->lock);
+ file->private_data = pcm;
+ pcm->audio_client = q6audio_open(AUDIO_FLAG_READ, BUFSZ);
+ if (!pcm->audio_client) {
+ kfree(pcm);
+ return -ENOMEM;
+ }
+ pcm->cfg.channel_count = 1;
+ pcm->cfg.buffer_count = 2;
+ pcm->cfg.buffer_size = BUFSZ;
+ pcm->cfg.unused[0] = 0;
+ pcm->cfg.unused[1] = 0;
+ pcm->cfg.unused[2] = 0;
+ pcm->cfg.sample_rate = 8000;
+
+ return 0;
+}
+
+static ssize_t q6_in_read(struct file *file, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio_client *ac;
+ struct audio_buffer *ab;
+ const char __user *start = buf;
+ struct pcm *pcm = file->private_data;
+ int xfer;
+ int res;
+
+ mutex_lock(&pcm->lock);
+ ac = pcm->audio_client;
+ if (!ac) {
+ res = -ENODEV;
+ goto fail;
+ }
+ while (count > 0) {
+ ab = ac->buf + ac->cpu_buf;
+
+ if (ab->used)
+ wait_event(ac->wait, (ab->used == 0));
+
+ xfer = count;
+ if (xfer > ab->size)
+ xfer = ab->size;
+
+ if (copy_to_user(buf, ab->data, xfer)) {
+ res = -EFAULT;
+ goto fail;
+ }
+
+ buf += xfer;
+ count -= xfer;
+
+ ab->used = 1;
+ q6audio_read(ac, ab);
+ ac->cpu_buf ^= 1;
+ }
+fail:
+ res = buf - start;
+ mutex_unlock(&pcm->lock);
+
+ return res;
+}
+
+static int q6_in_release(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+ struct pcm *pcm = file->private_data;
+
+ mutex_lock(&pcm->lock);
+ if (pcm->audio_client)
+ rc = q6audio_close(pcm->audio_client);
+ mutex_unlock(&pcm->lock);
+ kfree(pcm);
+ return rc;
+}
+
+static const struct file_operations q6_in_fops = {
+ .owner = THIS_MODULE,
+ .open = q6_in_open,
+ .read = q6_in_read,
+ .release = q6_in_release,
+ .unlocked_ioctl = q6_in_ioctl,
+};
+
+struct miscdevice q6_in_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_pcm_in",
+ .fops = &q6_in_fops,
+};
+
+static int __init q6_in_init(void)
+{
+ return misc_register(&q6_in_misc);
+}
+
+device_initcall(q6_in_init);
diff --git a/arch/arm/mach-msm/qdsp6/audiov2/pcm_out.c b/arch/arm/mach-msm/qdsp6/audiov2/pcm_out.c
new file mode 100644
index 0000000..6743c6c
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/audiov2/pcm_out.c
@@ -0,0 +1,196 @@
+/* arch/arm/mach-msm/qdsp6/audiov2/pcm_out.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * Author: Brian Swetland <swetland@google.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/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+
+#include <linux/msm_audio.h>
+
+#include <mach/msm_qdsp6_audiov2.h>
+#include "dal_audio.h"
+#include "dal_audio_format.h"
+
+#define BUFSZ (8192)
+#define DMASZ (BUFSZ * 2)
+
+struct pcm {
+ struct mutex lock;
+ struct audio_client *ac;
+ struct msm_audio_config cfg;
+
+};
+
+static long pcm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct pcm *pcm = file->private_data;
+ struct adsp_open_command rpc;
+ int rc = 0;
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ memset(&stats, 0, sizeof(stats));
+ if (copy_to_user((void *) arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ }
+
+ mutex_lock(&pcm->lock);
+ switch (cmd) {
+ case AUDIO_START:
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_WRITE;
+ rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_PLAYBACK;
+ rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT;
+ rpc.format_block.standard.format = ADSP_AUDIO_FORMAT_PCM;
+ rpc.format_block.standard.channels = pcm->cfg.channel_count;
+ rpc.format_block.standard.bits_per_sample = 16;
+ rpc.format_block.standard.sampling_rate = pcm->cfg.sample_rate;
+ rpc.format_block.standard.is_signed = 1;
+ rpc.format_block.standard.is_interleaved = 1;
+ rpc.buf_max_size = BUFSZ;
+ q6audio_start(pcm->ac, (void *) &rpc, sizeof(rpc));
+ break;
+ case AUDIO_STOP:
+ break;
+ case AUDIO_FLUSH:
+ break;
+ case AUDIO_SET_CONFIG:
+ if (copy_from_user(&pcm->cfg, (void *) arg,
+ sizeof(struct msm_audio_config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (pcm->cfg.channel_count < 1 || pcm->cfg.channel_count > 2) {
+ rc = -EINVAL;
+ break;
+ }
+
+ break;
+ case AUDIO_GET_CONFIG:
+ if (copy_to_user((void *) arg, &pcm->cfg,
+ sizeof(struct msm_audio_config))) {
+ rc = -EFAULT;
+ }
+ break;
+
+ default:
+ rc = -EINVAL;
+ }
+
+ mutex_unlock(&pcm->lock);
+ return rc;
+}
+
+static int pcm_open(struct inode *inode, struct file *file)
+{
+ struct pcm *pcm;
+ pcm = kzalloc(sizeof(struct pcm), GFP_KERNEL);
+
+ if (!pcm)
+ return -ENOMEM;
+
+ mutex_init(&pcm->lock);
+ file->private_data = pcm;
+ pcm->ac = q6audio_open(AUDIO_FLAG_WRITE, BUFSZ);
+ if (!pcm->ac) {
+ kfree(pcm);
+ return -ENOMEM;
+ }
+ pcm->cfg.channel_count = 2;
+ pcm->cfg.buffer_count = 2;
+ pcm->cfg.buffer_size = BUFSZ;
+ pcm->cfg.unused[0] = 0;
+ pcm->cfg.unused[1] = 0;
+ pcm->cfg.unused[2] = 0;
+ pcm->cfg.sample_rate = 48000;
+
+ return 0;
+}
+
+static ssize_t pcm_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct pcm *pcm = file->private_data;
+ struct audio_client *ac;
+ struct audio_buffer *ab;
+ const char __user *start = buf;
+ int xfer;
+
+ ac = pcm->ac;
+ if (!ac)
+ return -ENODEV;
+
+ while (count > 0) {
+ ab = ac->buf + ac->cpu_buf;
+
+ if (ab->used)
+ wait_event(ac->wait, (ab->used == 0));
+
+ xfer = count;
+ if (xfer > ab->size)
+ xfer = ab->size;
+
+ if (copy_from_user(ab->data, buf, xfer))
+ return -EFAULT;
+
+ buf += xfer;
+ count -= xfer;
+
+ ab->used = 1;
+ ab->actual_size = xfer;
+ q6audio_write(ac, ab);
+ ac->cpu_buf ^= 1;
+ }
+
+ return buf - start;
+}
+
+static int pcm_release(struct inode *inode, struct file *file)
+{
+ struct pcm *pcm = file->private_data;
+ if (pcm->ac)
+ q6audio_close(pcm->ac);
+ kfree(pcm);
+ return 0;
+}
+
+static const struct file_operations pcm_fops = {
+ .owner = THIS_MODULE,
+ .open = pcm_open,
+ .write = pcm_write,
+ .release = pcm_release,
+ .unlocked_ioctl = pcm_ioctl,
+};
+
+struct miscdevice pcm_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_pcm_out",
+ .fops = &pcm_fops,
+};
+
+static int __init pcm_init(void)
+{
+ return misc_register(&pcm_misc);
+}
+
+device_initcall(pcm_init);
diff --git a/arch/arm/mach-msm/qdsp6/audiov2/q6audio.c b/arch/arm/mach-msm/qdsp6/audiov2/q6audio.c
new file mode 100644
index 0000000..f713e3d
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/audiov2/q6audio.c
@@ -0,0 +1,1312 @@
+/* arch/arm/mach-msm/qdsp6/audiov2/q6audio.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * Author: Brian Swetland <swetland@google.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/mutex.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+
+#include <linux/delay.h>
+#include <linux/wakelock.h>
+#include <linux/android_pmem.h>
+#include <linux/gpio.h>
+#include <mach/msm_qdsp6_audiov2.h>
+
+#include "../dal.h"
+#include "dal_audio.h"
+#include "dal_audio_format.h"
+#include "dal_acdb.h"
+#include "dal_adie.h"
+#include "q6audio_devices.h"
+
+struct q6_hw_info {
+ int min_gain;
+ int max_gain;
+};
+
+/* TODO: provide mechanism to configure from board file */
+
+static struct q6_hw_info q6_audio_hw[Q6_HW_COUNT] = {
+ [Q6_HW_HANDSET] = {
+ .min_gain = -2000,
+ .max_gain = 0,
+ },
+ [Q6_HW_HEADSET] = {
+ .min_gain = -2000,
+ .max_gain = 0,
+ },
+ [Q6_HW_SPEAKER] = {
+ .min_gain = -1500,
+ .max_gain = 0,
+ },
+ [Q6_HW_TTY] = {
+ .min_gain = -2000,
+ .max_gain = 0,
+ },
+ [Q6_HW_BT_SCO] = {
+ .min_gain = -2000,
+ .max_gain = 0,
+ },
+ [Q6_HW_BT_A2DP] = {
+ .min_gain = -2000,
+ .max_gain = 0,
+ },
+};
+
+static struct wake_lock idlelock;
+static int idlecount;
+static DEFINE_MUTEX(idlecount_lock);
+
+void audio_prevent_sleep(void)
+{
+ mutex_lock(&idlecount_lock);
+ if (++idlecount == 1)
+ wake_lock(&idlelock);
+ mutex_unlock(&idlecount_lock);
+}
+
+void audio_allow_sleep(void)
+{
+ mutex_lock(&idlecount_lock);
+ if (--idlecount == 0)
+ wake_unlock(&idlelock);
+ mutex_unlock(&idlecount_lock);
+}
+
+static struct clk *icodec_rx_clk;
+static struct clk *icodec_tx_clk;
+static struct clk *ecodec_clk;
+static struct clk *sdac_clk;
+
+static struct q6audio_analog_ops default_analog_ops;
+static struct q6audio_analog_ops *analog_ops = &default_analog_ops;
+uint32_t tx_clk_freq = 8000;
+static int tx_mute_status;
+
+void q6audio_register_analog_ops(struct q6audio_analog_ops *ops)
+{
+ analog_ops = ops;
+}
+
+static struct q6_device_info *q6_lookup_device(uint32_t device_id)
+{
+ struct q6_device_info *di = q6_audio_devices;
+ for (;;) {
+ if (di->id == device_id)
+ return di;
+ if (di->id == 0) {
+ pr_err("q6_lookup_device: bogus id 0x%08x\n",
+ device_id);
+ return di;
+ }
+ di++;
+ }
+}
+
+static uint32_t q6_device_to_codec(uint32_t device_id)
+{
+ struct q6_device_info *di = q6_lookup_device(device_id);
+ return di->codec;
+}
+
+static uint32_t q6_device_to_dir(uint32_t device_id)
+{
+ struct q6_device_info *di = q6_lookup_device(device_id);
+ return di->dir;
+}
+
+static uint32_t q6_device_to_cad_id(uint32_t device_id)
+{
+ struct q6_device_info *di = q6_lookup_device(device_id);
+ return di->cad_id;
+}
+
+static uint32_t q6_device_to_path(uint32_t device_id)
+{
+ struct q6_device_info *di = q6_lookup_device(device_id);
+ return di->path;
+}
+
+static uint32_t q6_device_to_rate(uint32_t device_id)
+{
+ struct q6_device_info *di = q6_lookup_device(device_id);
+ return di->rate;
+}
+
+int q6_device_volume(uint32_t device_id, int level)
+{
+ struct q6_device_info *di = q6_lookup_device(device_id);
+ struct q6_hw_info *hw;
+
+ hw = &q6_audio_hw[di->hw];
+
+ return hw->min_gain + ((hw->max_gain - hw->min_gain) * level) / 100;
+}
+
+static inline int adie_open(struct dal_client *client)
+{
+ return dal_call_f0(client, DAL_OP_OPEN, 0);
+}
+
+static inline int adie_close(struct dal_client *client)
+{
+ return dal_call_f0(client, DAL_OP_CLOSE, 0);
+}
+
+static inline int adie_set_path(struct dal_client *client,
+ uint32_t *adie_params, uint32_t size)
+{
+ uint32_t tmp;
+ return dal_call(client, ADIE_OP_SET_PATH, 5, adie_params, size,
+ (void *)&tmp, sizeof(uint32_t));
+
+}
+
+static inline int adie_proceed_to_stage(struct dal_client *client,
+ uint32_t path_type, uint32_t stage)
+{
+ return dal_call_f1(client, ADIE_OP_PROCEED_TO_STAGE,
+ path_type, stage);
+}
+
+static int adie_refcount;
+
+static struct dal_client *adie;
+static struct dal_client *adsp;
+static struct dal_client *acdb;
+
+static int adie_enable(void)
+{
+ adie_refcount++;
+ if (adie_refcount == 1)
+ adie_open(adie);
+ return 0;
+}
+
+static int adie_disable(void)
+{
+ adie_refcount--;
+ if (adie_refcount == 0)
+ adie_close(adie);
+ return 0;
+}
+
+/* 4k DMA scratch page used for exchanging acdb device config tables
+ * and stream format descriptions with the DSP.
+ */
+char *audio_data;
+int32_t audio_phys;
+
+#define SESSION_MIN 0
+#define SESSION_MAX 64
+
+static DEFINE_MUTEX(session_lock);
+static DEFINE_MUTEX(audio_lock);
+
+static struct audio_client *session[SESSION_MAX];
+
+static int session_alloc(struct audio_client *ac)
+{
+ int n;
+
+ mutex_lock(&session_lock);
+ for (n = SESSION_MIN; n < SESSION_MAX; n++) {
+ if (!session[n]) {
+ session[n] = ac;
+ mutex_unlock(&session_lock);
+ return n;
+ }
+ }
+ mutex_unlock(&session_lock);
+ return -ENOMEM;
+}
+
+static void session_free(int n, struct audio_client *ac)
+{
+ mutex_lock(&session_lock);
+ if (session[n] == ac)
+ session[n] = 0;
+ mutex_unlock(&session_lock);
+}
+
+static void audio_client_free(struct audio_client *ac)
+{
+ session_free(ac->session, ac);
+
+ if (ac->buf[0].data)
+ pmem_kfree(ac->buf[0].phys);
+ if (ac->buf[1].data)
+ pmem_kfree(ac->buf[1].phys);
+ kfree(ac);
+}
+
+static struct audio_client *audio_client_alloc(unsigned bufsz)
+{
+ struct audio_client *ac;
+ int n;
+
+ ac = kzalloc(sizeof(*ac), GFP_KERNEL);
+ if (!ac)
+ return 0;
+
+ n = session_alloc(ac);
+ if (n < 0)
+ goto fail_session;
+ ac->session = n;
+
+ if (bufsz > 0) {
+ ac->buf[0].phys = pmem_kalloc(bufsz,
+ PMEM_MEMTYPE_EBI1|PMEM_ALIGNMENT_4K);
+ ac->buf[0].data = ioremap(ac->buf[0].phys, bufsz);
+ if (!ac->buf[0].data)
+ goto fail;
+
+ ac->buf[1].phys = pmem_kalloc(bufsz,
+ PMEM_MEMTYPE_EBI1|PMEM_ALIGNMENT_4K);
+ ac->buf[1].data = ioremap(ac->buf[1].phys, bufsz);
+ if (!ac->buf[1].data)
+ goto fail;
+
+ ac->buf[0].size = bufsz;
+ ac->buf[1].size = bufsz;
+ }
+
+ init_waitqueue_head(&ac->wait);
+ ac->client = adsp;
+
+ return ac;
+
+fail:
+ pr_err("pmem_kalloc failed\n");
+ session_free(n, ac);
+fail_session:
+ audio_client_free(ac);
+ return 0;
+}
+
+static int audio_ioctl(struct audio_client *ac, void *ptr, uint32_t len)
+{
+ struct adsp_command_hdr *hdr = ptr;
+ uint32_t tmp;
+ int r;
+
+ hdr->size = len - sizeof(u32);
+ hdr->dest = AUDIO_ADDR(DOMAIN_DSP, ac->session, 0);
+ hdr->src = AUDIO_ADDR(DOMAIN_APP, ac->session, 0);
+ hdr->context = ac->session;
+ ac->cb_status = -EBUSY;
+ r = dal_call(ac->client, AUDIO_OP_CONTROL, 5, ptr, len,
+ &tmp, sizeof(tmp));
+ if (r != 4)
+ return -EIO;
+ wait_event(ac->wait, (ac->cb_status != -EBUSY));
+ return tmp;
+}
+
+static int audio_command(struct audio_client *ac, uint32_t cmd)
+{
+ struct adsp_command_hdr rpc;
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.opcode = cmd;
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+static int audio_open_control(struct audio_client *ac)
+{
+ struct adsp_open_command rpc;
+
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_DEVICE;
+ rpc.hdr.dest = AUDIO_ADDR(DOMAIN_DSP, ac->session, 0);
+ rpc.hdr.src = AUDIO_ADDR(DOMAIN_APP, ac->session, 0);
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+
+static int audio_close(struct audio_client *ac)
+{
+ audio_command(ac, ADSP_AUDIO_IOCTL_CMD_STREAM_STOP);
+ audio_command(ac, ADSP_AUDIO_IOCTL_CMD_CLOSE);
+ return 0;
+}
+
+static int audio_set_table(struct audio_client *ac,
+ uint32_t device_id, int size)
+{
+ struct adsp_set_dev_cfg_table_command rpc;
+
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_SET_DEVICE_CONFIG_TABLE;
+ rpc.hdr.dest = AUDIO_ADDR(DOMAIN_DSP, ac->session, 0);
+ rpc.hdr.src = AUDIO_ADDR(DOMAIN_APP, ac->session, 0);
+ rpc.device_id = device_id;
+ rpc.phys_addr = audio_phys;
+ rpc.phys_size = size;
+ rpc.phys_used = size;
+
+ if (q6_device_to_dir(device_id) == Q6_TX)
+ rpc.hdr.data = tx_clk_freq;
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+int q6audio_read(struct audio_client *ac, struct audio_buffer *ab)
+{
+ struct adsp_buffer_command rpc;
+ uint32_t res;
+ int r;
+
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.hdr.size = sizeof(rpc) - sizeof(u32);
+ rpc.hdr.dest = AUDIO_ADDR(DOMAIN_DSP, ac->session, 0);
+ rpc.hdr.src = AUDIO_ADDR(DOMAIN_APP, ac->session, 0);
+ rpc.hdr.context = ac->session;
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_DATA_TX;
+ rpc.buffer.addr = ab->phys;
+ rpc.buffer.max_size = ab->size;
+ rpc.buffer.actual_size = ab->actual_size;
+
+ r = dal_call(ac->client, AUDIO_OP_DATA, 5, &rpc, sizeof(rpc),
+ &res, sizeof(res));
+
+ if ((r == sizeof(res)))
+ return 0;
+
+ return -EIO;
+
+}
+
+int q6audio_write(struct audio_client *ac, struct audio_buffer *ab)
+{
+ struct adsp_buffer_command rpc;
+ uint32_t res;
+ int r;
+
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.hdr.size = sizeof(rpc) - sizeof(u32);
+ rpc.hdr.src = AUDIO_ADDR(DOMAIN_APP, ac->session, 0);
+ rpc.hdr.dest = AUDIO_ADDR(DOMAIN_DSP, ac->session, 0);
+ rpc.hdr.context = ac->session;
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_DATA_RX;
+ rpc.buffer.addr = ab->phys;
+ rpc.buffer.max_size = ab->size;
+ rpc.buffer.actual_size = ab->actual_size;
+
+ r = dal_call(ac->client, AUDIO_OP_DATA, 5, &rpc, sizeof(rpc),
+ &res, sizeof(res));
+ return 0;
+}
+
+static int audio_rx_volume(struct audio_client *ac, uint32_t dev_id,
+ int32_t volume)
+{
+ struct adsp_set_dev_volume_command rpc;
+
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_VOL;
+ rpc.hdr.dest = AUDIO_ADDR(DOMAIN_DSP, ac->session, 0);
+ rpc.hdr.src = AUDIO_ADDR(DOMAIN_APP, ac->session, 0);
+ rpc.device_id = dev_id;
+ rpc.path = ADSP_PATH_RX;
+ rpc.volume = volume;
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+static int audio_rx_mute(struct audio_client *ac, uint32_t dev_id, int mute)
+{
+ struct adsp_set_dev_mute_command rpc;
+
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_MUTE;
+ rpc.hdr.dest = AUDIO_ADDR(DOMAIN_DSP, ac->session, 0);
+ rpc.hdr.src = AUDIO_ADDR(DOMAIN_APP, ac->session, 0);
+ rpc.device_id = dev_id;
+ rpc.path = ADSP_PATH_RX;
+ rpc.mute = !!mute;
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+static int audio_tx_volume(struct audio_client *ac, uint32_t dev_id,
+ int32_t volume)
+{
+ struct adsp_set_dev_volume_command rpc;
+
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_VOL;
+ rpc.hdr.dest = AUDIO_ADDR(DOMAIN_DSP, ac->session, 0);
+ rpc.hdr.src = AUDIO_ADDR(DOMAIN_APP, ac->session, 0);
+ rpc.device_id = dev_id;
+ rpc.path = ADSP_PATH_TX;
+ rpc.volume = volume;
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+static int audio_tx_mute(struct audio_client *ac, uint32_t dev_id, int mute)
+{
+ struct adsp_set_dev_mute_command rpc;
+
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_MUTE;
+ rpc.hdr.dest = AUDIO_ADDR(DOMAIN_DSP, ac->session, 0);
+ rpc.hdr.src = AUDIO_ADDR(DOMAIN_APP, ac->session, 0);
+ rpc.device_id = dev_id;
+ rpc.path = ADSP_PATH_TX;
+ rpc.mute = !!mute;
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+static void callback(void *data, int len, void *cookie)
+{
+ struct adsp_event_hdr *e = data;
+ struct audio_client *ac;
+ struct adsp_buffer_event *abe = data;
+
+ if (e->context >= SESSION_MAX) {
+ pr_err("audio callback: bogus session %d\n",
+ e->context);
+ return;
+ }
+ ac = session[e->context];
+ if (!ac) {
+ pr_err("audio callback: unknown session %d\n",
+ e->context);
+ return;
+ }
+
+ if (e->event_id == ADSP_AUDIO_IOCTL_CMD_STREAM_EOS) {
+ pr_info("playback done\n");
+ if (e->status)
+ pr_err("playback status %d\n", e->status);
+ if (ac->cb_status == -EBUSY) {
+ ac->cb_status = e->status;
+ wake_up(&ac->wait);
+ }
+ return;
+ }
+
+ if (e->event_id == ADSP_AUDIO_EVT_STATUS_BUF_DONE) {
+ if (e->status)
+ pr_err("buffer status %d\n", e->status);
+
+ ac->buf[ac->dsp_buf].actual_size = abe->buffer.actual_size;
+ ac->buf[ac->dsp_buf].used = 0;
+ ac->dsp_buf ^= 1;
+ wake_up(&ac->wait);
+ return;
+ }
+
+ if (e->status)
+ pr_warning("audio_cb: s=%d e=%08x status=%d\n",
+ e->context, e->event_id, e->status);
+
+ if (ac->cb_status == -EBUSY) {
+ ac->cb_status = e->status;
+ wake_up(&ac->wait);
+ }
+}
+
+static void audio_init(struct dal_client *client)
+{
+ u32 tmp[3];
+
+ tmp[0] = 2 * sizeof(u32);
+ tmp[1] = 0;
+ tmp[2] = 0;
+ dal_call(client, AUDIO_OP_INIT, 5, tmp, sizeof(tmp),
+ tmp, sizeof(u32));
+}
+
+static struct audio_client *ac_control;
+
+static int q6audio_init(void)
+{
+ struct audio_client *ac = 0;
+ int res = -ENODEV;
+
+ mutex_lock(&audio_lock);
+ if (ac_control) {
+ res = 0;
+ goto done;
+ }
+
+ icodec_rx_clk = clk_get(0, "icodec_rx_clk");
+ icodec_tx_clk = clk_get(0, "icodec_tx_clk");
+ ecodec_clk = clk_get(0, "ecodec_clk");
+ sdac_clk = clk_get(0, "sdac_clk");
+
+ tx_mute_status = 0;
+ audio_phys = pmem_kalloc(4096, PMEM_MEMTYPE_EBI1|PMEM_ALIGNMENT_4K);
+ audio_data = ioremap(audio_phys, 4096);
+ if (!audio_data) {
+ pr_err("pmem kalloc failed\n");
+ res = -ENOMEM;
+ goto done;
+ }
+
+ adsp = dal_attach(AUDIO_DAL_DEVICE, AUDIO_DAL_PORT, 1,
+ callback, 0);
+ if (!adsp) {
+ pr_err("audio_init: cannot attach to adsp\n");
+ res = -ENODEV;
+ goto done;
+ }
+ if (check_version(adsp, AUDIO_DAL_VERSION) != 0) {
+ pr_err("Incompatible adsp version\n");
+ res = -ENODEV;
+ goto done;
+ }
+
+ audio_init(adsp);
+
+ ac = audio_client_alloc(0);
+ if (!ac) {
+ pr_err("audio_init: cannot allocate client\n");
+ res = -ENOMEM;
+ goto done;
+ }
+
+ if (audio_open_control(ac)) {
+ pr_err("audio_init: cannot open control channel\n");
+ res = -ENODEV;
+ goto done;
+ }
+
+ acdb = dal_attach(ACDB_DAL_DEVICE, ACDB_DAL_PORT, 0, 0, 0);
+ if (!acdb) {
+ pr_err("audio_init: cannot attach to acdb channel\n");
+ res = -ENODEV;
+ goto done;
+ }
+ if (check_version(acdb, ACDB_DAL_VERSION) != 0) {
+ pr_err("Incompatablie acdb version\n");
+ res = -ENODEV;
+ goto done;
+ }
+
+
+ adie = dal_attach(ADIE_DAL_DEVICE, ADIE_DAL_PORT, 0, 0, 0);
+ if (!adie) {
+ pr_err("audio_init: cannot attach to adie\n");
+ res = -ENODEV;
+ goto done;
+ }
+ if (check_version(adie, ADIE_DAL_VERSION) != 0) {
+ pr_err("Incompatablie adie version\n");
+ res = -ENODEV;
+ goto done;
+ }
+ if (analog_ops->init)
+ analog_ops->init();
+
+ res = 0;
+ ac_control = ac;
+
+ wake_lock_init(&idlelock, WAKE_LOCK_IDLE, "audio_pcm_idle");
+done:
+ if ((res < 0) && ac)
+ audio_client_free(ac);
+ mutex_unlock(&audio_lock);
+
+ return res;
+}
+
+static int acdb_get_config_table(uint32_t device_id, uint32_t sample_rate)
+{
+ struct acdb_cmd_device_table rpc;
+ struct acdb_result res;
+ int r;
+
+ if (q6audio_init())
+ return 0;
+
+ memset(audio_data, 0, 4096);
+ memset(&rpc, 0, sizeof(rpc));
+
+ rpc.size = sizeof(rpc) - (2 * sizeof(uint32_t));
+ rpc.command_id = ACDB_GET_DEVICE_TABLE;
+ rpc.device_id = q6_device_to_cad_id(device_id);
+ rpc.network_id = 0x00010023;
+ rpc.sample_rate_id = sample_rate;
+ rpc.total_bytes = 4096;
+ rpc.unmapped_buf = audio_phys;
+ rpc.res_size = sizeof(res) - (2 * sizeof(uint32_t));
+
+ r = dal_call(acdb, ACDB_OP_IOCTL, 8, &rpc, sizeof(rpc),
+ &res, sizeof(res));
+
+ if ((r == sizeof(res)) && (res.dal_status == 0))
+ return res.used_bytes;
+
+ return -EIO;
+}
+
+static uint32_t audio_rx_path_id = ADIE_PATH_HANDSET_RX;
+static uint32_t audio_rx_device_id = ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR;
+static uint32_t audio_rx_device_group = -1;
+static uint32_t audio_tx_path_id = ADIE_PATH_HANDSET_TX;
+static uint32_t audio_tx_device_id = ADSP_AUDIO_DEVICE_ID_HANDSET_MIC;
+static uint32_t audio_tx_device_group = -1;
+
+static int qdsp6_devchg_notify(struct audio_client *ac,
+ uint32_t dev_type, uint32_t dev_id)
+{
+ struct adsp_device_switch_command rpc;
+
+ if (dev_type != ADSP_AUDIO_RX_DEVICE &&
+ dev_type != ADSP_AUDIO_TX_DEVICE)
+ return -EINVAL;
+
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_PREPARE;
+ rpc.hdr.dest = AUDIO_ADDR(DOMAIN_DSP, ac->session, 0);
+ rpc.hdr.src = AUDIO_ADDR(DOMAIN_APP, ac->session, 0);
+
+ if (dev_type == ADSP_AUDIO_RX_DEVICE) {
+ rpc.old_device = audio_rx_device_id;
+ rpc.new_device = dev_id;
+ } else {
+ rpc.old_device = audio_tx_device_id;
+ rpc.new_device = dev_id;
+ }
+ rpc.device_class = 0;
+ rpc.device_type = dev_type;
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+static int qdsp6_standby(struct audio_client *ac)
+{
+ return audio_command(ac, ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_STANDBY);
+}
+
+static int qdsp6_start(struct audio_client *ac)
+{
+ return audio_command(ac, ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_COMMIT);
+}
+
+static void audio_rx_analog_enable(int en)
+{
+ switch (audio_rx_device_id) {
+ case ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_MONO:
+ case ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_STEREO:
+ case ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_SPKR:
+ if (analog_ops->headset_enable)
+ analog_ops->headset_enable(en);
+ break;
+ case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_MONO_HEADSET:
+ case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_STEREO_HEADSET:
+ case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_MONO_HEADSET:
+ case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_STEREO_HEADSET:
+ if (analog_ops->headset_enable)
+ analog_ops->headset_enable(en);
+ if (analog_ops->speaker_enable)
+ analog_ops->speaker_enable(en);
+ break;
+ case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO:
+ case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO:
+ if (analog_ops->speaker_enable)
+ analog_ops->speaker_enable(en);
+ break;
+ case ADSP_AUDIO_DEVICE_ID_BT_SCO_SPKR:
+ if (analog_ops->bt_sco_enable)
+ analog_ops->bt_sco_enable(en);
+ break;
+ }
+}
+
+static void audio_tx_analog_enable(int en)
+{
+ switch (audio_tx_device_id) {
+ case ADSP_AUDIO_DEVICE_ID_HANDSET_MIC:
+ case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MIC:
+ if (analog_ops->int_mic_enable)
+ analog_ops->int_mic_enable(en);
+ break;
+ case ADSP_AUDIO_DEVICE_ID_HEADSET_MIC:
+ case ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_MIC:
+ if (analog_ops->ext_mic_enable)
+ analog_ops->ext_mic_enable(en);
+ break;
+ case ADSP_AUDIO_DEVICE_ID_BT_SCO_MIC:
+ if (analog_ops->bt_sco_enable)
+ analog_ops->bt_sco_enable(en);
+ break;
+ }
+}
+
+static void _audio_rx_path_enable(void)
+{
+ uint32_t adev, sample_rate;
+ int sz;
+ uint32_t adie_params[5];
+
+ adev = audio_rx_device_id;
+ sample_rate = q6_device_to_rate(adev);
+
+ sz = acdb_get_config_table(adev, sample_rate);
+ audio_set_table(ac_control, adev, sz);
+
+ adie_params[0] = 4*sizeof(uint32_t);
+ adie_params[1] = audio_rx_path_id;
+ adie_params[2] = ADIE_PATH_RX;
+ adie_params[3] = 48000;
+ adie_params[4] = 256;
+ /*check for errors here*/
+ if (!adie_set_path(adie, adie_params, sizeof(adie_params)))
+ pr_err("adie set rx path failed\n");
+
+ adie_proceed_to_stage(adie, ADIE_PATH_RX,
+ ADIE_STAGE_DIGITAL_READY);
+ adie_proceed_to_stage(adie, ADIE_PATH_RX,
+ ADIE_STAGE_DIGITAL_ANALOG_READY);
+
+ audio_rx_analog_enable(1);
+
+ audio_rx_mute(ac_control, adev, 0);
+
+ audio_rx_volume(ac_control, adev, q6_device_volume(adev, 100));
+}
+
+static void _audio_tx_path_enable(void)
+{
+ uint32_t adev;
+ int sz;
+ uint32_t adie_params[5];
+
+ adev = audio_tx_device_id;
+
+ pr_info("audiolib: load %08x cfg table\n", adev);
+
+ if (tx_clk_freq > 16000) {
+ adie_params[3] = 48000;
+ sz = acdb_get_config_table(adev, 48000);
+
+ } else if (tx_clk_freq > 8000) {
+ adie_params[3] = 16000;
+ sz = acdb_get_config_table(adev, 16000);
+ } else {
+
+ adie_params[3] = 8000;
+ sz = acdb_get_config_table(adev, 8000);
+ }
+
+ pr_info("cfg table is %d bytes\n", sz);
+ audio_set_table(ac_control, adev, sz);
+
+ pr_info("audiolib: set adie tx path\n");
+
+ adie_params[0] = 4*sizeof(uint32_t);
+ adie_params[1] = audio_tx_path_id;
+ adie_params[2] = ADIE_PATH_TX;
+ adie_params[4] = 256;
+
+ if (!adie_set_path(adie, adie_params, sizeof(adie_params)))
+ pr_err("adie set tx path failed\n");
+
+ adie_proceed_to_stage(adie, ADIE_PATH_TX,
+ ADIE_STAGE_DIGITAL_READY);
+ adie_proceed_to_stage(adie, ADIE_PATH_TX,
+ ADIE_STAGE_DIGITAL_ANALOG_READY);
+
+ audio_tx_analog_enable(1);
+ audio_tx_mute(ac_control, adev, tx_mute_status);
+
+ if (!tx_mute_status)
+ audio_tx_volume(ac_control, adev, q6_device_volume(adev, 100));
+}
+
+static void _audio_rx_path_disable(void)
+{
+ audio_rx_analog_enable(0);
+
+ adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_ANALOG_OFF);
+ adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_DIGITAL_OFF);
+}
+
+static void _audio_tx_path_disable(void)
+{
+ audio_tx_analog_enable(0);
+
+ adie_proceed_to_stage(adie, ADIE_PATH_TX, ADIE_STAGE_ANALOG_OFF);
+ adie_proceed_to_stage(adie, ADIE_PATH_TX, ADIE_STAGE_DIGITAL_OFF);
+}
+
+static int icodec_rx_clk_refcount;
+static int icodec_tx_clk_refcount;
+static int ecodec_clk_refcount;
+static int sdac_clk_refcount;
+
+static void _audio_rx_clk_enable(void)
+{
+ uint32_t device_group = q6_device_to_codec(audio_rx_device_id);
+
+ switch (device_group) {
+ case Q6_ICODEC_RX:
+ icodec_rx_clk_refcount++;
+ if (icodec_rx_clk_refcount == 1) {
+ clk_set_rate(icodec_rx_clk, 12288000);
+ clk_enable(icodec_rx_clk);
+ }
+ break;
+ case Q6_ECODEC_RX:
+ ecodec_clk_refcount++;
+ if (ecodec_clk_refcount == 1) {
+ clk_set_rate(ecodec_clk, 2048000);
+ clk_enable(ecodec_clk);
+ }
+ break;
+ case Q6_SDAC_RX:
+ sdac_clk_refcount++;
+ if (sdac_clk_refcount == 1) {
+ clk_set_rate(sdac_clk, 12288000);
+ clk_enable(sdac_clk);
+ }
+ break;
+ default:
+ return;
+ }
+ audio_rx_device_group = device_group;
+}
+
+static void _audio_tx_clk_enable(void)
+{
+ uint32_t device_group = q6_device_to_codec(audio_tx_device_id);
+
+ switch (device_group) {
+ case Q6_ICODEC_TX:
+ icodec_tx_clk_refcount++;
+ if (icodec_tx_clk_refcount == 1) {
+ clk_set_rate(icodec_tx_clk, tx_clk_freq * 256);
+ clk_enable(icodec_tx_clk);
+ }
+ break;
+ case Q6_ECODEC_TX:
+ ecodec_clk_refcount++;
+ if (ecodec_clk_refcount == 1) {
+ clk_set_rate(ecodec_clk, 2048000);
+ clk_enable(ecodec_clk);
+ }
+ break;
+ case Q6_SDAC_TX:
+ /* TODO: In QCT BSP, clk rate was set to 20480000 */
+ sdac_clk_refcount++;
+ if (sdac_clk_refcount == 1) {
+ clk_set_rate(sdac_clk, 12288000);
+ clk_enable(sdac_clk);
+ }
+ break;
+ default:
+ return;
+ }
+ audio_tx_device_group = device_group;
+}
+
+static void _audio_rx_clk_disable(void)
+{
+ switch (audio_rx_device_group) {
+ case Q6_ICODEC_RX:
+ icodec_rx_clk_refcount--;
+ if (icodec_rx_clk_refcount == 0) {
+ clk_disable(icodec_rx_clk);
+ audio_rx_device_group = -1;
+ }
+ break;
+ case Q6_ECODEC_RX:
+ ecodec_clk_refcount--;
+ if (ecodec_clk_refcount == 0) {
+ clk_disable(ecodec_clk);
+ audio_rx_device_group = -1;
+ }
+ break;
+ case Q6_SDAC_RX:
+ sdac_clk_refcount--;
+ if (sdac_clk_refcount == 0) {
+ clk_disable(sdac_clk);
+ audio_rx_device_group = -1;
+ }
+ break;
+ default:
+ pr_err("audiolib: invalid rx device group %d\n",
+ audio_rx_device_group);
+ break;
+ }
+}
+
+static void _audio_tx_clk_disable(void)
+{
+ switch (audio_tx_device_group) {
+ case Q6_ICODEC_TX:
+ icodec_tx_clk_refcount--;
+ if (icodec_tx_clk_refcount == 0) {
+ clk_disable(icodec_tx_clk);
+ audio_tx_device_group = -1;
+ }
+ break;
+ case Q6_ECODEC_TX:
+ ecodec_clk_refcount--;
+ if (ecodec_clk_refcount == 0) {
+ clk_disable(ecodec_clk);
+ audio_tx_device_group = -1;
+ }
+ break;
+ case Q6_SDAC_TX:
+ sdac_clk_refcount--;
+ if (sdac_clk_refcount == 0) {
+ clk_disable(sdac_clk);
+ audio_tx_device_group = -1;
+ }
+ break;
+ default:
+ pr_err("audiolib: invalid tx device group %d\n",
+ audio_tx_device_group);
+ break;
+ }
+}
+
+static void _audio_rx_clk_reinit(uint32_t rx_device)
+{
+ uint32_t device_group = q6_device_to_codec(rx_device);
+
+ if (device_group != audio_rx_device_group)
+ _audio_rx_clk_disable();
+
+ audio_rx_device_id = rx_device;
+ audio_rx_path_id = q6_device_to_path(rx_device);
+
+ if (device_group != audio_rx_device_group)
+ _audio_rx_clk_enable();
+
+}
+
+static void _audio_tx_clk_reinit(uint32_t tx_device)
+{
+ uint32_t device_group = q6_device_to_codec(tx_device);
+
+ if (device_group != audio_tx_device_group)
+ _audio_tx_clk_disable();
+
+ audio_tx_device_id = tx_device;
+ audio_tx_path_id = q6_device_to_path(tx_device);
+
+ if (device_group != audio_tx_device_group)
+ _audio_tx_clk_enable();
+}
+
+static DEFINE_MUTEX(audio_path_lock);
+static int audio_rx_path_refcount;
+static int audio_tx_path_refcount;
+
+static int audio_rx_path_enable(int en)
+{
+ mutex_lock(&audio_path_lock);
+ if (en) {
+ audio_rx_path_refcount++;
+ if (audio_rx_path_refcount == 1) {
+ adie_enable();
+ _audio_rx_clk_enable();
+ _audio_rx_path_enable();
+ }
+ } else {
+ audio_rx_path_refcount--;
+ if (audio_rx_path_refcount == 0) {
+ _audio_rx_path_disable();
+ _audio_rx_clk_disable();
+ adie_disable();
+ }
+ }
+ mutex_unlock(&audio_path_lock);
+ return 0;
+}
+
+static int audio_tx_path_enable(int en)
+{
+ mutex_lock(&audio_path_lock);
+ if (en) {
+ audio_tx_path_refcount++;
+ if (audio_tx_path_refcount == 1) {
+ adie_enable();
+ _audio_tx_clk_enable();
+ _audio_tx_path_enable();
+ }
+ } else {
+ audio_tx_path_refcount--;
+ if (audio_tx_path_refcount == 0) {
+ _audio_tx_path_disable();
+ _audio_tx_clk_disable();
+ adie_disable();
+ }
+ }
+ mutex_unlock(&audio_path_lock);
+ return 0;
+}
+
+int q6audio_update_acdb(uint32_t id_src, uint32_t id_dst)
+{
+ mutex_lock(&audio_path_lock);
+ mutex_unlock(&audio_path_lock);
+ return 0;
+}
+
+int q6audio_set_tx_mute(int mute)
+{
+ uint32_t adev;
+ int rc;
+
+ if (q6audio_init())
+ return 0;
+
+ mutex_lock(&audio_path_lock);
+
+ if (mute == tx_mute_status) {
+ mutex_unlock(&audio_path_lock);
+ return 0;
+ }
+
+ adev = audio_tx_device_id;
+ rc = audio_tx_mute(ac_control, adev, mute);
+ if (!rc)
+ tx_mute_status = mute;
+ mutex_unlock(&audio_path_lock);
+ return 0;
+}
+
+int q6audio_set_rx_volume(int level)
+{
+ uint32_t adev;
+ int vol;
+
+ if (q6audio_init())
+ return 0;
+
+ if (level < 0 || level > 100)
+ return -EINVAL;
+
+ mutex_lock(&audio_path_lock);
+ adev = audio_rx_device_id;
+ vol = q6_device_volume(adev, level);
+ audio_rx_mute(ac_control, adev, 0);
+ audio_rx_volume(ac_control, adev, vol);
+ mutex_unlock(&audio_path_lock);
+ return 0;
+}
+
+static void do_rx_routing(uint32_t device_id)
+{
+ int sz;
+ uint32_t sample_rate;
+
+ if (device_id == audio_rx_device_id)
+ return;
+
+ if (audio_rx_path_refcount > 0) {
+ qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE,
+ device_id);
+ _audio_rx_path_disable();
+ _audio_rx_clk_reinit(device_id);
+ _audio_rx_path_enable();
+ } else {
+ sample_rate = q6_device_to_rate(device_id);
+ sz = acdb_get_config_table(device_id, sample_rate);
+ if (sz < 0)
+ pr_err("could not get ACDB config table\n");
+
+ audio_set_table(ac_control, device_id, sz);
+ qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE,
+ device_id);
+ qdsp6_standby(ac_control);
+ qdsp6_start(ac_control);
+ audio_rx_device_id = device_id;
+ audio_rx_path_id = q6_device_to_path(device_id);
+ }
+}
+
+static void do_tx_routing(uint32_t device_id)
+{
+ int sz;
+ uint32_t sample_rate;
+
+ if (device_id == audio_tx_device_id)
+ return;
+
+ if (audio_tx_path_refcount > 0) {
+ qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE,
+ device_id);
+ _audio_tx_path_disable();
+ _audio_tx_clk_reinit(device_id);
+ _audio_tx_path_enable();
+ } else {
+ sample_rate = q6_device_to_rate(device_id);
+ sz = acdb_get_config_table(device_id, sample_rate);
+ audio_set_table(ac_control, device_id, sz);
+ qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE,
+ device_id);
+ qdsp6_standby(ac_control);
+ qdsp6_start(ac_control);
+ audio_tx_device_id = device_id;
+ audio_tx_path_id = q6_device_to_path(device_id);
+ }
+}
+
+int q6audio_do_routing(uint32_t device_id)
+{
+ if (q6audio_init())
+ return 0;
+
+ mutex_lock(&audio_path_lock);
+
+ switch (q6_device_to_dir(device_id)) {
+ case Q6_RX:
+ do_rx_routing(device_id);
+ break;
+ case Q6_TX:
+ do_tx_routing(device_id);
+ break;
+ }
+
+ mutex_unlock(&audio_path_lock);
+ return 0;
+}
+
+int q6audio_set_route(const char *name)
+{
+ uint32_t route;
+ if (!strcmp(name, "speaker"))
+ route = ADIE_PATH_SPEAKER_STEREO_RX;
+ else if (!strcmp(name, "headphones"))
+ route = ADIE_PATH_HEADSET_STEREO_RX;
+ else if (!strcmp(name, "handset"))
+ route = ADIE_PATH_HANDSET_RX;
+ else
+ return -EINVAL;
+
+ mutex_lock(&audio_path_lock);
+ if (route == audio_rx_path_id)
+ goto done;
+
+ audio_rx_path_id = route;
+
+ if (audio_rx_path_refcount > 0) {
+ _audio_rx_path_disable();
+ _audio_rx_path_enable();
+ }
+ if (audio_tx_path_refcount > 0) {
+ _audio_tx_path_disable();
+ _audio_tx_path_enable();
+ }
+done:
+ mutex_unlock(&audio_path_lock);
+ return 0;
+}
+
+struct audio_client *q6audio_open(uint32_t flags, uint32_t bufsz)
+{
+ struct audio_client *ac;
+
+ if (q6audio_init())
+ return 0;
+
+ ac = audio_client_alloc(bufsz);
+ if (!ac)
+ return 0;
+
+ ac->flags = flags;
+ if (ac->flags & AUDIO_FLAG_WRITE)
+ audio_rx_path_enable(1);
+ else
+ audio_tx_path_enable(1);
+
+ return ac;
+}
+
+int q6audio_start(struct audio_client *ac, void *rpc,
+ uint32_t len)
+{
+
+ audio_ioctl(ac, rpc, len);
+
+ audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START);
+
+ if (!(ac->flags & AUDIO_FLAG_WRITE)) {
+ ac->buf[0].used = 1;
+ ac->buf[1].used = 1;
+ q6audio_read(ac, &ac->buf[0]);
+ q6audio_read(ac, &ac->buf[1]);
+ }
+
+ audio_prevent_sleep();
+ return 0;
+}
+
+int q6audio_close(struct audio_client *ac)
+{
+ audio_close(ac);
+
+ if (ac->flags & AUDIO_FLAG_WRITE)
+ audio_rx_path_enable(0);
+ else
+ audio_tx_path_enable(0);
+
+ audio_client_free(ac);
+ audio_allow_sleep();
+ return 0;
+}
+
+struct audio_client *q6voice_open(void)
+{
+ struct audio_client *ac;
+
+ if (q6audio_init())
+ return 0;
+
+ ac = audio_client_alloc(0);
+ if (!ac)
+ return 0;
+
+ return ac;
+}
+
+int q6voice_setup(void)
+{
+ audio_rx_path_enable(1);
+ tx_clk_freq = 8000;
+ audio_tx_path_enable(1);
+
+ return 0;
+}
+
+int q6voice_teardown(void)
+{
+ audio_rx_path_enable(0);
+ audio_tx_path_enable(0);
+ return 0;
+}
+
+
+int q6voice_close(struct audio_client *ac)
+{
+ audio_client_free(ac);
+ return 0;
+}
+
+int q6audio_async(struct audio_client *ac)
+{
+ struct adsp_command_hdr rpc;
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.opcode = ADSP_AUDIO_IOCTL_CMD_STREAM_EOS;
+ rpc.response_type = ADSP_AUDIO_RESPONSE_ASYNC;
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
diff --git a/arch/arm/mach-msm/qdsp6/audiov2/q6audio_devices.h b/arch/arm/mach-msm/qdsp6/audiov2/q6audio_devices.h
new file mode 100644
index 0000000..aa8a699
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/audiov2/q6audio_devices.h
@@ -0,0 +1,276 @@
+/* arch/arm/mach-msm/qdsp6/audiov2/q6audio_devices.h
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * Author: Brian Swetland <swetland@google.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.
+ *
+ */
+
+struct q6_device_info {
+ uint32_t id;
+ uint32_t cad_id;
+ uint32_t path;
+ uint32_t rate;
+ uint8_t dir;
+ uint8_t codec;
+ uint8_t hw;
+};
+
+#define Q6_ICODEC_RX 0
+#define Q6_ICODEC_TX 1
+#define Q6_ECODEC_RX 2
+#define Q6_ECODEC_TX 3
+#define Q6_SDAC_RX 6
+#define Q6_SDAC_TX 7
+#define Q6_CODEC_NONE 255
+
+#define Q6_TX 1
+#define Q6_RX 2
+#define Q6_TX_RX 3
+
+#define Q6_HW_HANDSET 0
+#define Q6_HW_HEADSET 1
+#define Q6_HW_SPEAKER 2
+#define Q6_HW_TTY 3
+#define Q6_HW_BT_SCO 4
+#define Q6_HW_BT_A2DP 5
+
+#define Q6_HW_COUNT 6
+
+#define CAD_HW_DEVICE_ID_HANDSET_MIC 0x01
+#define CAD_HW_DEVICE_ID_HANDSET_SPKR 0x02
+#define CAD_HW_DEVICE_ID_HEADSET_MIC 0x03
+#define CAD_HW_DEVICE_ID_HEADSET_SPKR_MONO 0x04
+#define CAD_HW_DEVICE_ID_HEADSET_SPKR_STEREO 0x05
+#define CAD_HW_DEVICE_ID_SPKR_PHONE_MIC 0x06
+#define CAD_HW_DEVICE_ID_SPKR_PHONE_MONO 0x07
+#define CAD_HW_DEVICE_ID_SPKR_PHONE_STEREO 0x08
+#define CAD_HW_DEVICE_ID_BT_SCO_MIC 0x09
+#define CAD_HW_DEVICE_ID_BT_SCO_SPKR 0x0A
+#define CAD_HW_DEVICE_ID_BT_A2DP_SPKR 0x0B
+#define CAD_HW_DEVICE_ID_TTY_HEADSET_MIC 0x0C
+#define CAD_HW_DEVICE_ID_TTY_HEADSET_SPKR 0x0D
+
+#define CAD_HW_DEVICE_ID_DEFAULT_TX 0x0E
+#define CAD_HW_DEVICE_ID_DEFAULT_RX 0x0F
+
+/* Logical Device to indicate A2DP routing */
+#define CAD_HW_DEVICE_ID_BT_A2DP_TX 0x10
+#define CAD_HW_DEVICE_ID_HEADSET_MONO_PLUS_SPKR_MONO_RX 0x11
+#define CAD_HW_DEVICE_ID_HEADSET_MONO_PLUS_SPKR_STEREO_RX 0x12
+#define CAD_HW_DEVICE_ID_HEADSET_STEREO_PLUS_SPKR_MONO_RX 0x13
+#define CAD_HW_DEVICE_ID_HEADSET_STEREO_PLUS_SPKR_STEREO_RX 0x14
+
+#define CAD_HW_DEVICE_ID_VOICE 0x15
+
+#define CAD_HW_DEVICE_ID_I2S_RX 0x20
+#define CAD_HW_DEVICE_ID_I2S_TX 0x21
+
+/* AUXPGA */
+#define CAD_HW_DEVICE_ID_HEADSET_SPKR_STEREO_LB 0x22
+#define CAD_HW_DEVICE_ID_HEADSET_SPKR_MONO_LB 0x23
+#define CAD_HW_DEVICE_ID_SPEAKER_SPKR_STEREO_LB 0x24
+#define CAD_HW_DEVICE_ID_SPEAKER_SPKR_MONO_LB 0x25
+
+#define CAD_HW_DEVICE_ID_NULL_RX 0x2A
+
+#define CAD_HW_DEVICE_ID_MAX_NUM 0x2F
+
+#define CAD_HW_DEVICE_ID_INVALID 0xFF
+
+#define CAD_RX_DEVICE 0x00
+#define CAD_TX_DEVICE 0x01
+
+static struct q6_device_info q6_audio_devices[] = {
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR,
+ .cad_id = CAD_HW_DEVICE_ID_HANDSET_SPKR,
+ .path = ADIE_PATH_HANDSET_RX,
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ICODEC_RX,
+ .hw = Q6_HW_HANDSET,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_MONO,
+ .cad_id = CAD_HW_DEVICE_ID_HEADSET_SPKR_MONO,
+ .path = ADIE_PATH_HEADSET_MONO_RX,
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ICODEC_RX,
+ .hw = Q6_HW_HEADSET,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_STEREO,
+ .cad_id = CAD_HW_DEVICE_ID_HEADSET_SPKR_STEREO,
+ .path = ADIE_PATH_HEADSET_STEREO_RX,
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ICODEC_RX,
+ .hw = Q6_HW_HEADSET,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO,
+ .cad_id = CAD_HW_DEVICE_ID_SPKR_PHONE_MONO,
+ .path = ADIE_PATH_SPEAKER_RX,
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ICODEC_RX,
+ .hw = Q6_HW_HEADSET,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO,
+ .cad_id = CAD_HW_DEVICE_ID_SPKR_PHONE_STEREO,
+ .path = ADIE_PATH_SPEAKER_STEREO_RX,
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ICODEC_RX,
+ .hw = Q6_HW_SPEAKER,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_MONO_HEADSET,
+ .cad_id = CAD_HW_DEVICE_ID_HEADSET_MONO_PLUS_SPKR_MONO_RX,
+ .path = ADIE_PATH_SPKR_MONO_HDPH_MONO_RX,
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ICODEC_RX,
+ .hw = Q6_HW_SPEAKER,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_STEREO_HEADSET,
+ .cad_id = CAD_HW_DEVICE_ID_HEADSET_MONO_PLUS_SPKR_STEREO_RX,
+ .path = ADIE_PATH_SPKR_MONO_HDPH_STEREO_RX,
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ICODEC_RX,
+ .hw = Q6_HW_SPEAKER,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_MONO_HEADSET,
+ .cad_id = CAD_HW_DEVICE_ID_HEADSET_STEREO_PLUS_SPKR_MONO_RX,
+ .path = ADIE_PATH_SPKR_STEREO_HDPH_MONO_RX,
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ICODEC_RX,
+ .hw = Q6_HW_SPEAKER,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_STEREO_HEADSET,
+ .cad_id = CAD_HW_DEVICE_ID_HEADSET_STEREO_PLUS_SPKR_STEREO_RX,
+ .path = ADIE_PATH_SPKR_STEREO_HDPH_STEREO_RX,
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ICODEC_RX,
+ .hw = Q6_HW_SPEAKER,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_SPKR,
+ .cad_id = CAD_HW_DEVICE_ID_TTY_HEADSET_SPKR,
+ .path = ADIE_PATH_TTY_HEADSET_RX,
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ICODEC_RX,
+ .hw = Q6_HW_TTY,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_HANDSET_MIC,
+ .cad_id = CAD_HW_DEVICE_ID_HANDSET_MIC,
+ .path = ADIE_PATH_HANDSET_TX,
+ .rate = 8000,
+ .dir = Q6_TX,
+ .codec = Q6_ICODEC_TX,
+ .hw = Q6_HW_HANDSET,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_HEADSET_MIC,
+ .cad_id = CAD_HW_DEVICE_ID_HEADSET_MIC,
+ .path = ADIE_PATH_HEADSET_MONO_TX,
+ .rate = 8000,
+ .dir = Q6_TX,
+ .codec = Q6_ICODEC_TX,
+ .hw = Q6_HW_HEADSET,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MIC,
+ .cad_id = CAD_HW_DEVICE_ID_SPKR_PHONE_MIC,
+ .path = ADIE_PATH_SPEAKER_TX,
+ .rate = 8000,
+ .dir = Q6_TX,
+ .codec = Q6_ICODEC_TX,
+ .hw = Q6_HW_SPEAKER,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_MIC,
+ .cad_id = CAD_HW_DEVICE_ID_TTY_HEADSET_MIC,
+ .path = ADIE_PATH_TTY_HEADSET_TX,
+ .rate = 8000,
+ .dir = Q6_TX,
+ .codec = Q6_ICODEC_TX,
+ .hw = Q6_HW_HEADSET,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_BT_SCO_SPKR,
+ .cad_id = CAD_HW_DEVICE_ID_BT_SCO_SPKR,
+ .path = 0, /* XXX */
+ .rate = 8000,
+ .dir = Q6_RX,
+ .codec = Q6_ECODEC_RX,
+ .hw = Q6_HW_BT_SCO,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_BT_A2DP_SPKR,
+ .cad_id = CAD_HW_DEVICE_ID_BT_A2DP_SPKR,
+ .path = 0, /* XXX */
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ECODEC_RX,
+ .hw = Q6_HW_BT_A2DP,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_BT_SCO_MIC,
+ .cad_id = CAD_HW_DEVICE_ID_BT_SCO_MIC,
+ .path = 0, /* XXX */
+ .rate = 8000,
+ .dir = Q6_TX,
+ .codec = Q6_ECODEC_TX,
+ .hw = Q6_HW_BT_SCO,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_I2S_SPKR,
+ .cad_id = CAD_HW_DEVICE_ID_I2S_RX,
+ .path = 0, /* XXX */
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_SDAC_RX,
+ .hw = Q6_HW_SPEAKER,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_I2S_MIC,
+ .cad_id = CAD_HW_DEVICE_ID_I2S_TX,
+ .path = 0, /* XXX */
+ .rate = 16000,
+ .dir = Q6_TX,
+ .codec = Q6_SDAC_TX,
+ .hw = Q6_HW_SPEAKER,
+ },
+ {
+ .id = 0,
+ .cad_id = 0,
+ .path = 0,
+ .rate = 8000,
+ .dir = 0,
+ .codec = Q6_CODEC_NONE,
+ .hw = 0,
+ },
+};
+
diff --git a/arch/arm/mach-msm/qdsp6/audiov2/qcelp_in.c b/arch/arm/mach-msm/qdsp6/audiov2/qcelp_in.c
new file mode 100644
index 0000000..a13084f
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/audiov2/qcelp_in.c
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2009 Google, Inc.
+ * Copyright (C) 2009 HTC Corporation
+ * Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * Author: Brian Swetland <swetland@google.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/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+
+#include <linux/msm_audio_qcp.h>
+#include <mach/msm_qdsp6_audiov2.h>
+#include "dal_audio.h"
+#include "dal_audio_format.h"
+#include <mach/debug_mm.h>
+
+
+struct qcelp {
+ struct mutex lock;
+ struct msm_audio_qcelp_enc_config cfg;
+ struct msm_audio_stream_config str_cfg;
+ struct audio_client *audio_client;
+};
+
+
+static long q6_qcelp_in_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct qcelp *qcelp = file->private_data;
+ struct adsp_open_command rpc;
+ int rc = 0;
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ memset(&stats, 0, sizeof(stats));
+ if (copy_to_user((void *) arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ }
+
+ mutex_lock(&qcelp->lock);
+ switch (cmd) {
+ case AUDIO_START:
+ if (qcelp->audio_client) {
+ rc = -EBUSY;
+ break;
+ } else {
+ qcelp->audio_client = q6audio_open(AUDIO_FLAG_READ,
+ qcelp->str_cfg.buffer_size);
+
+ if (!qcelp->audio_client) {
+ kfree(qcelp);
+ rc = -ENOMEM;
+ break;
+ }
+ }
+
+ tx_clk_freq = 8000;
+
+ memset(&rpc, 0, sizeof(rpc));
+
+ rpc.format_block.standard.format = ADSP_AUDIO_FORMAT_V13K_FS;
+ rpc.format_block.standard.channels = 1;
+ rpc.format_block.standard.bits_per_sample = 16;
+ rpc.format_block.standard.sampling_rate = 8000;
+ rpc.format_block.standard.is_signed = 1;
+ rpc.format_block.standard.is_interleaved = 0;
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_READ;
+ rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT;
+ rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD;
+ rpc.buf_max_size = qcelp->str_cfg.buffer_size;
+ rpc.config.qcelp13k.min_rate = qcelp->cfg.min_bit_rate;
+ rpc.config.qcelp13k.max_rate = qcelp->cfg.max_bit_rate;
+
+ q6audio_start(qcelp->audio_client, &rpc, sizeof(rpc));
+ break;
+ case AUDIO_STOP:
+ break;
+ case AUDIO_FLUSH:
+ break;
+ case AUDIO_SET_VOLUME:
+ break;
+ case AUDIO_GET_STREAM_CONFIG:
+ if (copy_to_user((void *)arg, &qcelp->str_cfg,
+ sizeof(struct msm_audio_stream_config)))
+ rc = -EFAULT;
+ break;
+ case AUDIO_SET_STREAM_CONFIG:
+ if (copy_from_user(&qcelp->str_cfg, (void *)arg,
+ sizeof(struct msm_audio_stream_config))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ if (qcelp->str_cfg.buffer_size < 35) {
+ pr_err("[%s:%s] Buffer size too small\n", __MM_FILE__,
+ __func__);
+ rc = -EINVAL;
+ break;
+ }
+
+ if (qcelp->str_cfg.buffer_count != 2)
+ pr_info("[%s:%s] Buffer count set to 2\n", __MM_FILE__,
+ __func__);
+ break;
+ case AUDIO_SET_QCELP_ENC_CONFIG:
+ if (copy_from_user(&qcelp->cfg, (void *) arg,
+ sizeof(struct msm_audio_qcelp_enc_config)))
+ rc = -EFAULT;
+
+ if (qcelp->cfg.min_bit_rate > 4 ||
+ qcelp->cfg.min_bit_rate < 1) {
+
+ pr_err("[%s:%s] invalid min bitrate\n", __MM_FILE__,
+ __func__);
+ rc = -EINVAL;
+ }
+ if (qcelp->cfg.max_bit_rate > 4 ||
+ qcelp->cfg.max_bit_rate < 1) {
+
+ pr_err("[%s:%s] invalid max bitrate\n", __MM_FILE__,
+ __func__);
+ rc = -EINVAL;
+ }
+
+ break;
+ case AUDIO_GET_QCELP_ENC_CONFIG:
+ if (copy_to_user((void *) arg, &qcelp->cfg,
+ sizeof(struct msm_audio_qcelp_enc_config)))
+ rc = -EFAULT;
+ break;
+
+ default:
+ rc = -EINVAL;
+ }
+
+ mutex_unlock(&qcelp->lock);
+ return rc;
+}
+
+static int q6_qcelp_in_open(struct inode *inode, struct file *file)
+{
+ struct qcelp *qcelp;
+ qcelp = kmalloc(sizeof(struct qcelp), GFP_KERNEL);
+ if (qcelp == NULL) {
+ pr_err("[%s:%s] Could not allocate memory for qcelp driver\n",
+ __MM_FILE__, __func__);
+ return -ENOMEM;
+ }
+
+ mutex_init(&qcelp->lock);
+ file->private_data = qcelp;
+ qcelp->audio_client = NULL;
+ qcelp->str_cfg.buffer_size = 35;
+ qcelp->str_cfg.buffer_count = 2;
+ qcelp->cfg.cdma_rate = CDMA_RATE_FULL;
+ qcelp->cfg.min_bit_rate = 1;
+ qcelp->cfg.max_bit_rate = 4;
+ return 0;
+}
+
+static ssize_t q6_qcelp_in_read(struct file *file, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio_client *ac;
+ struct audio_buffer *ab;
+ const char __user *start = buf;
+ struct qcelp *qcelp = file->private_data;
+ int xfer = 0;
+ int res;
+
+ mutex_lock(&qcelp->lock);
+ ac = qcelp->audio_client;
+ if (!ac) {
+ res = -ENODEV;
+ goto fail;
+ }
+ while (count > xfer) {
+ ab = ac->buf + ac->cpu_buf;
+
+ if (ab->used)
+ wait_event(ac->wait, (ab->used == 0));
+
+ xfer = ab->actual_size;
+
+ if (copy_to_user(buf, ab->data, xfer)) {
+ res = -EFAULT;
+ goto fail;
+ }
+
+ buf += xfer;
+ count -= xfer;
+
+ ab->used = 1;
+ q6audio_read(ac, ab);
+ ac->cpu_buf ^= 1;
+ }
+
+ res = buf - start;
+
+fail:
+ mutex_unlock(&qcelp->lock);
+
+ return res;
+}
+
+static int q6_qcelp_in_release(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+ struct qcelp *qcelp = file->private_data;
+
+ mutex_lock(&qcelp->lock);
+ if (qcelp->audio_client)
+ rc = q6audio_close(qcelp->audio_client);
+ mutex_unlock(&qcelp->lock);
+ kfree(qcelp);
+ return rc;
+}
+
+static const struct file_operations q6_qcelp_in_fops = {
+ .owner = THIS_MODULE,
+ .open = q6_qcelp_in_open,
+ .read = q6_qcelp_in_read,
+ .release = q6_qcelp_in_release,
+ .unlocked_ioctl = q6_qcelp_in_ioctl,
+};
+
+struct miscdevice q6_qcelp_in_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_qcelp_in",
+ .fops = &q6_qcelp_in_fops,
+};
+
+static int __init q6_qcelp_in_init(void)
+{
+ return misc_register(&q6_qcelp_in_misc);
+}
+
+device_initcall(q6_qcelp_in_init);
diff --git a/arch/arm/mach-msm/qdsp6/audiov2/routing.c b/arch/arm/mach-msm/qdsp6/audiov2/routing.c
new file mode 100644
index 0000000..1a2476b
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/audiov2/routing.c
@@ -0,0 +1,73 @@
+/* arch/arm/mach-msm/qdsp6/audiov2/routing.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * Author: Brian Swetland <swetland@google.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/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <mach/msm_qdsp6_audiov2.h>
+
+static int q6_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static ssize_t q6_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ char cmd[32];
+
+ if (count >= sizeof(cmd))
+ return -EINVAL;
+ if (copy_from_user(cmd, buf, count))
+ return -EFAULT;
+ cmd[count] = 0;
+
+ if ((count > 1) && (cmd[count-1] == '\n'))
+ cmd[count-1] = 0;
+
+ q6audio_set_route(cmd);
+
+ return count;
+}
+
+static int q6_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static const struct file_operations q6_fops = {
+ .owner = THIS_MODULE,
+ .open = q6_open,
+ .write = q6_write,
+ .release = q6_release,
+};
+
+static struct miscdevice q6_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_audio_route",
+ .fops = &q6_fops,
+};
+
+
+static int __init q6_init(void)
+{
+ return misc_register(&q6_misc);
+}
+
+device_initcall(q6_init);
diff --git a/arch/arm/mach-msm/qdsp6/audiov2/voice.c b/arch/arm/mach-msm/qdsp6/audiov2/voice.c
new file mode 100644
index 0000000..906c534
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/audiov2/voice.c
@@ -0,0 +1,188 @@
+/* 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/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/kthread.h>
+#include <linux/completion.h>
+#include <linux/wait.h>
+#include <mach/msm_qdsp6_audiov2.h>
+#include "../dal.h"
+#include "dal_voice.h"
+#include <mach/debug_mm.h>
+
+struct voice_struct {
+ struct dal_client *cvd;
+ struct apr_command_pkt apr_pkt;
+ struct completion compl;
+};
+
+static struct voice_struct voice;
+
+static int cvd_send_response(void)
+{
+ struct apr_command_pkt *pkt;
+ uint16_t src_addr;
+ uint16_t src_token;
+ uint16_t dst_token;
+ uint16_t dst_addr;
+
+ pkt = &voice.apr_pkt;
+ src_addr = pkt->dst_addr;
+ dst_addr = pkt->src_addr;
+ src_token = pkt->dst_token;
+ dst_token = pkt->src_token;
+
+ pkt->header &= ~APR_PKTV1_TYPE_MASK;
+ pkt->header |= APR_SET_FIELD(APR_PKTV1_TYPE, APR_PKTV1_TYPE_EVENT_V);
+ pkt->src_addr = src_addr;
+ pkt->dst_addr = dst_addr;
+ pkt->src_token = src_token;
+ pkt->dst_token = dst_token;
+ pkt->opcode = APR_IBASIC_RSP_RESULT;
+
+ dal_call(voice.cvd, VOICE_OP_CONTROL, 5, pkt,
+ sizeof(struct apr_command_pkt),
+ pkt, sizeof(u32));
+ return 0;
+}
+
+static int cvd_process_voice_setup(void)
+{
+ q6voice_setup();
+ cvd_send_response();
+ return 0;
+}
+
+static int cvd_process_voice_teardown(void)
+{
+ q6voice_teardown();
+ cvd_send_response();
+ return 0;
+}
+
+static int cvd_process_set_network(void)
+{
+ cvd_send_response();
+ return 0;
+}
+
+static int voice_thread(void *data)
+{
+ while (!kthread_should_stop()) {
+ wait_for_completion(&voice.compl);
+ init_completion(&voice.compl);
+
+ switch (voice.apr_pkt.opcode) {
+
+ case APR_OP_CMD_CREATE:
+ cvd_send_response();
+ break;
+ case VOICE_OP_CMD_BRINGUP:
+ cvd_process_voice_setup();
+ break;
+ case APR_OP_CMD_DESTROY:
+ cvd_send_response();
+ break;
+ case VOICE_OP_CMD_TEARDOWN:
+ cvd_process_voice_teardown();
+ break;
+ case VOICE_OP_CMD_SET_NETWORK:
+ cvd_process_set_network();
+ break;
+ default:
+ pr_err("[%s:%s] Undefined event\n", __MM_FILE__,
+ __func__);
+
+ }
+ }
+ return 0;
+}
+
+static void remote_cb_function(void *data, int len, void *cookie)
+{
+ struct apr_command_pkt *apr = data + 2*sizeof(uint32_t);
+
+ memcpy(&voice.apr_pkt, apr, sizeof(struct apr_command_pkt));
+
+ if (len <= 0) {
+ pr_err("[%s:%s] unexpected event with length %d\n",
+ __MM_FILE__, __func__, len);
+ return;
+ }
+
+ pr_debug("[%s:%s] APR = %x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n", __MM_FILE__,
+ __func__,
+ apr->header,
+ apr->reserved1,
+ apr->src_addr,
+ apr->dst_addr,
+ apr->ret_addr,
+ apr->src_token,
+ apr->dst_token,
+ apr->ret_token,
+ apr->context,
+ apr->opcode);
+
+ complete(&voice.compl);
+}
+
+static int __init voice_init(void)
+{
+ int res = 0;
+ struct task_struct *task;
+ u32 tmp[2];
+
+ tmp[0] = sizeof(u32);
+ tmp[1] = 0;
+
+ voice.cvd = dal_attach(VOICE_DAL_DEVICE, VOICE_DAL_PORT, 0,
+ remote_cb_function, 0);
+
+ if (!voice.cvd) {
+ pr_err("[%s:%s] audio_init: cannot attach to cvd\n",
+ __MM_FILE__, __func__);
+ res = -ENODEV;
+ goto done;
+ }
+
+ if (check_version(voice.cvd, VOICE_DAL_VERSION) != 0) {
+ pr_err("[%s:%s] Incompatible cvd version\n",
+ __MM_FILE__, __func__);
+ res = -ENODEV;
+ goto done;
+ }
+ dal_call(voice.cvd, VOICE_OP_INIT, 5, tmp, sizeof(tmp),
+ tmp, sizeof(u32));
+
+ init_completion(&voice.compl);
+ task = kthread_run(voice_thread, &voice, "voice_thread");
+
+ if (IS_ERR(task)) {
+ pr_err("[%s:%s] Cannot start the voice thread\n", __MM_FILE__,
+ __func__);
+ res = PTR_ERR(task);
+ task = NULL;
+ } else
+ goto done;
+
+done:
+ return res;
+}
+
+late_initcall(voice_init);
diff --git a/arch/arm/mach-msm/qdsp6/auxpcm_lb_in.c b/arch/arm/mach-msm/qdsp6/auxpcm_lb_in.c
new file mode 100644
index 0000000..4195454
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/auxpcm_lb_in.c
@@ -0,0 +1,190 @@
+/* arch/arm/mach-msm/qdsp6/auxpcm_lb_in.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Copyright (C) 2009 HTC Corporation
+ * Copyright (c) 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/slab.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+
+#include <linux/msm_audio.h>
+
+#include <mach/msm_qdsp6_audio.h>
+#include <mach/debug_mm.h>
+
+struct auxpcm {
+ struct mutex lock;
+ struct audio_client *ac;
+ uint32_t sample_rate;
+ uint32_t channel_count;
+ int opened;;
+};
+
+static long auxpcmin_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct auxpcm *auxpcmin = file->private_data;
+ int rc = 0;
+
+ mutex_lock(&auxpcmin->lock);
+ switch (cmd) {
+ case AUDIO_START: {
+ uint32_t acdb_id;
+ pr_debug("[%s:%s] AUDIO_START\n", __MM_FILE__, __func__);
+ if (arg == 0) {
+ acdb_id = 0;
+ } else if (copy_from_user(&acdb_id, (void *) arg,
+ sizeof(acdb_id))) {
+ pr_info("[%s:%s] copy acdb_id from user failed\n",
+ __MM_FILE__, __func__);
+ rc = -EFAULT;
+ break;
+ }
+ if (auxpcmin->ac) {
+ pr_err("[%s:%s] active session already existing\n",
+ __MM_FILE__, __func__);
+ rc = -EBUSY;
+ } else {
+ auxpcmin->ac =
+ q6audio_open_auxpcm(auxpcmin->sample_rate,
+ auxpcmin->channel_count,
+ AUDIO_FLAG_READ, acdb_id);
+ if (!auxpcmin->ac) {
+ pr_err("[%s:%s] auxpcm open session failed\n",
+ __MM_FILE__, __func__);
+ rc = -ENOMEM;
+ }
+ }
+ break;
+ }
+ case AUDIO_STOP:
+ pr_debug("[%s:%s] AUDIO_STOP\n", __MM_FILE__, __func__);
+ break;
+ case AUDIO_FLUSH:
+ break;
+ case AUDIO_SET_CONFIG: {
+ struct msm_audio_config config;
+ if (auxpcmin->ac) {
+ rc = -EBUSY;
+ pr_err("[%s:%s] active session already existing\n",
+ __MM_FILE__, __func__);
+ break;
+ }
+ if (copy_from_user(&config, (void *) arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ pr_debug("[%s:%s] SET_CONFIG: samplerate = %d, channels = %d\n",
+ __MM_FILE__, __func__, config.sample_rate,
+ config.channel_count);
+ if (config.channel_count != 1) {
+ rc = -EINVAL;
+ pr_err("[%s:%s] invalid channelcount %d\n",
+ __MM_FILE__, __func__, config.channel_count);
+ break;
+ }
+ if (config.sample_rate != 8000) {
+ rc = -EINVAL;
+ pr_err("[%s:%s] invalid samplerate %d\n", __MM_FILE__,
+ __func__, config.sample_rate);
+ break;
+ }
+ auxpcmin->sample_rate = config.sample_rate;
+ auxpcmin->channel_count = config.channel_count;
+ break;
+ }
+ case AUDIO_GET_CONFIG: {
+ struct msm_audio_config config;
+ config.buffer_size = 0;
+ config.buffer_count = 0;
+ config.sample_rate = auxpcmin->sample_rate;
+ config.channel_count = auxpcmin->channel_count;
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+ config.unused[2] = 0;
+ if (copy_to_user((void *) arg, &config, sizeof(config)))
+ rc = -EFAULT;
+ pr_debug("[%s:%s] GET_CONFIG: samplerate = %d, channels = %d\n",
+ __MM_FILE__, __func__, config.sample_rate,
+ config.channel_count);
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&auxpcmin->lock);
+ pr_debug("[%s:%s] rc = %d\n", __MM_FILE__, __func__, rc);
+ return rc;
+}
+
+static struct auxpcm the_auxpcmin;
+
+static int auxpcmin_open(struct inode *inode, struct file *file)
+{
+ struct auxpcm *auxpcmin = &the_auxpcmin;
+
+ pr_info("[%s:%s] open\n", __MM_FILE__, __func__);
+ mutex_lock(&auxpcmin->lock);
+ if (auxpcmin->opened) {
+ pr_err("aux pcm loopback tx already open!\n");
+ mutex_unlock(&auxpcmin->lock);
+ return -EBUSY;
+ }
+ auxpcmin->channel_count = 1;
+ auxpcmin->sample_rate = 8000;
+ auxpcmin->opened = 1;
+ file->private_data = auxpcmin;
+ mutex_unlock(&auxpcmin->lock);
+ return 0;
+}
+
+static int auxpcmin_release(struct inode *inode, struct file *file)
+{
+ struct auxpcm *auxpcmin = file->private_data;
+ mutex_lock(&auxpcmin->lock);
+ if (auxpcmin->ac)
+ q6audio_auxpcm_close(auxpcmin->ac);
+ auxpcmin->ac = NULL;
+ auxpcmin->opened = 0;
+ mutex_unlock(&auxpcmin->lock);
+ pr_info("[%s:%s] release\n", __MM_FILE__, __func__);
+ return 0;
+}
+
+static const struct file_operations auxpcmin_fops = {
+ .owner = THIS_MODULE,
+ .open = auxpcmin_open,
+ .release = auxpcmin_release,
+ .unlocked_ioctl = auxpcmin_ioctl,
+};
+
+struct miscdevice auxpcmin_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_aux_pcm_lb_in",
+ .fops = &auxpcmin_fops,
+};
+
+static int __init auxpcmin_init(void)
+{
+ mutex_init(&the_auxpcmin.lock);
+ return misc_register(&auxpcmin_misc);
+}
+
+device_initcall(auxpcmin_init);
diff --git a/arch/arm/mach-msm/qdsp6/auxpcm_lb_out.c b/arch/arm/mach-msm/qdsp6/auxpcm_lb_out.c
new file mode 100644
index 0000000..b680597
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/auxpcm_lb_out.c
@@ -0,0 +1,191 @@
+/* arch/arm/mach-msm/qdsp6/auxpcm_lb_out.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Author: Brian Swetland <swetland@google.com>
+ * Copyright (c) 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/slab.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+
+#include <linux/msm_audio.h>
+
+#include <mach/msm_qdsp6_audio.h>
+#include <mach/debug_mm.h>
+
+struct auxpcm {
+ struct mutex lock;
+ struct audio_client *ac;
+ uint32_t sample_rate;
+ uint32_t channel_count;
+ int opened;;
+};
+
+static long auxpcmout_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct auxpcm *auxpcmout = file->private_data;
+ int rc = 0;
+
+ mutex_lock(&auxpcmout->lock);
+ switch (cmd) {
+ case AUDIO_START: {
+ uint32_t acdb_id;
+ pr_debug("[%s:%s] AUDIO_START\n", __MM_FILE__, __func__);
+ if (arg == 0) {
+ acdb_id = 0;
+ } else if (copy_from_user(&acdb_id, (void *) arg,
+ sizeof(acdb_id))) {
+ pr_info("[%s:%s] copy acdb_id from user failed\n",
+ __MM_FILE__, __func__);
+ rc = -EFAULT;
+ break;
+ }
+ if (auxpcmout->ac) {
+ rc = -EBUSY;
+ pr_err("[%s:%s] active session already existing\n",
+ __MM_FILE__, __func__);
+ } else {
+ auxpcmout->ac =
+ q6audio_open_auxpcm(auxpcmout->sample_rate,
+ auxpcmout->channel_count,
+ AUDIO_FLAG_WRITE, acdb_id);
+ if (!auxpcmout->ac) {
+ pr_err("[%s:%s] auxpcm open session failed\n",
+ __MM_FILE__, __func__);
+ rc = -ENOMEM;
+ }
+ }
+ break;
+ }
+ case AUDIO_STOP:
+ pr_debug("[%s:%s] AUDIO_STOP\n", __MM_FILE__, __func__);
+ break;
+ case AUDIO_FLUSH:
+ break;
+ case AUDIO_SET_CONFIG: {
+ struct msm_audio_config config;
+ if (auxpcmout->ac) {
+ rc = -EBUSY;
+ pr_err("[%s:%s] active session already existing\n",
+ __MM_FILE__, __func__);
+ break;
+ }
+ if (copy_from_user(&config, (void *) arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ pr_debug("[%s:%s] SET_CONFIG: samplerate = %d, channels = %d\n",
+ __MM_FILE__, __func__, config.sample_rate,
+ config.channel_count);
+ if (config.channel_count != 1) {
+ rc = -EINVAL;
+ pr_err("[%s:%s] invalid channelcount %d\n",
+ __MM_FILE__, __func__, config.channel_count);
+ break;
+ }
+ if (config.sample_rate != 8000) {
+ rc = -EINVAL;
+ pr_err("[%s:%s] invalid samplerate %d\n", __MM_FILE__,
+ __func__, config.sample_rate);
+ break;
+ }
+ auxpcmout->sample_rate = config.sample_rate;
+ auxpcmout->channel_count = config.channel_count;
+ break;
+ }
+ case AUDIO_GET_CONFIG: {
+ struct msm_audio_config config;
+ config.buffer_size = 0;
+ config.buffer_count = 0;
+ config.sample_rate = auxpcmout->sample_rate;
+ config.channel_count = auxpcmout->channel_count;
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+ config.unused[2] = 0;
+ if (copy_to_user((void *) arg, &config, sizeof(config)))
+ rc = -EFAULT;
+ pr_debug("[%s:%s] GET_CONFIG: samplerate = %d, channels= %d\n",
+ __MM_FILE__, __func__, config.sample_rate,
+ config.channel_count);
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&auxpcmout->lock);
+ pr_debug("[%s:%s] rc = %d\n", __MM_FILE__, __func__, rc);
+ return rc;
+}
+
+static struct auxpcm the_auxpcmout;
+
+static int auxpcmout_open(struct inode *inode, struct file *file)
+{
+ struct auxpcm *auxpcmout = &the_auxpcmout;
+
+ pr_info("[%s:%s] open\n", __MM_FILE__, __func__);
+
+ mutex_lock(&auxpcmout->lock);
+
+ if (auxpcmout->opened) {
+ pr_err("aux pcm loopback rx already open!\n");
+ mutex_unlock(&auxpcmout->lock);
+ return -EBUSY;
+ }
+ auxpcmout->channel_count = 1;
+ auxpcmout->sample_rate = 8000;
+ auxpcmout->opened = 1;
+ file->private_data = auxpcmout;
+ mutex_unlock(&auxpcmout->lock);
+ return 0;
+}
+
+static int auxpcmout_release(struct inode *inode, struct file *file)
+{
+ struct auxpcm *auxpcmout = file->private_data;
+ mutex_lock(&auxpcmout->lock);
+ if (auxpcmout->ac)
+ q6audio_auxpcm_close(auxpcmout->ac);
+ auxpcmout->ac = NULL;
+ auxpcmout->opened = 0;
+ mutex_unlock(&auxpcmout->lock);
+ pr_info("[%s:%s] release\n", __MM_FILE__, __func__);
+ return 0;
+}
+
+static const struct file_operations auxpcmout_fops = {
+ .owner = THIS_MODULE,
+ .open = auxpcmout_open,
+ .release = auxpcmout_release,
+ .unlocked_ioctl = auxpcmout_ioctl,
+};
+
+struct miscdevice auxpcmout_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_aux_pcm_lb_out",
+ .fops = &auxpcmout_fops,
+};
+
+static int __init auxpcmout_init(void)
+{
+ mutex_init(&the_auxpcmout.lock);
+ return misc_register(&auxpcmout_misc);
+}
+
+device_initcall(auxpcmout_init);
diff --git a/arch/arm/mach-msm/qdsp6/dal.c b/arch/arm/mach-msm/qdsp6/dal.c
new file mode 100644
index 0000000..378432b
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/dal.c
@@ -0,0 +1,727 @@
+/* arch/arm/mach-msm/qdsp6/dal.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Author: Brian Swetland <swetland@google.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/slab.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/errno.h>
+
+#include <linux/delay.h>
+
+#include <mach/msm_smd.h>
+#include <mach/debug_mm.h>
+#include <mach/msm_qdsp6_audio.h>
+
+#include "dal.h"
+
+#define DAL_TRACE 0
+
+struct dal_hdr {
+ uint32_t length:16; /* message length (header inclusive) */
+ uint32_t version:8; /* DAL protocol version */
+ uint32_t priority:7;
+ uint32_t async:1;
+ uint32_t ddi:16; /* DDI method number */
+ uint32_t prototype:8; /* DDI serialization format */
+ uint32_t msgid:8; /* message id (DDI, ATTACH, DETACH, ...) */
+ void *from;
+ void *to;
+} __attribute__((packed));
+
+#define TRACE_DATA_MAX 128
+#define TRACE_LOG_MAX 32
+#define TRACE_LOG_MASK (TRACE_LOG_MAX - 1)
+
+struct dal_trace {
+ unsigned timestamp;
+ struct dal_hdr hdr;
+ uint32_t data[TRACE_DATA_MAX];
+};
+
+#define DAL_HDR_SIZE (sizeof(struct dal_hdr))
+#define DAL_DATA_MAX 512
+#define DAL_MSG_MAX (DAL_HDR_SIZE + DAL_DATA_MAX)
+
+#define DAL_VERSION 0x11
+
+#define DAL_MSGID_DDI 0x00
+#define DAL_MSGID_ATTACH 0x01
+#define DAL_MSGID_DETACH 0x02
+#define DAL_MSGID_ASYNCH 0xC0
+#define DAL_MSGID_REPLY 0x80
+
+struct dal_channel {
+ struct list_head list;
+ struct list_head clients;
+
+ /* synchronization for changing channel state,
+ * adding/removing clients, smd callbacks, etc
+ */
+ spinlock_t lock;
+
+ struct smd_channel *sch;
+ char *name;
+
+ /* events are delivered at IRQ context immediately, so
+ * we only need one assembly buffer for the entire channel
+ */
+ struct dal_hdr hdr;
+ unsigned char data[DAL_DATA_MAX];
+
+ unsigned count;
+ void *ptr;
+
+ /* client which the current inbound message is for */
+ struct dal_client *active;
+};
+
+struct dal_client {
+ struct list_head list;
+ struct dal_channel *dch;
+ void *cookie;
+ dal_event_func_t event;
+
+ /* opaque handle for the far side */
+ void *remote;
+
+ /* dal rpc calls are fully synchronous -- only one call may be
+ * active per client at a time
+ */
+ struct mutex write_lock;
+ wait_queue_head_t wait;
+
+ unsigned char data[DAL_DATA_MAX];
+
+ void *reply;
+ int reply_max;
+ int status;
+ unsigned msgid; /* msgid of expected reply */
+
+ spinlock_t tr_lock;
+ unsigned tr_head;
+ unsigned tr_tail;
+ struct dal_trace *tr_log;
+};
+
+static unsigned now(void)
+{
+ struct timespec ts;
+ ktime_get_ts(&ts);
+ return (ts.tv_nsec / 1000000) + (ts.tv_sec * 1000);
+}
+
+void dal_trace(struct dal_client *c)
+{
+ if (c->tr_log)
+ return;
+ c->tr_log = kzalloc(sizeof(struct dal_trace) * TRACE_LOG_MAX,
+ GFP_KERNEL);
+}
+
+void dal_trace_print(struct dal_hdr *hdr, unsigned *data, int len, unsigned when)
+{
+ int i;
+ printk("DAL %08x -> %08x L=%03x A=%d D=%04x P=%02x M=%02x T=%d",
+ (unsigned) hdr->from, (unsigned) hdr->to,
+ hdr->length, hdr->async,
+ hdr->ddi, hdr->prototype, hdr->msgid,
+ when);
+ len /= 4;
+ for (i = 0; i < len; i++) {
+ if (!(i & 7))
+ printk("\n%03x", i * 4);
+ printk(" %08x", data[i]);
+ }
+ printk("\n");
+}
+
+void dal_trace_dump(struct dal_client *c)
+{
+ struct dal_trace *dt;
+ unsigned n, len;
+
+ if (!c->tr_log)
+ return;
+
+ for (n = c->tr_tail; n != c->tr_head; n = (n + 1) & TRACE_LOG_MASK) {
+ dt = c->tr_log + n;
+ len = dt->hdr.length - sizeof(dt->hdr);
+ if (len > TRACE_DATA_MAX)
+ len = TRACE_DATA_MAX;
+ dal_trace_print(&dt->hdr, dt->data, len, dt->timestamp);
+ }
+}
+
+static void dal_trace_log(struct dal_client *c,
+ struct dal_hdr *hdr, void *data, unsigned len)
+{
+ unsigned long flags;
+ unsigned t, n;
+ struct dal_trace *dt;
+
+ t = now();
+ if (len > TRACE_DATA_MAX)
+ len = TRACE_DATA_MAX;
+
+ spin_lock_irqsave(&c->tr_lock, flags);
+ n = (c->tr_head + 1) & TRACE_LOG_MASK;
+ if (c->tr_tail == n)
+ c->tr_tail = (c->tr_tail + 1) & TRACE_LOG_MASK;
+ dt = c->tr_log + n;
+ dt->timestamp = t;
+ memcpy(&dt->hdr, hdr, sizeof(struct dal_hdr));
+ memcpy(dt->data, data, len);
+ c->tr_head = n;
+
+ spin_unlock_irqrestore(&c->tr_lock, flags);
+}
+
+
+static void dal_channel_notify(void *priv, unsigned event)
+{
+ struct dal_channel *dch = priv;
+ struct dal_hdr *hdr = &dch->hdr;
+ struct dal_client *client;
+ unsigned long flags;
+ int len;
+ int r;
+
+ spin_lock_irqsave(&dch->lock, flags);
+
+again:
+ if (dch->count == 0) {
+ if (smd_read_avail(dch->sch) < DAL_HDR_SIZE)
+ goto done;
+
+ smd_read(dch->sch, hdr, DAL_HDR_SIZE);
+
+ if (hdr->length < DAL_HDR_SIZE)
+ goto done;
+
+ if (hdr->length > DAL_MSG_MAX)
+ panic("oversize message");
+
+ dch->count = hdr->length - DAL_HDR_SIZE;
+
+ /* locate the client this message is targeted to */
+ list_for_each_entry(client, &dch->clients, list) {
+ if (dch->hdr.to == client) {
+ dch->active = client;
+ dch->ptr = client->data;
+ goto check_data;
+ }
+ }
+ pr_err("[%s:%s] $$$ receiving unknown message len = %d $$$\n",
+ __MM_FILE__, __func__, dch->count);
+ dch->active = 0;
+ dch->ptr = dch->data;
+ }
+
+check_data:
+ len = dch->count;
+ if (len > 0) {
+ if (smd_read_avail(dch->sch) < len)
+ goto done;
+
+ r = smd_read(dch->sch, dch->ptr, len);
+ if (r != len)
+ panic("invalid read");
+
+#if DAL_TRACE
+ pr_info("[%s:%s] dal recv %p <- %p %02x:%04x:%02x %d\n",
+ __MM_FILE__, __func__, hdr->to, hdr->from, hdr->msgid,
+ hdr->ddi, hdr->prototype, hdr->length - sizeof(*hdr));
+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, dch->ptr, len);
+#endif
+ dch->count = 0;
+
+ client = dch->active;
+ if (!client) {
+ pr_err("[%s:%s] message to %p discarded\n",
+ __MM_FILE__, __func__, dch->hdr.to);
+ goto again;
+ }
+
+ if (client->tr_log)
+ dal_trace_log(client, hdr, dch->ptr, len);
+
+ if (hdr->msgid == DAL_MSGID_ASYNCH) {
+ if (client->event)
+ client->event(dch->ptr, len, client->cookie);
+ else
+ pr_err("[%s:%s] client %p has no event \
+ handler\n", __MM_FILE__, __func__,
+ client);
+ goto again;
+ }
+
+ if (hdr->msgid == client->msgid) {
+ if (!client->remote)
+ client->remote = hdr->from;
+ if (len > client->reply_max)
+ len = client->reply_max;
+ memcpy(client->reply, client->data, len);
+ client->status = len;
+ wake_up(&client->wait);
+ goto again;
+ }
+
+ pr_err("[%s:%s] cannot find client %p\n", __MM_FILE__,
+ __func__, dch->hdr.to);
+ goto again;
+ }
+
+done:
+ spin_unlock_irqrestore(&dch->lock, flags);
+}
+
+static LIST_HEAD(dal_channel_list);
+static DEFINE_MUTEX(dal_channel_list_lock);
+
+static struct dal_channel *dal_open_channel(const char *name, uint32_t cpu)
+{
+ struct dal_channel *dch;
+
+ pr_debug("[%s:%s]\n", __MM_FILE__, __func__);
+ mutex_lock(&dal_channel_list_lock);
+
+ list_for_each_entry(dch, &dal_channel_list, list) {
+ if (!strcmp(dch->name, name))
+ goto found_it;
+ }
+
+ dch = kzalloc(sizeof(*dch) + strlen(name) + 1, GFP_KERNEL);
+ if (!dch)
+ goto fail;
+
+ dch->name = (char *) (dch + 1);
+ strcpy(dch->name, name);
+ spin_lock_init(&dch->lock);
+ INIT_LIST_HEAD(&dch->clients);
+
+ list_add(&dch->list, &dal_channel_list);
+
+found_it:
+ if (!dch->sch) {
+ if (smd_named_open_on_edge(name, cpu, &dch->sch,
+ dch, dal_channel_notify)) {
+ pr_err("[%s:%s] smd open failed\n", __MM_FILE__,
+ __func__);
+ dch = NULL;
+ }
+ /* FIXME: wait for channel to open before returning */
+ msleep(100);
+ }
+
+fail:
+ mutex_unlock(&dal_channel_list_lock);
+
+ return dch;
+}
+
+int dal_call_raw(struct dal_client *client,
+ struct dal_hdr *hdr,
+ void *data, int data_len,
+ void *reply, int reply_max)
+{
+ struct dal_channel *dch = client->dch;
+ unsigned long flags;
+
+ client->reply = reply;
+ client->reply_max = reply_max;
+ client->msgid = hdr->msgid | DAL_MSGID_REPLY;
+ client->status = -EBUSY;
+
+#if DAL_TRACE
+ pr_info("[%s:%s:%x] dal send %p -> %p %02x:%04x:%02x %d\n",
+ __MM_FILE__, __func__, (unsigned int)client, hdr->from, hdr->to,
+ hdr->msgid, hdr->ddi, hdr->prototype,
+ hdr->length - sizeof(*hdr));
+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, data, data_len);
+#endif
+
+ if (client->tr_log)
+ dal_trace_log(client, hdr, data, data_len);
+
+ spin_lock_irqsave(&dch->lock, flags);
+ /* FIXME: ensure entire message is written or none. */
+ smd_write(dch->sch, hdr, sizeof(*hdr));
+ smd_write(dch->sch, data, data_len);
+ spin_unlock_irqrestore(&dch->lock, flags);
+
+ if (!wait_event_timeout(client->wait, (client->status != -EBUSY), 5*HZ)) {
+ dal_trace_dump(client);
+ pr_err("[%s:%s] call timed out. dsp is probably dead.\n",
+ __MM_FILE__, __func__);
+ dal_trace_print(hdr, data, data_len, 0);
+ q6audio_dsp_not_responding();
+ }
+
+ return client->status;
+}
+
+int dal_call(struct dal_client *client,
+ unsigned ddi, unsigned prototype,
+ void *data, int data_len,
+ void *reply, int reply_max)
+{
+ struct dal_hdr hdr;
+ int r;
+
+ memset(&hdr, 0, sizeof(hdr));
+
+ hdr.length = data_len + sizeof(hdr);
+ hdr.version = DAL_VERSION;
+ hdr.msgid = DAL_MSGID_DDI;
+ hdr.ddi = ddi;
+ hdr.prototype = prototype;
+ hdr.from = client;
+ hdr.to = client->remote;
+
+ if (hdr.length > DAL_MSG_MAX)
+ return -EINVAL;
+
+ mutex_lock(&client->write_lock);
+ r = dal_call_raw(client, &hdr, data, data_len, reply, reply_max);
+ mutex_unlock(&client->write_lock);
+
+ return r;
+}
+
+struct dal_msg_attach {
+ uint32_t device_id;
+ char attach[64];
+ char service_name[32];
+} __attribute__((packed));
+
+struct dal_reply_attach {
+ uint32_t status;
+ char name[64];
+};
+
+struct dal_client *dal_attach(uint32_t device_id, const char *name,
+ uint32_t cpu, dal_event_func_t func, void *cookie)
+{
+ struct dal_hdr hdr;
+ struct dal_msg_attach msg;
+ struct dal_reply_attach reply;
+ struct dal_channel *dch;
+ struct dal_client *client;
+ unsigned long flags;
+ int r;
+
+ pr_debug("[%s:%s]\n", __MM_FILE__, __func__);
+ dch = dal_open_channel(name, cpu);
+ if (!dch)
+ return 0;
+
+ client = kzalloc(sizeof(*client), GFP_KERNEL);
+ if (!client)
+ return 0;
+
+ client->dch = dch;
+ client->event = func;
+ client->cookie = cookie;
+ mutex_init(&client->write_lock);
+ spin_lock_init(&client->tr_lock);
+ init_waitqueue_head(&client->wait);
+
+ spin_lock_irqsave(&dch->lock, flags);
+ list_add(&client->list, &dch->clients);
+ spin_unlock_irqrestore(&dch->lock, flags);
+
+ memset(&hdr, 0, sizeof(hdr));
+ memset(&msg, 0, sizeof(msg));
+
+ hdr.length = sizeof(hdr) + sizeof(msg);
+ hdr.version = DAL_VERSION;
+ hdr.msgid = DAL_MSGID_ATTACH;
+ hdr.from = client;
+ msg.device_id = device_id;
+
+ r = dal_call_raw(client, &hdr, &msg, sizeof(msg),
+ &reply, sizeof(reply));
+
+ if ((r == sizeof(reply)) && (reply.status == 0)) {
+ reply.name[63] = 0;
+ pr_info("[%s:%s] status = %d, name = '%s' dal_client %x\n",
+ __MM_FILE__, __func__, reply.status,
+ reply.name, (unsigned int)client);
+ return client;
+ }
+
+ pr_err("[%s:%s] failure\n", __MM_FILE__, __func__);
+
+ dal_detach(client);
+ return 0;
+}
+
+int dal_detach(struct dal_client *client)
+{
+ struct dal_channel *dch;
+ unsigned long flags;
+
+ pr_debug("[%s:%s]\n", __MM_FILE__, __func__);
+ mutex_lock(&client->write_lock);
+ if (client->remote) {
+ struct dal_hdr hdr;
+ uint32_t data;
+
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.length = sizeof(hdr) + sizeof(data);
+ hdr.version = DAL_VERSION;
+ hdr.msgid = DAL_MSGID_DETACH;
+ hdr.from = client;
+ hdr.to = client->remote;
+ data = (uint32_t) client;
+
+ dal_call_raw(client, &hdr, &data, sizeof(data),
+ &data, sizeof(data));
+ }
+
+ dch = client->dch;
+ spin_lock_irqsave(&dch->lock, flags);
+ if (dch->active == client) {
+ /* We have received a message header for this client
+ * but not the body of the message. Ensure that when
+ * the body arrives we don't write it into the now-closed
+ * client. In *theory* this should never happen.
+ */
+ dch->active = 0;
+ dch->ptr = dch->data;
+ }
+ list_del(&client->list);
+ spin_unlock_irqrestore(&dch->lock, flags);
+
+ mutex_unlock(&client->write_lock);
+
+ kfree(client);
+ return 0;
+}
+
+void *dal_get_remote_handle(struct dal_client *client)
+{
+ return client->remote;
+}
+
+/* convenience wrappers */
+
+int dal_call_f0(struct dal_client *client, uint32_t ddi, uint32_t arg1)
+{
+ uint32_t tmp = arg1;
+ int res;
+ res = dal_call(client, ddi, 0, &tmp, sizeof(tmp), &tmp, sizeof(tmp));
+ if (res >= 4)
+ return (int) tmp;
+ return res;
+}
+
+int dal_call_f1(struct dal_client *client, uint32_t ddi, uint32_t arg1,
+ uint32_t arg2)
+{
+ uint32_t tmp[2];
+ int res;
+ tmp[0] = arg1;
+ tmp[1] = arg2;
+ res = dal_call(client, ddi, 1, tmp, sizeof(tmp), tmp, sizeof(uint32_t));
+ if (res >= 4)
+ return (int) tmp[0];
+ return res;
+}
+
+int dal_call_f5(struct dal_client *client, uint32_t ddi, void *ibuf, uint32_t ilen)
+{
+ uint32_t tmp[128];
+ int res;
+ int param_idx = 0;
+
+ if (ilen + 4 > DAL_DATA_MAX)
+ return -EINVAL;
+
+ tmp[param_idx] = ilen;
+ param_idx++;
+
+ memcpy(&tmp[param_idx], ibuf, ilen);
+ param_idx += DIV_ROUND_UP(ilen, 4);
+
+ res = dal_call(client, ddi, 5, tmp, param_idx * 4, tmp, sizeof(tmp));
+
+ if (res >= 4)
+ return (int) tmp[0];
+ return res;
+}
+
+int dal_call_f6(struct dal_client *client, uint32_t ddi, uint32_t s1,
+ void *ibuf, uint32_t ilen)
+{
+ uint32_t tmp[128];
+ int res;
+ int param_idx = 0;
+
+ if (ilen + 8 > DAL_DATA_MAX)
+ return -EINVAL;
+
+ tmp[param_idx] = s1;
+ param_idx++;
+ tmp[param_idx] = ilen;
+ param_idx++;
+ memcpy(&tmp[param_idx], ibuf, ilen);
+ param_idx += DIV_ROUND_UP(ilen, 4);
+
+ res = dal_call(client, ddi, 6, tmp, param_idx * 4, tmp, sizeof(tmp));
+
+ if (res >= 4)
+ return (int) tmp[0];
+
+ return res;
+}
+
+int dal_call_f9(struct dal_client *client, uint32_t ddi, void *obuf,
+ uint32_t olen)
+{
+ uint32_t tmp[128];
+ int res;
+
+ if (olen > sizeof(tmp) - 8)
+ return -EINVAL;
+ tmp[0] = olen;
+
+ res = dal_call(client, ddi, 9, tmp, sizeof(uint32_t), tmp,
+ sizeof(tmp));
+
+ if (res >= 4)
+ res = (int)tmp[0];
+
+ if (!res) {
+ if (tmp[1] > olen)
+ return -EIO;
+ memcpy(obuf, &tmp[2], tmp[1]);
+ }
+ return res;
+}
+
+int dal_call_f11(struct dal_client *client, uint32_t ddi, uint32_t s1,
+ void *obuf, uint32_t olen)
+{
+ uint32_t tmp[DAL_DATA_MAX/4] = {0};
+ int res;
+ int param_idx = 0;
+ int num_bytes = 4;
+
+ num_bytes += (DIV_ROUND_UP(olen, 4)) * 4;
+
+ if ((num_bytes > DAL_DATA_MAX - 12) || (olen > DAL_DATA_MAX - 8))
+ return -EINVAL;
+
+ tmp[param_idx] = s1;
+ param_idx++;
+ tmp[param_idx] = olen;
+ param_idx += DIV_ROUND_UP(olen, 4);
+
+ res = dal_call(client, ddi, 11, tmp, param_idx * 4, tmp, sizeof(tmp));
+
+ if (res >= 4)
+ res = (int) tmp[0];
+ if (!res) {
+ if (tmp[1] > olen)
+ return -EIO;
+ memcpy(obuf, &tmp[2], tmp[1]);
+ }
+ return res;
+}
+
+int dal_call_f13(struct dal_client *client, uint32_t ddi, void *ibuf1,
+ uint32_t ilen1, void *ibuf2, uint32_t ilen2, void *obuf,
+ uint32_t olen)
+{
+ uint32_t tmp[DAL_DATA_MAX/4];
+ int res;
+ int param_idx = 0;
+ int num_bytes = 0;
+
+ num_bytes = (DIV_ROUND_UP(ilen1, 4)) * 4;
+ num_bytes += (DIV_ROUND_UP(ilen2, 4)) * 4;
+
+ if ((num_bytes > DAL_DATA_MAX - 12) || (olen > DAL_DATA_MAX - 8) ||
+ (ilen1 > DAL_DATA_MAX) || (ilen2 > DAL_DATA_MAX))
+ return -EINVAL;
+
+ tmp[param_idx] = ilen1;
+ param_idx++;
+
+ memcpy(&tmp[param_idx], ibuf1, ilen1);
+ param_idx += DIV_ROUND_UP(ilen1, 4);
+
+ tmp[param_idx++] = ilen2;
+ memcpy(&tmp[param_idx], ibuf2, ilen2);
+ param_idx += DIV_ROUND_UP(ilen2, 4);
+
+ tmp[param_idx++] = olen;
+ res = dal_call(client, ddi, 13, tmp, param_idx * 4, tmp,
+ sizeof(tmp));
+
+ if (res >= 4)
+ res = (int)tmp[0];
+
+ if (!res) {
+ if (tmp[1] > olen)
+ return -EIO;
+ memcpy(obuf, &tmp[2], tmp[1]);
+ }
+ return res;
+}
+int dal_call_f14(struct dal_client *client, uint32_t ddi, void *ibuf,
+ uint32_t ilen, void *obuf1, uint32_t olen1, void *obuf2,
+ uint32_t olen2, uint32_t *oalen2)
+{
+ uint32_t tmp[128];
+ int res;
+ int param_idx = 0;
+
+ if (olen1 + olen2 + 8 > DAL_DATA_MAX ||
+ ilen + 12 > DAL_DATA_MAX)
+ return -EINVAL;
+
+ tmp[param_idx] = ilen;
+ param_idx++;
+
+ memcpy(&tmp[param_idx], ibuf, ilen);
+ param_idx += DIV_ROUND_UP(ilen, 4);
+
+ tmp[param_idx++] = olen1;
+ tmp[param_idx++] = olen2;
+ res = dal_call(client, ddi, 14, tmp, param_idx * 4, tmp, sizeof(tmp));
+
+ if (res >= 4)
+ res = (int)tmp[0];
+
+ if (!res) {
+ if (tmp[1] > olen1)
+ return -EIO;
+ param_idx = DIV_ROUND_UP(tmp[1], 4) + 2;
+ if (tmp[param_idx] > olen2)
+ return -EIO;
+
+ memcpy(obuf1, &tmp[2], tmp[1]);
+ memcpy(obuf2, &tmp[param_idx+1], tmp[param_idx]);
+ *oalen2 = tmp[param_idx];
+ }
+ return res;
+}
diff --git a/arch/arm/mach-msm/qdsp6/dal.h b/arch/arm/mach-msm/qdsp6/dal.h
new file mode 100644
index 0000000..1176eb9
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/dal.h
@@ -0,0 +1,96 @@
+/* arch/arm/mach-msm/qdsp6/dal.h
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Author: Brian Swetland <swetland@google.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 _MACH_MSM_DAL_
+#define _MACH_MSM_DAL_
+
+struct dal_client;
+
+struct dal_info {
+ uint32_t size;
+ uint32_t version;
+ char name[32];
+};
+
+typedef void (*dal_event_func_t)(void *data, int len, void *cookie);
+
+struct dal_client *dal_attach(uint32_t device_id, const char *name,
+ uint32_t cpu, dal_event_func_t func, void *cookie);
+
+int dal_detach(struct dal_client *client);
+
+int dal_call(struct dal_client *client,
+ unsigned ddi, unsigned prototype,
+ void *data, int data_len,
+ void *reply, int reply_max);
+
+void dal_trace(struct dal_client *client);
+void dal_trace_dump(struct dal_client *client);
+
+/* function to call before panic on stalled dal calls */
+void dal_set_oops(struct dal_client *client, void (*oops)(void));
+
+/* convenience wrappers */
+int dal_call_f0(struct dal_client *client, uint32_t ddi,
+ uint32_t arg1);
+int dal_call_f1(struct dal_client *client, uint32_t ddi,
+ uint32_t arg1, uint32_t arg2);
+int dal_call_f5(struct dal_client *client, uint32_t ddi,
+ void *ibuf, uint32_t ilen);
+int dal_call_f6(struct dal_client *client, uint32_t ddi,
+ uint32_t s1, void *ibuf, uint32_t ilen);
+int dal_call_f9(struct dal_client *client, uint32_t ddi,
+ void *obuf, uint32_t olen);
+int dal_call_f11(struct dal_client *client, uint32_t ddi,
+ uint32_t s1, void *obuf, uint32_t olen);
+int dal_call_f13(struct dal_client *client, uint32_t ddi, void *ibuf1,
+ uint32_t ilen1, void *ibuf2, uint32_t ilen2, void *obuf,
+ uint32_t olen);
+int dal_call_f14(struct dal_client *client, uint32_t ddi, void *ibuf,
+ uint32_t ilen, void *obuf1, uint32_t olen1, void *obuf2,
+ uint32_t olen2, uint32_t *oalen2);
+
+/* common DAL operations */
+enum {
+ DAL_OP_ATTACH = 0,
+ DAL_OP_DETACH,
+ DAL_OP_INIT,
+ DAL_OP_DEINIT,
+ DAL_OP_OPEN,
+ DAL_OP_CLOSE,
+ DAL_OP_INFO,
+ DAL_OP_POWEREVENT,
+ DAL_OP_SYSREQUEST,
+ DAL_OP_FIRST_DEVICE_API,
+};
+
+static inline int check_version(struct dal_client *client, uint32_t version)
+{
+ struct dal_info info;
+ int res;
+
+ res = dal_call_f9(client, DAL_OP_INFO, &info, sizeof(struct dal_info));
+ if (!res) {
+ if (((info.version & 0xFFFF0000) != (version & 0xFFFF0000)) ||
+ ((info.version & 0x0000FFFF) <
+ (version & 0x0000FFFF))) {
+ res = -EINVAL;
+ }
+ }
+ return res;
+}
+
+#endif
diff --git a/arch/arm/mach-msm/qdsp6/dal_acdb.h b/arch/arm/mach-msm/qdsp6/dal_acdb.h
new file mode 100644
index 0000000..dfb1fef
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/dal_acdb.h
@@ -0,0 +1,69 @@
+/* Copyright (c) 2009, 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.
+ *
+ */
+
+#define ACDB_DAL_DEVICE 0x02000069
+#define ACDB_DAL_PORT "DAL_AM_AUD"
+
+#define ACDB_OP_IOCTL DAL_OP_FIRST_DEVICE_API
+
+/* ioctls */
+#define ACDB_GET_DEVICE 0x0108bb92
+#define ACDB_SET_DEVICE 0x0108bb93
+#define ACDB_GET_STREAM 0x0108bb95
+#define ACDB_SET_STREAM 0x0108bb96
+#define ACDB_GET_DEVICE_TABLE 0x0108bb97
+#define ACDB_GET_STREAM_TABLE 0x0108bb98
+
+#define ACDB_RES_SUCCESS 0
+#define ACDB_RES_FAILURE -1
+#define ACDB_RES_BADPARM -2
+#define ACDB_RES_BADSTATE -3
+
+struct acdb_cmd_device {
+ uint32_t size;
+
+ uint32_t command_id;
+ uint32_t device_id;
+ uint32_t network_id;
+ uint32_t sample_rate_id;
+ uint32_t interface_id;
+ uint32_t algorithm_block_id;
+
+ /* physical page aligned buffer */
+ uint32_t total_bytes;
+ uint32_t unmapped_buf;
+} __attribute__((packed));
+
+struct acdb_cmd_device_table {
+ uint32_t size;
+
+ uint32_t command_id;
+ uint32_t device_id;
+ uint32_t network_id;
+ uint32_t sample_rate_id;
+
+ /* physical page aligned buffer */
+ uint32_t total_bytes;
+ uint32_t unmapped_buf;
+
+ uint32_t res_size;
+} __attribute__((packed));
+
+struct acdb_result {
+ uint32_t dal_status;
+ uint32_t size;
+
+ uint32_t unmapped_buf;
+ uint32_t used_bytes;
+ uint32_t result;
+} __attribute__((packed));
diff --git a/arch/arm/mach-msm/qdsp6/dal_adie.h b/arch/arm/mach-msm/qdsp6/dal_adie.h
new file mode 100644
index 0000000..6abc60c
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/dal_adie.h
@@ -0,0 +1,104 @@
+/* Copyright (c) 2009, 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_MSM_QDSP6_ADIE_
+#define _MACH_MSM_QDSP6_ADIE_
+
+#include "dal.h"
+
+#define ADIE_DAL_DEVICE 0x02000029
+#define ADIE_DAL_PORT "DAL_AM_AUD"
+
+enum {
+ ADIE_OP_GET_NUM_PATHS = DAL_OP_FIRST_DEVICE_API,
+ ADIE_OP_GET_ALL_PATH_IDS,
+ ADIE_OP_SET_PATH,
+ ADIE_OP_GET_NUM_PATH_FREQUENCY_PLANS,
+ ADIE_OP_GET_PATH_FREQUENCY_PLANS,
+ ADIE_OP_SET_PATH_FREQUENCY_PLAN,
+ ADIE_OP_PROCEED_TO_STAGE,
+ ADIE_OP_MUTE_PATH
+};
+
+/* Path IDs for normal operation. */
+#define ADIE_PATH_HANDSET_TX 0x010740f6
+#define ADIE_PATH_HANDSET_RX 0x010740f7
+#define ADIE_PATH_HEADSET_MONO_TX 0x010740f8
+#define ADIE_PATH_HEADSET_STEREO_TX 0x010740f9
+#define ADIE_PATH_HEADSET_MONO_RX 0x010740fa
+#define ADIE_PATH_HEADSET_STEREO_RX 0x010740fb
+#define ADIE_PATH_SPEAKER_TX 0x010740fc
+#define ADIE_PATH_SPEAKER_RX 0x010740fd
+#define ADIE_PATH_SPEAKER_STEREO_RX 0x01074101
+
+/* Path IDs used for TTY */
+#define ADIE_PATH_TTY_HEADSET_TX 0x010740fe
+#define ADIE_PATH_TTY_HEADSET_RX 0x010740ff
+
+/* Path IDs used by Factory Test Mode. */
+#define ADIE_PATH_FTM_MIC1_TX 0x01074108
+#define ADIE_PATH_FTM_MIC2_TX 0x01074107
+#define ADIE_PATH_FTM_HPH_L_RX 0x01074106
+#define ADIE_PATH_FTM_HPH_R_RX 0x01074104
+#define ADIE_PATH_FTM_EAR_RX 0x01074103
+#define ADIE_PATH_FTM_SPKR_RX 0x01074102
+
+/* Path IDs for Loopback */
+/* Path IDs used for Line in -> AuxPGA -> Line Out Stereo Mode*/
+#define ADIE_PATH_AUXPGA_LINEOUT_STEREO_LB 0x01074100
+/* Line in -> AuxPGA -> LineOut Mono */
+#define ADIE_PATH_AUXPGA_LINEOUT_MONO_LB 0x01073d82
+/* Line in -> AuxPGA -> Stereo Headphone */
+#define ADIE_PATH_AUXPGA_HDPH_STEREO_LB 0x01074109
+/* Line in -> AuxPGA -> Mono Headphone */
+#define ADIE_PATH_AUXPGA_HDPH_MONO_LB 0x01073d85
+/* Line in -> AuxPGA -> Earpiece */
+#define ADIE_PATH_AUXPGA_EAP_LB 0x01073d81
+/* Line in -> AuxPGA -> AuxOut */
+#define ADIE_PATH_AUXPGA_AUXOUT_LB 0x01073d86
+
+/* Concurrency Profiles */
+#define ADIE_PATH_SPKR_STEREO_HDPH_MONO_RX 0x01073d83
+#define ADIE_PATH_SPKR_MONO_HDPH_MONO_RX 0x01073d84
+#define ADIE_PATH_SPKR_MONO_HDPH_STEREO_RX 0x01073d88
+#define ADIE_PATH_SPKR_STEREO_HDPH_STEREO_RX 0x01073d89
+
+
+/** Fluence Profiles **/
+
+/* Broadside/Bowsetalk profile,
+ * For Handset and Speaker phone Tx*/
+#define ADIE_CODEC_HANDSET_SPKR_BS_TX 0x0108fafa
+/* EndFire profile,
+ * For Handset and Speaker phone Tx*/
+#define ADIE_CODEC_HANDSET_SPKR_EF_TX 0x0108fafb
+
+
+/* stages */
+#define ADIE_STAGE_PATH_OFF 0x0050
+#define ADIE_STAGE_DIGITAL_READY 0x0100
+#define ADIE_STAGE_DIGITAL_ANALOG_READY 0x1000
+#define ADIE_STAGE_ANALOG_OFF 0x0750
+#define ADIE_STAGE_DIGITAL_OFF 0x0600
+
+/* path types */
+#define ADIE_PATH_RX 0
+#define ADIE_PATH_TX 1
+#define ADIE_PATH_LOOPBACK 2
+
+/* mute states */
+#define ADIE_MUTE_OFF 0
+#define ADIE_MUTE_ON 1
+
+
+#endif
diff --git a/arch/arm/mach-msm/qdsp6/dal_audio.h b/arch/arm/mach-msm/qdsp6/dal_audio.h
new file mode 100644
index 0000000..25d1e4f
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/dal_audio.h
@@ -0,0 +1,604 @@
+/* Copyright (c) 2009-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.
+ *
+ */
+
+#ifndef __DAL_AUDIO_H__
+#define __DAL_AUDIO_H__
+
+#include "dal_audio_format.h"
+
+#define AUDIO_DAL_DEVICE 0x02000028
+#define AUDIO_DAL_PORT "DAL_AQ_AUD"
+
+enum {
+ AUDIO_OP_CONTROL = DAL_OP_FIRST_DEVICE_API,
+ AUDIO_OP_DATA,
+ AUDIO_OP_INIT,
+};
+
+/* ---- common audio structures ---- */
+
+/* This flag, if set, indicates that the beginning of the data in the*/
+/* buffer is a synchronization point or key frame, meaning no data */
+/* before it in the stream is required in order to render the stream */
+/* from this point onward. */
+#define ADSP_AUDIO_BUFFER_FLAG_SYNC_POINT 0x01
+
+/* This flag, if set, indicates that the buffer object is using valid */
+/* physical address used to store the media data */
+#define ADSP_AUDIO_BUFFER_FLAG_PHYS_ADDR 0x04
+
+/* This flag, if set, indicates that a media start timestamp has been */
+/* set for a buffer. */
+#define ADSP_AUDIO_BUFFER_FLAG_START_SET 0x08
+
+/* This flag, if set, indicates that a media stop timestamp has been set */
+/* for a buffer. */
+#define ADSP_AUDIO_BUFFER_FLAG_STOP_SET 0x10
+
+/* This flag, if set, indicates that a preroll timestamp has been set */
+/* for a buffer. */
+#define ADSP_AUDIO_BUFFER_FLAG_PREROLL_SET 0x20
+
+/* This flag, if set, indicates that the data in the buffer is a fragment of */
+/* a larger block of data, and will be continued by the data in the next */
+/* buffer to be delivered. */
+#define ADSP_AUDIO_BUFFER_FLAG_CONTINUATION 0x40
+
+struct adsp_audio_buffer {
+ u32 addr; /* Physical Address of buffer */
+ u32 max_size; /* Maximum size of buffer */
+ u32 actual_size; /* Actual size of valid data in the buffer */
+ u32 offset; /* Offset to the first valid byte */
+ u32 flags; /* ADSP_AUDIO_BUFFER_FLAGs that has been set */
+ s64 start; /* Start timestamp, if any */
+ s64 stop; /* Stop timestamp, if any */
+ s64 preroll; /* Preroll timestamp, if any */
+} __attribute__ ((packed));
+
+
+
+/* ---- audio commands ---- */
+
+/* Command/event response types */
+#define ADSP_AUDIO_RESPONSE_COMMAND 0
+#define ADSP_AUDIO_RESPONSE_ASYNC 1
+
+struct adsp_command_hdr {
+ u32 size; /* sizeof(cmd) - sizeof(u32) */
+
+ u32 dst;
+ u32 src;
+
+ u32 opcode;
+ u32 response_type;
+ u32 seq_number;
+
+ u32 context; /* opaque to DSP */
+ u32 data;
+
+ u32 padding;
+} __attribute__ ((packed));
+
+
+#define AUDIO_DOMAIN_APP 0
+#define AUDIO_DOMAIN_MODEM 1
+#define AUDIO_DOMAIN_DSP 2
+
+#define AUDIO_SERVICE_AUDIO 0
+#define AUDIO_SERVICE_VIDEO 1 /* really? */
+
+/* adsp audio addresses are (byte order) domain, service, major, minor */
+//#define AUDIO_ADDR(maj,min) ( (((maj) & 0xff) << 16) | (((min) & 0xff) << 24) | (1) )
+
+#define AUDIO_ADDR(maj,min,dom) ( (((min) & 0xff) << 24) | (((maj) & 0xff) << 16) | ((AUDIO_SERVICE_AUDIO) << 8) | (dom) )
+
+
+/* AAC Encoder modes */
+#define ADSP_AUDIO_ENC_AAC_LC_ONLY_MODE 0
+#define ADSP_AUDIO_ENC_AAC_PLUS_MODE 1
+#define ADSP_AUDIO_ENC_ENHANCED_AAC_PLUS_MODE 2
+
+struct adsp_audio_aac_enc_cfg {
+ u32 bit_rate; /* bits per second */
+ u32 encoder_mode; /* ADSP_AUDIO_ENC_* */
+} __attribute__ ((packed));
+
+#define ADSP_AUDIO_ENC_SBC_ALLOCATION_METHOD_LOUNDNESS 0
+#define ADSP_AUDIO_ENC_SBC_ALLOCATION_METHOD_SNR 1
+
+#define ADSP_AUDIO_ENC_SBC_CHANNEL_MODE_MONO 1
+#define ADSP_AUDIO_ENC_SBC_CHANNEL_MODE_STEREO 2
+#define ADSP_AUDIO_ENC_SBC_CHANNEL_MODE_DUAL 8
+#define ADSP_AUDIO_ENC_SBC_CHANNEL_MODE_JOINT_STEREO 9
+
+struct adsp_audio_sbc_encoder_cfg {
+ u32 num_subbands;
+ u32 block_len;
+ u32 channel_mode;
+ u32 allocation_method;
+ u32 bit_rate;
+} __attribute__ ((packed));
+
+/* AMR NB encoder modes */
+#define ADSP_AUDIO_AMR_MR475 0
+#define ADSP_AUDIO_AMR_MR515 1
+#define ADSP_AUDIO_AMR_MMR59 2
+#define ADSP_AUDIO_AMR_MMR67 3
+#define ADSP_AUDIO_AMR_MMR74 4
+#define ADSP_AUDIO_AMR_MMR795 5
+#define ADSP_AUDIO_AMR_MMR102 6
+#define ADSP_AUDIO_AMR_MMR122 7
+
+/* The following are valid AMR NB DTX modes */
+#define ADSP_AUDIO_AMR_DTX_MODE_OFF 0
+#define ADSP_AUDIO_AMR_DTX_MODE_ON_VAD1 1
+#define ADSP_AUDIO_AMR_DTX_MODE_ON_VAD2 2
+#define ADSP_AUDIO_AMR_DTX_MODE_ON_AUTO 3
+
+/* AMR Encoder configuration */
+struct adsp_audio_amr_enc_cfg {
+ u32 mode; /* ADSP_AUDIO_AMR_MR* */
+ u32 dtx_mode; /* ADSP_AUDIO_AMR_DTX_MODE* */
+ u32 enable; /* 1 = enable, 0 = disable */
+} __attribute__ ((packed));
+
+struct adsp_audio_qcelp13k_enc_cfg {
+ u16 min_rate;
+ u16 max_rate;
+} __attribute__ ((packed));
+
+struct adsp_audio_evrc_enc_cfg {
+ u16 min_rate;
+ u16 max_rate;
+} __attribute__ ((packed));
+
+union adsp_audio_codec_config {
+ struct adsp_audio_amr_enc_cfg amr;
+ struct adsp_audio_aac_enc_cfg aac;
+ struct adsp_audio_qcelp13k_enc_cfg qcelp13k;
+ struct adsp_audio_evrc_enc_cfg evrc;
+ struct adsp_audio_sbc_encoder_cfg sbc;
+} __attribute__ ((packed));
+
+
+/* This is the default value. */
+#define ADSP_AUDIO_OPEN_STREAM_MODE_NONE 0x0000
+
+/* This bit, if set, indicates that the AVSync mode is activated. */
+#define ADSP_AUDIO_OPEN_STREAM_MODE_AVSYNC 0x0001
+
+/* This bit, if set, indicates that the Sample Rate/Channel Mode */
+/* Change Notification mode is activated. */
+#define ADSP_AUDIO_OPEN_STREAM_MODE_SR_CM_NOTIFY 0x0002
+
+/* This bit, if set, indicates that the sync clock is enabled */
+#define ADSP_AUDIO_OPEN_STREAM_MODE_ENABLE_SYNC_CLOCK 0x0004
+
+/* This bit, if set, indicates that the AUX PCM loopback is enabled */
+#define ADSP_AUDIO_OPEN_STREAM_MODE_AUX_PCM 0x0040
+
+struct adsp_open_command {
+ struct adsp_command_hdr hdr;
+
+ u32 device;
+ u32 endpoint; /* address */
+
+ u32 stream_context;
+ u32 mode;
+
+ u32 buf_max_size;
+
+ union adsp_audio_format format;
+ union adsp_audio_codec_config config;
+} __attribute__ ((packed));
+
+
+/* --- audio control and stream session ioctls ---- */
+
+/* Opcode to open a device stream session to capture audio */
+#define ADSP_AUDIO_IOCTL_CMD_OPEN_READ 0x0108dd79
+
+/* Opcode to open a device stream session to render audio */
+#define ADSP_AUDIO_IOCTL_CMD_OPEN_WRITE 0x0108dd7a
+
+/* Opcode to open a device session, must open a device */
+#define ADSP_AUDIO_IOCTL_CMD_OPEN_DEVICE 0x0108dd7b
+
+/* Close an existing stream or device */
+#define ADSP_AUDIO_IOCTL_CMD_CLOSE 0x0108d8bc
+
+
+
+/* A device switch requires three IOCTL */
+/* commands in the following sequence: PREPARE, STANDBY, COMMIT */
+
+/* adsp_audio_device_switch_command structure is needed for */
+/* DEVICE_SWITCH_PREPARE */
+
+/* Device switch protocol step #1. Pause old device and */
+/* generate silence for the old device. */
+#define ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_PREPARE 0x010815c4
+
+/* Device switch protocol step #2. Release old device, */
+/* create new device and generate silence for the new device. */
+
+/* When client receives ack for this IOCTL, the client can */
+/* start sending IOCTL commands to configure, calibrate and */
+/* change filter settings on the new device. */
+#define ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_STANDBY 0x010815c5
+
+/* Device switch protocol step #3. Start normal operations on new device */
+#define ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_COMMIT 0x01075ee7
+
+struct adsp_device_switch_command {
+ struct adsp_command_hdr hdr;
+ u32 old_device;
+ u32 new_device;
+ u8 device_class; /* 0 = i.rx, 1 = i.tx, 2 = e.rx, 3 = e.tx */
+ u8 device_type; /* 0 = rx, 1 = tx, 2 = both */
+} __attribute__ ((packed));
+
+
+
+/* --- audio control session ioctls ---- */
+
+#define ADSP_PATH_RX 0
+#define ADSP_PATH_TX 1
+#define ADSP_PATH_BOTH 2
+#define ADSP_PATH_TX_CNG_DIS 3
+
+struct adsp_audio_dtmf_start_command {
+ struct adsp_command_hdr hdr;
+ u32 tone1_hz;
+ u32 tone2_hz;
+ u32 duration_usec;
+ s32 gain_mb;
+} __attribute__ ((packed));
+
+/* These commands will affect a logical device and all its associated */
+/* streams. */
+
+#define ADSP_AUDIO_MAX_EQ_BANDS 12
+
+struct adsp_audio_eq_band {
+ u16 band_idx; /* The band index, 0 .. 11 */
+ u32 filter_type; /* Filter band type */
+ u32 center_freq_hz; /* Filter band center frequency */
+ s32 filter_gain; /* Filter band initial gain (dB) */
+ /* Range is +12 dB to -12 dB with 1dB increments. */
+ s32 q_factor;
+ /* Filter band quality factor expressed as q-8 number, */
+ /* e.g. 3000/(2^8) */
+} __attribute__ ((packed));
+
+struct adsp_audio_eq_stream_config {
+ uint32_t enable; /* Number of consequtive bands specified */
+ uint32_t num_bands;
+ struct adsp_audio_eq_band eq_bands[ADSP_AUDIO_MAX_EQ_BANDS];
+} __attribute__ ((packed));
+
+/* set device equalizer */
+struct adsp_set_dev_equalizer_command {
+ struct adsp_command_hdr hdr;
+ u32 device_id;
+ u32 enable;
+ u32 num_bands;
+ struct adsp_audio_eq_band eq_bands[ADSP_AUDIO_MAX_EQ_BANDS];
+} __attribute__ ((packed));
+
+/* Set device volume. */
+#define ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_VOL 0x0107605c
+
+struct adsp_set_dev_volume_command {
+ struct adsp_command_hdr hdr;
+ u32 device_id;
+ u32 path; /* 0 = rx, 1 = tx, 2 = both */
+ s32 volume;
+} __attribute__ ((packed));
+
+/* Set Device stereo volume. This command has data payload, */
+/* struct adsp_audio_set_dev_stereo_volume_command. */
+#define ADSP_AUDIO_IOCTL_SET_DEVICE_STEREO_VOL 0x0108df3e
+
+/* Set L, R cross channel gain for a Device. This command has */
+/* data payload, struct adsp_audio_set_dev_x_chan_gain_command. */
+#define ADSP_AUDIO_IOCTL_SET_DEVICE_XCHAN_GAIN 0x0108df40
+
+/* Set device mute state. */
+#define ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_MUTE 0x0107605f
+
+struct adsp_set_dev_mute_command {
+ struct adsp_command_hdr hdr;
+ u32 device_id;
+ u32 path; /* 0 = rx, 1 = tx, 2 = both */
+ u32 mute; /* 1 = mute */
+} __attribute__ ((packed));
+
+/* Configure Equalizer for a device. */
+/* This command has payload struct adsp_audio_set_dev_equalizer_command. */
+#define ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_EQ_CONFIG 0x0108b10e
+
+/* Set configuration data for an algorithm aspect of a device. */
+/* This command has payload struct adsp_audio_set_dev_cfg_command. */
+#define ADSP_AUDIO_IOCTL_SET_DEVICE_CONFIG 0x0108b6cb
+
+struct adsp_set_dev_cfg_command {
+ struct adsp_command_hdr hdr;
+ u32 device_id;
+ u32 block_id;
+ u32 interface_id;
+ u32 phys_addr;
+ u32 phys_size;
+ u32 phys_used;
+} __attribute__ ((packed));
+
+/* Set configuration data for all interfaces of a device. */
+#define ADSP_AUDIO_IOCTL_SET_DEVICE_CONFIG_TABLE 0x0108b6bf
+
+struct adsp_set_dev_cfg_table_command {
+ struct adsp_command_hdr hdr;
+ u32 device_id;
+ u32 phys_addr;
+ u32 phys_size;
+ u32 phys_used;
+} __attribute__ ((packed));
+
+/* ---- audio stream data commands ---- */
+
+#define ADSP_AUDIO_IOCTL_CMD_DATA_TX 0x0108dd7f
+#define ADSP_AUDIO_IOCTL_CMD_DATA_RX 0x0108dd80
+
+struct adsp_buffer_command {
+ struct adsp_command_hdr hdr;
+ struct adsp_audio_buffer buffer;
+} __attribute__ ((packed));
+
+
+
+/* ---- audio stream ioctls (only affect a single stream in a session) ---- */
+
+/* Stop stream for audio device. */
+#define ADSP_AUDIO_IOCTL_CMD_STREAM_STOP 0x01075c54
+
+/* End of stream reached. Client will not send any more data. */
+#define ADSP_AUDIO_IOCTL_CMD_STREAM_EOS 0x0108b150
+
+/* Do sample slipping/stuffing on AAC outputs. The payload of */
+/* this command is struct adsp_audio_slip_sample_command. */
+#define ADSP_AUDIO_IOCTL_CMD_STREAM_SLIPSAMPLE 0x0108d40e
+
+/* Set stream volume. */
+/* This command has data payload, struct adsp_audio_set_volume_command. */
+#define ADSP_AUDIO_IOCTL_CMD_SET_STREAM_VOL 0x0108c0de
+
+/* Set stream stereo volume. This command has data payload, */
+/* struct adsp_audio_set_stereo_volume_command. */
+#define ADSP_AUDIO_IOCTL_SET_STREAM_STEREO_VOL 0x0108dd7c
+
+/* Set L, R cross channel gain for a Stream. This command has */
+/* data payload, struct adsp_audio_set_x_chan_gain_command. */
+#define ADSP_AUDIO_IOCTL_SET_STREAM_XCHAN_GAIN 0x0108dd7d
+
+/* Set stream mute state. */
+/* This command has data payload, struct adsp_audio_set_stream_mute. */
+#define ADSP_AUDIO_IOCTL_CMD_SET_STREAM_MUTE 0x0108c0df
+
+/* Reconfigure bit rate information. This command has data */
+/* payload, struct adsp_audio_set_bit_rate_command */
+#define ADSP_AUDIO_IOCTL_SET_STREAM_BITRATE 0x0108ccf1
+
+/* Set Channel Mapping. This command has data payload, struct */
+/* This command has data payload struct adsp_audio_set_channel_map_command. */
+#define ADSP_AUDIO_IOCTL_SET_STREAM_CHANNELMAP 0x0108d32a
+
+/* Enable/disable AACPlus SBR. */
+/* This command has data payload struct adsp_audio_set_sbr_command */
+#define ADSP_AUDIO_IOCTL_SET_STREAM_SBR 0x0108d416
+
+/* Enable/disable WMA Pro Chex and Fex. This command has data payload */
+/* struct adsp_audio_stream_set_wma_command. */
+#define ADSP_AUDIO_IOCTL_SET_STREAM_WMAPRO 0x0108d417
+
+
+/* ---- audio session ioctls (affect all streams in a session) --- */
+
+/* Start stream for audio device. */
+#define ADSP_AUDIO_IOCTL_CMD_SESSION_START 0x010815c6
+
+/* Stop all stream(s) for audio session as indicated by major id. */
+#define ADSP_AUDIO_IOCTL_CMD_SESSION_STOP 0x0108dd7e
+
+/* Pause the data flow for a session as indicated by major id. */
+#define ADSP_AUDIO_IOCTL_CMD_SESSION_PAUSE 0x01075ee8
+
+/* Resume the data flow for a session as indicated by major id. */
+#define ADSP_AUDIO_IOCTL_CMD_SESSION_RESUME 0x01075ee9
+
+/* Drop any unprocessed data buffers for a session as indicated by major id. */
+#define ADSP_AUDIO_IOCTL_CMD_SESSION_FLUSH 0x01075eea
+
+/* Start Stream DTMF tone */
+#define ADSP_AUDIO_IOCTL_CMD_SESSION_DTMF_START 0x0108c0dd
+
+/* Stop Stream DTMF tone */
+#define ADSP_AUDIO_IOCTL_CMD_SESSION_DTMF_STOP 0x01087554
+
+/* Set Session volume. */
+/* This command has data payload, struct adsp_audio_set_volume_command. */
+#define ADSP_AUDIO_IOCTL_SET_SESSION_VOL 0x0108d8bd
+
+/* Set session stereo volume. This command has data payload, */
+/* struct adsp_audio_set_stereo_volume_command. */
+#define ADSP_AUDIO_IOCTL_SET_SESSION_STEREO_VOL 0x0108df3d
+
+/* Set L, R cross channel gain for a session. This command has */
+/* data payload, struct adsp_audio_set_x_chan_gain_command. */
+#define ADSP_AUDIO_IOCTL_SET_SESSION_XCHAN_GAIN 0x0108df3f
+
+/* Set Session mute state. */
+/* This command has data payload, struct adsp_audio_set_mute_command. */
+#define ADSP_AUDIO_IOCTL_SET_SESSION_MUTE 0x0108d8be
+
+/* Configure Equalizer for a stream. */
+/* This command has payload struct adsp_audio_set_equalizer_command. */
+#define ADSP_AUDIO_IOCTL_SET_SESSION_EQ_CONFIG 0x0108c0e0
+
+/* Set Audio Video sync information. */
+/* This command has data payload, struct adsp_audio_set_av_sync_command. */
+#define ADSP_AUDIO_IOCTL_SET_SESSION_AVSYNC 0x0108d1e2
+
+/* Get Audio Media Session time. */
+/* This command returns the audioTime in adsp_audio_unsigned64_event */
+#define ADSP_AUDIO_IOCTL_CMD_GET_AUDIO_TIME 0x0108c26c
+
+
+/* these command structures are used for both STREAM and SESSION ioctls */
+
+struct adsp_set_volume_command {
+ struct adsp_command_hdr hdr;
+ s32 volume;
+} __attribute__ ((packed));
+
+struct adsp_set_mute_command {
+ struct adsp_command_hdr hdr;
+ u32 mute; /* 1 == mute */
+} __attribute__ ((packed));
+
+
+struct adsp_set_equalizer_command {
+ struct adsp_command_hdr hdr;
+ u32 enable;
+ u32 num_bands;
+ struct adsp_audio_eq_band eq_bands[ADSP_AUDIO_MAX_EQ_BANDS];
+} __attribute__ ((packed));
+
+/* ---- audio events ---- */
+
+/* All IOCTL commands generate an event with the IOCTL opcode as the */
+/* event id after the IOCTL command has been executed. */
+
+/* This event is generated after a media stream session is opened. */
+#define ADSP_AUDIO_EVT_STATUS_OPEN 0x0108c0d6
+
+/* This event is generated after a media stream session is closed. */
+#define ADSP_AUDIO_EVT_STATUS_CLOSE 0x0108c0d7
+
+/* Asyncronous buffer consumption. This event is generated after a */
+/* recived buffer is consumed during rendering or filled during */
+/* capture opeartion. */
+#define ADSP_AUDIO_EVT_STATUS_BUF_DONE 0x0108c0d8
+
+/* This event is generated when rendering operation is starving for */
+/* data. In order to avoid audio loss at the end of a plauback, the */
+/* client should wait for this event before issuing the close command. */
+#define ADSP_AUDIO_EVT_STATUS_BUF_UNDERRUN 0x0108c0d9
+
+/* This event is generated during capture operation when there are no */
+/* buffers available to copy the captured audio data */
+#define ADSP_AUDIO_EVT_STATUS_BUF_OVERFLOW 0x0108c0da
+
+/* This asynchronous event is generated as a result of an input */
+/* sample rate change and/or channel mode change detected by the */
+/* decoder. The event payload data is an array of 2 uint32 */
+/* values containing the sample rate in Hz and channel mode. */
+#define ADSP_AUDIO_EVT_SR_CM_CHANGE 0x0108d329
+
+struct adsp_event_hdr {
+ u32 evt_handle; /* DAL common header */
+ u32 evt_cookie;
+ u32 evt_length;
+
+ u32 src; /* "source" audio address */
+ u32 dst; /* "destination" audio address */
+
+ u32 event_id;
+ u32 response_type;
+ u32 seq_number;
+
+ u32 context; /* opaque to DSP */
+ u32 data;
+
+ u32 status;
+} __attribute__ ((packed));
+
+struct adsp_buffer_event {
+ struct adsp_event_hdr hdr;
+ struct adsp_audio_buffer buffer;
+} __attribute__ ((packed));
+
+
+/* ---- audio device IDs ---- */
+
+/* Device direction Rx/Tx flag */
+#define ADSP_AUDIO_RX_DEVICE 0x00
+#define ADSP_AUDIO_TX_DEVICE 0x01
+
+/* Default RX or TX device */
+#define ADSP_AUDIO_DEVICE_ID_DEFAULT 0x1081679
+
+/* Source (TX) devices */
+#define ADSP_AUDIO_DEVICE_ID_HANDSET_MIC 0x107ac8d
+#define ADSP_AUDIO_DEVICE_ID_HEADSET_MIC 0x1081510
+#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MIC 0x1081512
+#define ADSP_AUDIO_DEVICE_ID_BT_SCO_MIC 0x1081518
+#define ADSP_AUDIO_DEVICE_ID_AUXPCM_TX 0x1081518
+#define ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_MIC 0x108151b
+#define ADSP_AUDIO_DEVICE_ID_I2S_MIC 0x1089bf3
+
+#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_DUAL_MIC 0x108f9c5
+#define ADSP_AUDIO_DEVICE_ID_HANDSET_DUAL_MIC 0x108f9c3
+
+/* Special loopback pseudo device to be paired with an RX device */
+/* with usage ADSP_AUDIO_DEVICE_USAGE_MIXED_PCM_LOOPBACK */
+#define ADSP_AUDIO_DEVICE_ID_MIXED_PCM_LOOPBACK_TX 0x1089bf2
+
+/* Sink (RX) devices */
+#define ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR 0x107ac88
+#define ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_MONO 0x1081511
+#define ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_STEREO 0x107ac8a
+#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO 0x1081513
+#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_MONO_HEADSET 0x108c508
+#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_STEREO_HEADSET 0x108c894
+#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO 0x1081514
+#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_MONO_HEADSET 0x108c895
+#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_STEREO_HEADSET 0x108c509
+#define ADSP_AUDIO_DEVICE_ID_BT_SCO_SPKR 0x1081519
+#define ADSP_AUDIO_DEVICE_ID_AUXPCM_RX 0x1081519
+#define ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_SPKR 0x108151c
+#define ADSP_AUDIO_DEVICE_ID_I2S_SPKR 0x1089bf4
+#define ADSP_AUDIO_DEVICE_ID_NULL_SINK 0x108e512
+
+/* BT A2DP playback device. */
+/* This device must be paired with */
+/* ADSP_AUDIO_DEVICE_ID_MIXED_PCM_LOOPBACK_TX using */
+/* ADSP_AUDIO_DEVICE_USAGE_MIXED_PCM_LOOPBACK mode */
+#define ADSP_AUDIO_DEVICE_ID_BT_A2DP_SPKR 0x108151a
+
+/* Voice Destination identifier - specifically used for */
+/* controlling Voice module from the Device Control Session */
+#define ADSP_AUDIO_DEVICE_ID_VOICE 0x0108df3c
+
+/* Audio device usage types. */
+/* This is a bit mask to determine which topology to use in the */
+/* device session */
+#define ADSP_AUDIO_DEVICE_CONTEXT_VOICE 0x01
+#define ADSP_AUDIO_DEVICE_CONTEXT_PLAYBACK 0x02
+#define ADSP_AUDIO_DEVICE_CONTEXT_MIXED_RECORD 0x10
+#define ADSP_AUDIO_DEVICE_CONTEXT_RECORD 0x20
+#define ADSP_AUDIO_DEVICE_CONTEXT_PCM_LOOPBACK 0x40
+
+/* ADSP audio driver return codes */
+#define ADSP_AUDIO_STATUS_SUCCESS 0
+#define ADSP_AUDIO_STATUS_EUNSUPPORTED 20
+
+#endif
diff --git a/arch/arm/mach-msm/qdsp6/dal_audio_format.h b/arch/arm/mach-msm/qdsp6/dal_audio_format.h
new file mode 100644
index 0000000..6382693
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/dal_audio_format.h
@@ -0,0 +1,270 @@
+/* Copyright (c) 2009, 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 __ADSP_AUDIO_MEDIA_FORMAT_H
+#define __ADSP_AUDIO_MEDIA_FORMAT_H
+
+
+
+/* Supported audio media formats */
+
+/* format block in shmem */
+#define ADSP_AUDIO_FORMAT_SHAREDMEMORY 0x01091a78
+/* adsp_audio_format_raw_pcm type */
+#define ADSP_AUDIO_FORMAT_PCM 0x0103d2fd
+/* adsp_audio_format_raw_pcm type */
+#define ADSP_AUDIO_FORMAT_DTMF 0x01087725
+/* adsp_audio_format_adpcm type */
+#define ADSP_AUDIO_FORMAT_ADPCM 0x0103d2ff
+/* Yamaha PCM format */
+#define ADSP_AUDIO_FORMAT_YADPCM 0x0108dc07
+/* ISO/IEC 11172 */
+#define ADSP_AUDIO_FORMAT_MP3 0x0103d308
+/* ISO/IEC 14496 */
+#define ADSP_AUDIO_FORMAT_MPEG4_AAC 0x010422f1
+/* AMR-NB audio in FS format */
+#define ADSP_AUDIO_FORMAT_AMRNB_FS 0x0105c16c
+/* AMR-WB audio in FS format */
+#define ADSP_AUDIO_FORMAT_AMRWB_FS 0x0105c16e
+/* QCELP 13k, IS733 */
+#define ADSP_AUDIO_FORMAT_V13K_FS 0x01080b8a
+/* EVRC 8k, IS127 */
+#define ADSP_AUDIO_FORMAT_EVRC_FS 0x01080b89
+/* EVRC-B 8k, 4GV */
+#define ADSP_AUDIO_FORMAT_EVRCB_FS 0x0108f2a3
+/* MIDI command stream */
+#define ADSP_AUDIO_FORMAT_MIDI 0x0103d300
+/* A2DP SBC stream */
+#define ADSP_AUDIO_FORMAT_SBC 0x0108c4d8
+/* Version 10 Professional */
+#define ADSP_AUDIO_FORMAT_WMA_V10PRO 0x0108aa92
+/* Version 9 Starndard */
+#define ADSP_AUDIO_FORMAT_WMA_V9 0x0108d430
+/* AMR WideBand Plus */
+#define ADSP_AUDIO_FORMAT_AMR_WB_PLUS 0x0108f3da
+/* AC3 Decoder */
+#define ADSP_AUDIO_FORMAT_AC3_DECODER 0x0108d5f9
+
+
+/* Not yet supported audio media formats */
+
+
+
+/* ISO/IEC 13818 */
+#define ADSP_AUDIO_FORMAT_MPEG2_AAC 0x0103d309
+/* 3GPP TS 26.101 Sec 4.0 */
+#define ADSP_AUDIO_FORMAT_AMRNB_IF1 0x0103d305
+/* 3GPP TS 26.101 Annex A */
+#define ADSP_AUDIO_FORMAT_AMRNB_IF2 0x01057b31
+/* 3GPP TS 26.201 */
+#define ADSP_AUDIO_FORMAT_AMRWB_IF1 0x0103d306
+/* 3GPP TS 26.201 */
+#define ADSP_AUDIO_FORMAT_AMRWB_IF2 0x0105c16d
+/* G.711 */
+#define ADSP_AUDIO_FORMAT_G711 0x0106201d
+/* QCELP 8k, IS96A */
+#define ADSP_AUDIO_FORMAT_V8K_FS 0x01081d29
+/* Version 1 codec */
+#define ADSP_AUDIO_FORMAT_WMA_V1 0x01055b2b
+/* Version 2, 7 & 8 codec */
+#define ADSP_AUDIO_FORMAT_WMA_V8 0x01055b2c
+/* Version 9 Professional codec */
+#define ADSP_AUDIO_FORMAT_WMA_V9PRO 0x01055b2d
+/* Version 9 Voice codec */
+#define ADSP_AUDIO_FORMAT_WMA_SP1 0x01055b2e
+/* Version 9 Lossless codec */
+#define ADSP_AUDIO_FORMAT_WMA_LOSSLESS 0x01055b2f
+/* Real Media content, low-bitrate */
+#define ADSP_AUDIO_FORMAT_RA_SIPR 0x01042a0f
+/* Real Media content */
+#define ADSP_AUDIO_FORMAT_RA_COOK 0x01042a0e
+
+
+/* For all of the audio formats, unless specified otherwise, */
+/* the following apply: */
+/* Format block bits are arranged in bytes and words in little-endian */
+/* order, i.e., least-significant bit first and least-significant */
+/* byte first. */
+
+
+
+/* AAC Format Block. */
+
+/* AAC format block consist of a format identifier followed by */
+/* AudioSpecificConfig formatted according to ISO/IEC 14496-3 */
+
+/* The following AAC format identifiers are supported */
+#define ADSP_AUDIO_AAC_ADTS 0x010619cf
+#define ADSP_AUDIO_AAC_MPEG4_ADTS 0x010619d0
+#define ADSP_AUDIO_AAC_LOAS 0x010619d1
+#define ADSP_AUDIO_AAC_ADIF 0x010619d2
+#define ADSP_AUDIO_AAC_RAW 0x010619d3
+#define ADSP_AUDIO_AAC_FRAMED_RAW 0x0108c1fb
+
+
+#define ADSP_AUDIO_COMPANDING_ALAW 0x10619cd
+#define ADSP_AUDIO_COMPANDING_MLAW 0x10619ce
+
+/* Maxmum number of bytes allowed in a format block */
+#define ADSP_AUDIO_FORMAT_DATA_MAX 16
+
+
+struct adsp_audio_no_payload_format {
+ /* Media Format Code (must always be first element) */
+ u32 format;
+
+ /* no payload for this format type */
+} __attribute__ ((packed));
+
+
+/* For convenience, to be used as a standard format block */
+/* for various media types that don't need a unique format block */
+/* ie. PCM, DTMF, etc. */
+struct adsp_audio_standard_format {
+ /* Media Format Code (must always be first element) */
+ u32 format;
+
+ /* payload */
+ u16 channels;
+ u16 bits_per_sample;
+ u32 sampling_rate;
+ u8 is_signed;
+ u8 is_interleaved;
+} __attribute__ ((packed));
+
+
+
+/* ADPCM format block */
+struct adsp_audio_adpcm_format {
+ /* Media Format Code (must always be first element) */
+ u32 format;
+
+ /* payload */
+ u16 channels;
+ u16 bits_per_sample;
+ u32 sampling_rate;
+ u8 is_signed;
+ u8 is_interleaved;
+ u32 block_size;
+} __attribute__ ((packed));
+
+
+/* MIDI format block */
+struct adsp_audio_midi_format {
+ /* Media Format Code (must always be first element) */
+ u32 format;
+
+ /* payload */
+ u32 sampling_rate;
+ u16 channels;
+ u16 mode;
+} __attribute__ ((packed));
+
+
+/* G711 format block */
+struct adsp_audio_g711_format {
+ /* Media Format Code (must always be first element) */
+ u32 format;
+
+ /* payload */
+ u32 companding;
+} __attribute__ ((packed));
+
+
+struct adsp_audio_wma_pro_format {
+ /* Media Format Code (must always be first element) */
+ u32 format;
+
+ /* payload */
+ u16 format_tag;
+ u16 channels;
+ u32 samples_per_sec;
+ u32 avg_bytes_per_sec;
+ u16 block_align;
+ u16 valid_bits_per_sample;
+ u32 channel_mask;
+ u16 encode_opt;
+ u16 advanced_encode_opt;
+ u32 advanced_encode_opt2;
+ u32 drc_peak_reference;
+ u32 drc_peak_target;
+ u32 drc_average_reference;
+ u32 drc_average_target;
+} __attribute__ ((packed));
+
+
+struct adsp_audio_amrwb_plus_format {
+ /* Media Format Code (must always be first element) */
+ u32 format;
+
+ /* payload */
+ u32 size;
+ u32 version;
+ u32 channels;
+ u32 amr_band_mode;
+ u32 amr_dtx_mode;
+ u32 amr_frame_format;
+ u32 amr_isf_index;
+} __attribute__ ((packed));
+
+
+/* Binary Byte Stream Format */
+/* Binary format type that defines a byte stream, */
+/* can be used to specify any format (ie. AAC) */
+struct adsp_audio_binary_format {
+ /* Media Format Code (must always be first element) */
+ u32 format;
+
+ /* payload */
+ /* number of bytes set in byte stream */
+ u32 num_bytes;
+ /* Byte stream binary data */
+ u8 data[ADSP_AUDIO_FORMAT_DATA_MAX];
+} __attribute__ ((packed));
+
+
+struct adsp_audio_shared_memory_format {
+ /* Media Format Code (must always be first element) */
+ u32 format;
+
+ /* Number of bytes in shared memory */
+ u32 len;
+ /* Phyisical address to data in shared memory */
+ u32 address;
+} __attribute__ ((packed));
+
+
+/* Union of all format types */
+union adsp_audio_format {
+ /* Basic format block with no payload */
+ struct adsp_audio_no_payload_format no_payload;
+ /* Generic format block PCM, DTMF */
+ struct adsp_audio_standard_format standard;
+ /* ADPCM format block */
+ struct adsp_audio_adpcm_format adpcm;
+ /* MIDI format block */
+ struct adsp_audio_midi_format midi;
+ /* G711 format block */
+ struct adsp_audio_g711_format g711;
+ /* WmaPro format block */
+ struct adsp_audio_wma_pro_format wma_pro;
+ /* WmaPro format block */
+ struct adsp_audio_amrwb_plus_format amrwb_plus;
+ /* binary (byte stream) format block, used for AAC */
+ struct adsp_audio_binary_format binary;
+ /* format block in shared memory */
+ struct adsp_audio_shared_memory_format shared_mem;
+};
+
+#endif
+
diff --git a/arch/arm/mach-msm/qdsp6/dsp_debug.c b/arch/arm/mach-msm/qdsp6/dsp_debug.c
new file mode 100644
index 0000000..fdf049c
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/dsp_debug.c
@@ -0,0 +1,179 @@
+/* arch/arm/mach-msm/qdsp6/dsp_dump.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Author: Brian Swetland <swetland@google.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/io.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <asm/atomic.h>
+
+#include "../proc_comm.h"
+#include <mach/debug_mm.h>
+
+static wait_queue_head_t dsp_wait;
+static int dsp_has_crashed;
+static int dsp_wait_count;
+
+static atomic_t dsp_crash_count = ATOMIC_INIT(0);
+
+void q6audio_dsp_not_responding(void)
+{
+
+ if (atomic_add_return(1, &dsp_crash_count) != 1) {
+ pr_err("q6audio_dsp_not_responding() - parking additional crasher...\n");
+ for (;;)
+ msleep(1000);
+ }
+ if (dsp_wait_count) {
+ dsp_has_crashed = 1;
+ wake_up(&dsp_wait);
+
+ while (dsp_has_crashed != 2)
+ wait_event(dsp_wait, dsp_has_crashed == 2);
+ } else {
+ pr_err("q6audio_dsp_not_responding() - no waiter?\n");
+ }
+ BUG();
+}
+
+static int dsp_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static ssize_t dsp_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ char cmd[32];
+
+ if (count >= sizeof(cmd))
+ return -EINVAL;
+ if (copy_from_user(cmd, buf, count))
+ return -EFAULT;
+ cmd[count] = 0;
+
+ if ((count > 1) && (cmd[count-1] == '\n'))
+ cmd[count-1] = 0;
+
+ if (!strcmp(cmd, "wait-for-crash")) {
+ while (!dsp_has_crashed) {
+ int res;
+ dsp_wait_count++;
+ res = wait_event_interruptible(dsp_wait, dsp_has_crashed);
+ if (res < 0) {
+ dsp_wait_count--;
+ return res;
+ }
+ }
+#if defined(CONFIG_MACH_MAHIMAHI)
+ /* assert DSP NMI */
+ msm_proc_comm(PCOM_CUSTOMER_CMD1, 0, 0);
+ msleep(250);
+#endif
+ } else if (!strcmp(cmd, "boom")) {
+ q6audio_dsp_not_responding();
+ } else if (!strcmp(cmd, "continue-crash")) {
+ dsp_has_crashed = 2;
+ wake_up(&dsp_wait);
+ } else {
+ pr_err("[%s:%s] unknown dsp_debug command: %s\n", __MM_FILE__,
+ __func__, cmd);
+ }
+
+ return count;
+}
+
+#define DSP_RAM_BASE 0x2E800000
+#define DSP_RAM_SIZE 0x01800000
+
+static unsigned copy_ok_count;
+
+static ssize_t dsp_read(struct file *file, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ size_t actual = 0;
+ size_t mapsize = PAGE_SIZE;
+ unsigned addr;
+ void __iomem *ptr;
+
+ if (*pos >= DSP_RAM_SIZE)
+ return 0;
+
+ if (*pos & (PAGE_SIZE - 1))
+ return -EINVAL;
+
+ addr = (*pos + DSP_RAM_BASE);
+
+ /* don't blow up if we're unaligned */
+ if (addr & (PAGE_SIZE - 1))
+ mapsize *= 2;
+
+ while (count >= PAGE_SIZE) {
+ ptr = ioremap(addr, mapsize);
+ if (!ptr) {
+ pr_err("[%s:%s] map error @ %x\n", __MM_FILE__,
+ __func__, addr);
+ return -EFAULT;
+ }
+ if (copy_to_user(buf, ptr, PAGE_SIZE)) {
+ iounmap(ptr);
+ pr_err("[%s:%s] copy error @ %p\n", __MM_FILE__,
+ __func__, buf);
+ return -EFAULT;
+ }
+ copy_ok_count += PAGE_SIZE;
+ iounmap(ptr);
+ addr += PAGE_SIZE;
+ buf += PAGE_SIZE;
+ actual += PAGE_SIZE;
+ count -= PAGE_SIZE;
+ }
+
+ *pos += actual;
+ return actual;
+}
+
+static int dsp_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static const struct file_operations dsp_fops = {
+ .owner = THIS_MODULE,
+ .open = dsp_open,
+ .read = dsp_read,
+ .write = dsp_write,
+ .release = dsp_release,
+};
+
+static struct miscdevice dsp_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "dsp_debug",
+ .fops = &dsp_fops,
+};
+
+
+static int __init dsp_init(void)
+{
+ init_waitqueue_head(&dsp_wait);
+ return misc_register(&dsp_misc);
+}
+
+device_initcall(dsp_init);
diff --git a/arch/arm/mach-msm/qdsp6/dtmf.c b/arch/arm/mach-msm/qdsp6/dtmf.c
new file mode 100644
index 0000000..cf27488
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/dtmf.c
@@ -0,0 +1,126 @@
+/* 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/slab.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+
+#include <linux/msm_audio.h>
+
+#include <mach/msm_qdsp6_audio.h>
+#include <mach/debug_mm.h>
+
+struct dtmf {
+ struct mutex lock;
+ struct audio_client *ac;
+ struct msm_dtmf_config cfg;
+};
+
+static long dtmf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct dtmf *dtmf = file->private_data;
+ int rc = 0;
+
+ mutex_lock(&dtmf->lock);
+ switch (cmd) {
+
+ case AUDIO_START: {
+ pr_debug("[%s:%s] AUDIO_START\n", __MM_FILE__, __func__);
+ if (dtmf->ac) {
+ pr_err("[%s:%s] active session already existing\n",
+ __MM_FILE__, __func__);
+ rc = -EBUSY;
+ } else {
+ dtmf->ac = q6audio_open_dtmf(48000, 2, 0);
+ if (!dtmf->ac)
+ rc = -ENOMEM;
+ }
+ break;
+ }
+ case AUDIO_PLAY_DTMF: {
+ rc = copy_from_user((void *)&dtmf->cfg, (void *)arg,
+ sizeof(struct msm_dtmf_config));
+
+ pr_debug("[%s:%s] PLAY_DTMF: high = %d, low = %d\n",
+ __MM_FILE__, __func__, dtmf->cfg.dtmf_hi,
+ dtmf->cfg.dtmf_low);
+ rc = q6audio_play_dtmf(dtmf->ac, dtmf->cfg.dtmf_hi,
+ dtmf->cfg.dtmf_low, dtmf->cfg.duration,
+ dtmf->cfg.rx_gain);
+ if (rc) {
+ pr_err("[%s:%s] DTMF_START failed\n", __MM_FILE__,
+ __func__);
+ break;
+ }
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&dtmf->lock);
+
+ pr_debug("[%s:%s] rc = %d\n", __MM_FILE__, __func__, rc) ;
+ return rc;
+}
+
+static int dtmf_open(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+
+ struct dtmf *dtmf;
+ pr_info("[%s:%s] open\n", __MM_FILE__, __func__);
+ dtmf = kzalloc(sizeof(struct dtmf), GFP_KERNEL);
+
+ if (!dtmf)
+ return -ENOMEM;
+
+ mutex_init(&dtmf->lock);
+
+ file->private_data = dtmf;
+ return rc;
+}
+
+static int dtmf_release(struct inode *inode, struct file *file)
+{
+ struct dtmf *dtmf = file->private_data;
+ if (dtmf->ac)
+ q6audio_close(dtmf->ac);
+ kfree(dtmf);
+ pr_info("[%s:%s] release\n", __MM_FILE__, __func__);
+ return 0;
+}
+
+static const struct file_operations dtmf_fops = {
+ .owner = THIS_MODULE,
+ .open = dtmf_open,
+ .release = dtmf_release,
+ .unlocked_ioctl = dtmf_ioctl,
+};
+
+struct miscdevice dtmf_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_dtmf",
+ .fops = &dtmf_fops,
+};
+
+static int __init dtmf_init(void)
+{
+ return misc_register(&dtmf_misc);
+}
+
+device_initcall(dtmf_init);
diff --git a/arch/arm/mach-msm/qdsp6/evrc_in.c b/arch/arm/mach-msm/qdsp6/evrc_in.c
new file mode 100644
index 0000000..9fc412b
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/evrc_in.c
@@ -0,0 +1,468 @@
+/*
+ * Copyright (C) 2009 Google, Inc.
+ * Copyright (C) 2009 HTC Corporation
+ * Copyright (c) 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/slab.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+
+#include <linux/msm_audio_qcp.h>
+#include <mach/msm_qdsp6_audio.h>
+#include "dal_audio_format.h"
+#include <mach/debug_mm.h>
+
+#define EVRC_FC_BUFF_CNT 10
+#define EVRC_READ_TIMEOUT 2000
+struct evrc_fc_buff {
+ struct mutex lock;
+ int empty;
+ void *data;
+ int size;
+ int actual_size;
+};
+
+struct evrc_fc {
+ struct task_struct *task;
+ wait_queue_head_t fc_wq;
+ struct evrc_fc_buff fc_buff[EVRC_FC_BUFF_CNT];
+ int buff_index;
+};
+
+struct evrc {
+ struct mutex lock;
+ struct msm_audio_evrc_enc_config cfg;
+ struct msm_audio_stream_config str_cfg;
+ struct audio_client *audio_client;
+ struct msm_voicerec_mode voicerec_mode;
+ struct evrc_fc *evrc_fc;
+};
+
+
+static int q6_evrc_flowcontrol(void *data)
+{
+ struct audio_client *ac;
+ struct audio_buffer *ab;
+ struct evrc *evrc = data;
+ int buff_index = 0;
+ int xfer = 0;
+ struct evrc_fc *fc;
+
+
+ ac = evrc->audio_client;
+ fc = evrc->evrc_fc;
+ if (!ac) {
+ pr_err("[%s:%s] audio_client is NULL\n", __MM_FILE__, __func__);
+ return 0;
+ }
+
+ while (!kthread_should_stop()) {
+ ab = ac->buf + ac->cpu_buf;
+ if (ab->used)
+ wait_event(ac->wait, (ab->used == 0));
+ pr_debug("[%s:%s] ab->data = %p, cpu_buf = %d\n", __MM_FILE__,
+ __func__, ab->data, ac->cpu_buf);
+ xfer = ab->actual_size;
+
+
+ mutex_lock(&(fc->fc_buff[buff_index].lock));
+ if (!fc->fc_buff[buff_index].empty) {
+ pr_err("[%s:%s] flow control buffer[%d] not read!\n",
+ __MM_FILE__, __func__, buff_index);
+ }
+
+ if (fc->fc_buff[buff_index].size < xfer) {
+ pr_err("[%s:%s] buffer %d too small\n", __MM_FILE__,
+ __func__, buff_index);
+ memcpy(fc->fc_buff[buff_index].data, ab->data,
+ fc->fc_buff[buff_index].size);
+ fc->fc_buff[buff_index].empty = 0;
+ fc->fc_buff[buff_index].actual_size =
+ fc->fc_buff[buff_index].size;
+ } else {
+ memcpy(fc->fc_buff[buff_index].data, ab->data, xfer);
+ fc->fc_buff[buff_index].empty = 0;
+ fc->fc_buff[buff_index].actual_size = xfer;
+ }
+ mutex_unlock(&(fc->fc_buff[buff_index].lock));
+ /*wake up client, if any*/
+ wake_up(&fc->fc_wq);
+
+ buff_index++;
+ if (buff_index >= EVRC_FC_BUFF_CNT)
+ buff_index = 0;
+
+ ab->used = 1;
+
+ q6audio_read(ac, ab);
+ ac->cpu_buf ^= 1;
+ }
+
+ return 0;
+}
+static long q6_evrc_in_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct evrc *evrc = file->private_data;
+ int rc = 0;
+ int i = 0;
+ struct evrc_fc *fc;
+ int size = 0;
+
+ mutex_lock(&evrc->lock);
+ switch (cmd) {
+ case AUDIO_SET_VOLUME:
+ pr_debug("[%s:%s] SET_VOLUME\n", __MM_FILE__, __func__);
+ break;
+ case AUDIO_GET_STATS:
+ {
+ struct msm_audio_stats stats;
+ pr_debug("[%s:%s] GET_STATS\n", __MM_FILE__, __func__);
+ memset(&stats, 0, sizeof(stats));
+ if (copy_to_user((void *) arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ }
+ case AUDIO_START:
+ {
+ uint32_t acdb_id;
+ pr_debug("[%s:%s] AUDIO_START\n", __MM_FILE__, __func__);
+ if (arg == 0) {
+ acdb_id = 0;
+ } else {
+ if (copy_from_user(&acdb_id, (void *) arg,
+ sizeof(acdb_id))) {
+ rc = -EFAULT;
+ break;
+ }
+ }
+ if (evrc->audio_client) {
+ rc = -EBUSY;
+ pr_err("[%s:%s] active session already existing\n",
+ __MM_FILE__, __func__);
+ break;
+ } else {
+ evrc->audio_client = q6audio_open_qcp(
+ evrc->str_cfg.buffer_size,
+ evrc->cfg.min_bit_rate,
+ evrc->cfg.max_bit_rate,
+ evrc->voicerec_mode.rec_mode,
+ ADSP_AUDIO_FORMAT_EVRC_FS,
+ acdb_id);
+
+ if (!evrc->audio_client) {
+ pr_err("[%s:%s] evrc open session failed\n",
+ __MM_FILE__, __func__);
+ kfree(evrc);
+ rc = -ENOMEM;
+ break;
+ }
+ }
+
+ /*allocate flow control buffers*/
+ fc = evrc->evrc_fc;
+ size = evrc->str_cfg.buffer_size;
+ for (i = 0; i < EVRC_FC_BUFF_CNT; ++i) {
+ mutex_init(&(fc->fc_buff[i].lock));
+ fc->fc_buff[i].empty = 1;
+ fc->fc_buff[i].data = kmalloc(size, GFP_KERNEL);
+ if (fc->fc_buff[i].data == NULL) {
+ pr_err("[%s:%s] No memory for FC buffers\n",
+ __MM_FILE__, __func__);
+ rc = -ENOMEM;
+ goto fc_fail;
+ }
+ fc->fc_buff[i].size = size;
+ fc->fc_buff[i].actual_size = 0;
+ }
+
+ /*create flow control thread*/
+ fc->task = kthread_run(q6_evrc_flowcontrol,
+ evrc, "evrc_flowcontrol");
+ if (IS_ERR(fc->task)) {
+ rc = PTR_ERR(fc->task);
+ pr_err("[%s:%s] error creating flow control thread\n",
+ __MM_FILE__, __func__);
+ goto fc_fail;
+ }
+ break;
+fc_fail:
+ /*free flow control buffers*/
+ --i;
+ for (; i >= 0; i--) {
+ kfree(fc->fc_buff[i].data);
+ fc->fc_buff[i].data = NULL;
+ }
+ break;
+ }
+ case AUDIO_STOP:
+ pr_debug("[%s:%s] AUDIO_STOP\n", __MM_FILE__, __func__);
+ break;
+ case AUDIO_FLUSH:
+ break;
+ case AUDIO_SET_INCALL: {
+ pr_debug("[%s:%s] SET_INCALL\n", __MM_FILE__, __func__);
+ if (copy_from_user(&evrc->voicerec_mode,
+ (void *)arg, sizeof(struct msm_voicerec_mode)))
+ rc = -EFAULT;
+
+ if (evrc->voicerec_mode.rec_mode != AUDIO_FLAG_READ
+ && evrc->voicerec_mode.rec_mode !=
+ AUDIO_FLAG_INCALL_MIXED) {
+ evrc->voicerec_mode.rec_mode = AUDIO_FLAG_READ;
+ pr_err("[%s:%s] Invalid rec_mode\n", __MM_FILE__,
+ __func__);
+ rc = -EINVAL;
+ }
+ break;
+ }
+ case AUDIO_GET_STREAM_CONFIG:
+ if (copy_to_user((void *)arg, &evrc->str_cfg,
+ sizeof(struct msm_audio_stream_config)))
+ rc = -EFAULT;
+
+ pr_debug("[%s:%s] GET_STREAM_CONFIG: buffsz=%d, buffcnt=%d\n",
+ __MM_FILE__, __func__, evrc->str_cfg.buffer_size,
+ evrc->str_cfg.buffer_count);
+ break;
+ case AUDIO_SET_STREAM_CONFIG:
+ if (copy_from_user(&evrc->str_cfg, (void *)arg,
+ sizeof(struct msm_audio_stream_config))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ pr_debug("[%s:%s] SET_STREAM_CONFIG: buffsz=%d, buffcnt=%d\n",
+ __MM_FILE__, __func__, evrc->str_cfg.buffer_size,
+ evrc->str_cfg.buffer_count);
+
+ if (evrc->str_cfg.buffer_size < 23) {
+ pr_err("[%s:%s] Buffer size too small\n", __MM_FILE__,
+ __func__);
+ rc = -EINVAL;
+ break;
+ }
+
+ if (evrc->str_cfg.buffer_count != 2)
+ pr_info("[%s:%s] Buffer count set to 2\n", __MM_FILE__,
+ __func__);
+ break;
+ case AUDIO_SET_EVRC_ENC_CONFIG:
+ if (copy_from_user(&evrc->cfg, (void *) arg,
+ sizeof(struct msm_audio_evrc_enc_config)))
+ rc = -EFAULT;
+ pr_debug("[%s:%s] SET_EVRC_ENC_CONFIG\n", __MM_FILE__,
+ __func__);
+
+ if (evrc->cfg.min_bit_rate > 4 || evrc->cfg.min_bit_rate < 1) {
+ pr_err("[%s:%s] invalid min bitrate\n", __MM_FILE__,
+ __func__);
+ rc = -EINVAL;
+ }
+ if (evrc->cfg.max_bit_rate > 4 || evrc->cfg.max_bit_rate < 1) {
+ pr_err("[%s:%s] invalid max bitrate\n", __MM_FILE__,
+ __func__);
+ rc = -EINVAL;
+ }
+ break;
+ case AUDIO_GET_EVRC_ENC_CONFIG:
+ if (copy_to_user((void *) arg, &evrc->cfg,
+ sizeof(struct msm_audio_evrc_enc_config)))
+ rc = -EFAULT;
+ pr_debug("[%s:%s] GET_EVRC_ENC_CONFIG\n", __MM_FILE__,
+ __func__);
+ break;
+
+ default:
+ rc = -EINVAL;
+ }
+
+ mutex_unlock(&evrc->lock);
+ pr_debug("[%s:%s] rc = %d\n", __MM_FILE__, __func__, rc);
+ return rc;
+}
+
+static int q6_evrc_in_open(struct inode *inode, struct file *file)
+{
+ struct evrc *evrc;
+ struct evrc_fc *fc;
+ int i;
+
+ pr_info("[%s:%s] open\n", __MM_FILE__, __func__);
+ evrc = kmalloc(sizeof(struct evrc), GFP_KERNEL);
+ if (evrc == NULL) {
+ pr_err("[%s:%s] Could not allocate memory for evrc driver\n",
+ __MM_FILE__, __func__);
+ return -ENOMEM;
+ }
+
+ mutex_init(&evrc->lock);
+ file->private_data = evrc;
+ evrc->audio_client = NULL;
+ evrc->str_cfg.buffer_size = 23;
+ evrc->str_cfg.buffer_count = 2;
+ evrc->cfg.cdma_rate = CDMA_RATE_FULL;
+ evrc->cfg.min_bit_rate = 1;
+ evrc->cfg.max_bit_rate = 4;
+ evrc->voicerec_mode.rec_mode = AUDIO_FLAG_READ;
+
+ evrc->evrc_fc = kmalloc(sizeof(struct evrc_fc), GFP_KERNEL);
+ if (evrc->evrc_fc == NULL) {
+ pr_err("[%s:%s] Could not allocate memory for evrc_fc\n",
+ __MM_FILE__, __func__);
+ kfree(evrc);
+ return -ENOMEM;
+ }
+ fc = evrc->evrc_fc;
+ fc->task = NULL;
+ fc->buff_index = 0;
+ for (i = 0; i < EVRC_FC_BUFF_CNT; ++i) {
+ fc->fc_buff[i].data = NULL;
+ fc->fc_buff[i].size = 0;
+ fc->fc_buff[i].actual_size = 0;
+ }
+ /*initialize wait queue head*/
+ init_waitqueue_head(&fc->fc_wq);
+ return 0;
+}
+
+static ssize_t q6_evrc_in_read(struct file *file, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio_client *ac;
+ const char __user *start = buf;
+ struct evrc *evrc = file->private_data;
+ struct evrc_fc *fc;
+ int xfer = 0;
+ int res = 0;
+
+ pr_debug("[%s:%s] count = %d\n", __MM_FILE__, __func__, count);
+ mutex_lock(&evrc->lock);
+ ac = evrc->audio_client;
+ if (!ac) {
+ res = -ENODEV;
+ goto fail;
+ }
+ fc = evrc->evrc_fc;
+ while (count > xfer) {
+ /*wait for buffer to full*/
+ if (fc->fc_buff[fc->buff_index].empty != 0) {
+ res = wait_event_interruptible_timeout(fc->fc_wq,
+ (fc->fc_buff[fc->buff_index].empty == 0),
+ msecs_to_jiffies(EVRC_READ_TIMEOUT));
+
+ pr_debug("[%s:%s] buff_index = %d\n", __MM_FILE__,
+ __func__, fc->buff_index);
+ if (res == 0) {
+ pr_err("[%s:%s] Timeout!\n", __MM_FILE__,
+ __func__);
+ res = -ETIMEDOUT;
+ goto fail;
+ } else if (res < 0) {
+ pr_err("[%s:%s] Returning on Interrupt\n",
+ __MM_FILE__, __func__);
+ goto fail;
+ }
+ }
+ /*lock the buffer*/
+ mutex_lock(&(fc->fc_buff[fc->buff_index].lock));
+ xfer = fc->fc_buff[fc->buff_index].actual_size;
+
+ if (xfer > count) {
+ mutex_unlock(&(fc->fc_buff[fc->buff_index].lock));
+ pr_err("[%s:%s] read failed! byte count too small\n",
+ __MM_FILE__, __func__);
+ res = -EINVAL;
+ goto fail;
+ }
+
+ if (copy_to_user(buf, fc->fc_buff[fc->buff_index].data, xfer)) {
+ mutex_unlock(&(fc->fc_buff[fc->buff_index].lock));
+ pr_err("[%s:%s] copy_to_user failed at index %d\n",
+ __MM_FILE__, __func__, fc->buff_index);
+ res = -EFAULT;
+ goto fail;
+ }
+ buf += xfer;
+ count -= xfer;
+
+ fc->fc_buff[fc->buff_index].empty = 1;
+ fc->fc_buff[fc->buff_index].actual_size = 0;
+
+ mutex_unlock(&(fc->fc_buff[fc->buff_index].lock));
+ ++(fc->buff_index);
+ if (fc->buff_index >= EVRC_FC_BUFF_CNT)
+ fc->buff_index = 0;
+ }
+ res = buf - start;
+
+fail:
+ mutex_unlock(&evrc->lock);
+
+ return res;
+}
+
+static int q6_evrc_in_release(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+ struct evrc *evrc = file->private_data;
+ int i = 0;
+ struct evrc_fc *fc;
+
+ mutex_lock(&evrc->lock);
+ fc = evrc->evrc_fc;
+ kthread_stop(fc->task);
+ fc->task = NULL;
+ /*free flow control buffers*/
+ for (i = 0; i < EVRC_FC_BUFF_CNT; ++i) {
+ kfree(fc->fc_buff[i].data);
+ fc->fc_buff[i].data = NULL;
+ }
+ kfree(fc);
+ if (evrc->audio_client)
+ rc = q6audio_close(evrc->audio_client);
+ mutex_unlock(&evrc->lock);
+ kfree(evrc);
+ pr_info("[%s:%s] release\n", __MM_FILE__, __func__);
+ return rc;
+}
+
+static const struct file_operations q6_evrc_in_fops = {
+ .owner = THIS_MODULE,
+ .open = q6_evrc_in_open,
+ .read = q6_evrc_in_read,
+ .release = q6_evrc_in_release,
+ .unlocked_ioctl = q6_evrc_in_ioctl,
+};
+
+struct miscdevice q6_evrc_in_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_evrc_in",
+ .fops = &q6_evrc_in_fops,
+};
+
+static int __init q6_evrc_in_init(void)
+{
+ return misc_register(&q6_evrc_in_misc);
+}
+
+device_initcall(q6_evrc_in_init);
diff --git a/arch/arm/mach-msm/qdsp6/mp3.c b/arch/arm/mach-msm/qdsp6/mp3.c
new file mode 100644
index 0000000..16f6204
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/mp3.c
@@ -0,0 +1,249 @@
+/* arch/arm/mach-msm/qdsp6/mp3.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Copyright (C) 2009 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.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+
+#include <linux/msm_audio.h>
+
+#include <mach/msm_qdsp6_audio.h>
+#include <mach/debug_mm.h>
+
+#define BUFSZ (8192)
+#define DMASZ (BUFSZ * 2)
+
+struct mp3 {
+ struct mutex lock;
+ struct audio_client *ac;
+ uint32_t sample_rate;
+ uint32_t channel_count;
+};
+
+static long mp3_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct mp3 *mp3 = file->private_data;
+ int rc = 0;
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ memset(&stats, 0, sizeof(stats));
+ if (copy_to_user((void*) arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ }
+
+ mutex_lock(&mp3->lock);
+ switch (cmd) {
+ case AUDIO_SET_VOLUME: {
+ int vol;
+ pr_debug("[%s:%s] SET_VOLUME = %d\n", __MM_FILE__,
+ __func__, vol);
+ if (copy_from_user(&vol, (void*) arg, sizeof(vol))) {
+ rc = -EFAULT;
+ break;
+ }
+ rc = q6audio_set_stream_volume(mp3->ac, vol);
+ break;
+ }
+ case AUDIO_START: {
+ uint32_t acdb_id;
+ pr_debug("[%s:%s] AUDIO_START\n", __MM_FILE__, __func__);
+ if (arg == 0) {
+ acdb_id = 0;
+ } else if (copy_from_user(&acdb_id, (void*) arg, sizeof(acdb_id))) {
+ pr_info("[%s:%s] copy acdb_id from user failed\n",
+ __MM_FILE__, __func__);
+ rc = -EFAULT;
+ break;
+ }
+ if (mp3->ac) {
+ pr_err("[%s:%s] active session already existing\n",
+ __MM_FILE__, __func__);
+ rc = -EBUSY;
+ } else {
+ mp3->ac = q6audio_open_mp3(BUFSZ,
+ mp3->sample_rate, mp3->channel_count, acdb_id);
+ if (!mp3->ac) {
+ pr_err("[%s:%s] mp3 open session failed\n",
+ __MM_FILE__, __func__);
+ rc = -ENOMEM;
+ }
+ }
+ break;
+ }
+ case AUDIO_STOP:
+ pr_debug("[%s:%s] AUDIO_STOP\n", __MM_FILE__, __func__);
+ break;
+ case AUDIO_FLUSH:
+ break;
+ case AUDIO_SET_CONFIG: {
+ struct msm_audio_config config;
+ if (mp3->ac) {
+ rc = -EBUSY;
+ pr_err("[%s:%s] active session already existing\n",
+ __MM_FILE__, __func__);
+ break;
+ }
+ if (copy_from_user(&config, (void*) arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ pr_debug("[%s:%s] SET_CONFIG: buffsize = %d, samplerate = %d, \
+ channelcount = %d\n", __MM_FILE__, __func__,
+ config.buffer_size, config.sample_rate,
+ config.channel_count);
+ if (config.channel_count < 1 || config.channel_count > 2) {
+ rc = -EINVAL;
+ pr_err("[%s:%s] invalid channelcount\n", __MM_FILE__,
+ __func__);
+ break;
+ }
+ mp3->sample_rate = config.sample_rate;
+ mp3->channel_count = config.channel_count;
+ break;
+ }
+ case AUDIO_GET_CONFIG: {
+ struct msm_audio_config config;
+ config.buffer_size = BUFSZ;
+ config.buffer_count = 2;
+ config.sample_rate = mp3->sample_rate;
+ config.channel_count = mp3->channel_count;
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+ config.unused[2] = 0;
+ if (copy_to_user((void*) arg, &config, sizeof(config))) {
+ rc = -EFAULT;
+ }
+ pr_debug("[%s:%s] GET_CONFIG: buffsize = %d, samplerate = %d, \
+ channelcount = %d\n", __MM_FILE__, __func__,
+ config.buffer_size, config.sample_rate,
+ config.channel_count);
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&mp3->lock);
+ pr_debug("[%s:%s] rc = %d\n", __MM_FILE__, __func__, rc);
+ return rc;
+}
+
+static int mp3_open(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+
+ struct mp3 *mp3;
+ pr_info("[%s:%s] open\n", __MM_FILE__, __func__);
+ mp3 = kzalloc(sizeof(struct mp3), GFP_KERNEL);
+
+ if (!mp3)
+ return -ENOMEM;
+
+ mutex_init(&mp3->lock);
+ mp3->channel_count = 2;
+ mp3->sample_rate = 44100;
+
+ file->private_data = mp3;
+ return rc;
+}
+
+static ssize_t mp3_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct mp3 *mp3 = file->private_data;
+ struct audio_client *ac;
+ struct audio_buffer *ab;
+ const char __user *start = buf;
+ int xfer;
+
+ pr_debug("[%s:%s] count = %d\n", __MM_FILE__, __func__, count);
+ if (!mp3->ac)
+ mp3_ioctl(file, AUDIO_START, 0);
+
+ ac = mp3->ac;
+ if (!ac)
+ return -ENODEV;
+
+ while (count > 0) {
+ ab = ac->buf + ac->cpu_buf;
+
+ if (ab->used)
+ wait_event(ac->wait, (ab->used == 0));
+
+ pr_debug("[%s:%s] ab->data = %p, ac->cpu_buf = %d\n",
+ __MM_FILE__, __func__, ab->data, ac->cpu_buf);
+ xfer = count;
+ if (xfer > ab->size)
+ xfer = ab->size;
+
+ if (copy_from_user(ab->data, buf, xfer))
+ return -EFAULT;
+
+ buf += xfer;
+ count -= xfer;
+
+ ab->used = xfer;
+ q6audio_write(ac, ab);
+ ac->cpu_buf ^= 1;
+ }
+
+ return buf - start;
+}
+
+static int mp3_fsync(struct file *f, int datasync)
+{
+ struct mp3 *mp3 = f->private_data;
+ if (mp3->ac)
+ return q6audio_async(mp3->ac);
+ return -ENODEV;
+}
+
+static int mp3_release(struct inode *inode, struct file *file)
+{
+ struct mp3 *mp3 = file->private_data;
+ if (mp3->ac)
+ q6audio_mp3_close(mp3->ac);
+ kfree(mp3);
+ pr_info("[%s:%s] release\n", __MM_FILE__, __func__);
+ return 0;
+}
+
+static struct file_operations mp3_fops = {
+ .owner = THIS_MODULE,
+ .open = mp3_open,
+ .write = mp3_write,
+ .fsync = mp3_fsync,
+ .release = mp3_release,
+ .unlocked_ioctl = mp3_ioctl,
+};
+
+struct miscdevice mp3_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_mp3",
+ .fops = &mp3_fops,
+};
+
+static int __init mp3_init(void) {
+ return misc_register(&mp3_misc);
+}
+
+device_initcall(mp3_init);
diff --git a/arch/arm/mach-msm/qdsp6/msm_q6vdec.c b/arch/arm/mach-msm/qdsp6/msm_q6vdec.c
new file mode 100644
index 0000000..c79f0c4
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/msm_q6vdec.c
@@ -0,0 +1,1505 @@
+/* Copyright (c) 2008-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.
+ *
+ */
+
+/*
+#define DEBUG_TRACE_VDEC
+#define DEBUG
+*/
+
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <linux/delay.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/wakelock.h>
+
+#include <linux/android_pmem.h>
+#include <linux/msm_q6vdec.h>
+
+#include "dal.h"
+
+#define DALDEVICEID_VDEC_DEVICE 0x02000026
+#define DALDEVICEID_VDEC_PORTNAME "DAL_AQ_VID"
+
+#define VDEC_INTERFACE_VERSION 0x00020000
+
+#define MAJOR_MASK 0xFFFF0000
+#define MINOR_MASK 0x0000FFFF
+
+#define VDEC_GET_MAJOR_VERSION(version) (((version)&MAJOR_MASK)>>16)
+
+#define VDEC_GET_MINOR_VERSION(version) ((version)&MINOR_MASK)
+
+#ifdef DEBUG_TRACE_VDEC
+#define TRACE(fmt,x...) \
+ do { pr_debug("%s:%d " fmt, __func__, __LINE__, ##x); } while (0)
+#else
+#define TRACE(fmt,x...) do { } while (0)
+#endif
+
+#define YAMATO_COLOR_FORMAT 0x02
+#define MAX_Q6_LOAD ((720*1280)/256) /* 720p */
+#define MAX_Q6_LOAD_YAMATO ((736*1280)/256)
+#define MAX_Q6_LOAD_VP6 ((800*480)/256)
+
+#define VDEC_MAX_PORTS 4
+
+/*
+ *why magic number 300?
+
+ *the Maximum size of the DAL payload is 512 bytes according to DAL protocol
+ *Initialize call to QDSP6 from scorpion need to send sequence header as part of
+ *the DAL payload. DAL payload to initialize contains the following
+
+ *1) configuration data- 52 bytes 2) length field of config data - 4 bytes
+ *3) sequence header data ( that is from the bit stream)
+ *4) length field for sequence header - 4 bytes
+ *5) length field for output structure - 4 bytes
+
+ *that left with 512 - 68 = 448 bytes. It is unusual that we get a sequence
+ *header with such a big length unless the bit stream has multiple sequence
+ *headers.We estimated 300 is good enough which gives enough room for rest
+ *of the payload and even reserves some space for future payload.
+ */
+
+#define VDEC_MAX_SEQ_HEADER_SIZE 300
+
+char *Q6Portnames[] = {
+"DAL_AQ_VID_0",
+"DAL_AQ_VID_1",
+"DAL_AQ_VID_2",
+"DAL_AQ_VID_3"
+};
+
+
+
+#define DALDEVICEID_VDEC_DEVICE_0 0x020000D2
+#define DALDEVICEID_VDEC_DEVICE_1 0x020000D3
+#define DALDEVICEID_VDEC_DEVICE_2 0x020000D4
+#define DALDEVICEID_VDEC_DEVICE_3 0x020000D5
+#define DALDEVICEID_VDEC_DEVICE_4 0x020000D6
+#define DALDEVICEID_VDEC_DEVICE_5 0x020000D7
+#define DALDEVICEID_VDEC_DEVICE_6 0x020000D8
+#define DALDEVICEID_VDEC_DEVICE_7 0x020000D9
+#define DALDEVICEID_VDEC_DEVICE_8 0x020000DA
+#define DALDEVICEID_VDEC_DEVICE_9 0x020000DB
+#define DALDEVICEID_VDEC_DEVICE_10 0x020000DC
+#define DALDEVICEID_VDEC_DEVICE_11 0x020000DD
+#define DALDEVICEID_VDEC_DEVICE_12 0x020000DE
+#define DALDEVICEID_VDEC_DEVICE_13 0x020000DF
+#define DALDEVICEID_VDEC_DEVICE_14 0x020000E0
+#define DALDEVICEID_VDEC_DEVICE_15 0x020000E1
+#define DALDEVICEID_VDEC_DEVICE_16 0x020000E2
+#define DALDEVICEID_VDEC_DEVICE_17 0x020000E3
+#define DALDEVICEID_VDEC_DEVICE_18 0x020000E4
+#define DALDEVICEID_VDEC_DEVICE_19 0x020000E5
+#define DALDEVICEID_VDEC_DEVICE_20 0x020000E6
+#define DALDEVICEID_VDEC_DEVICE_21 0x020000E7
+#define DALDEVICEID_VDEC_DEVICE_22 0x020000E8
+#define DALDEVICEID_VDEC_DEVICE_23 0x020000E9
+#define DALDEVICEID_VDEC_DEVICE_24 0x020000EA
+#define DALDEVICEID_VDEC_DEVICE_25 0x020000EB
+#define DALDEVICEID_VDEC_DEVICE_26 0x020000EC
+#define DALDEVICEID_VDEC_DEVICE_27 0x020000ED
+#define DALDEVICEID_VDEC_DEVICE_28 0x020000EE
+#define DALDEVICEID_VDEC_DEVICE_29 0x020000EF
+#define DALDEVICEID_VDEC_DEVICE_30 0x020000F0
+#define DALDEVICEID_VDEC_DEVICE_31 0x020000F1
+
+#define DALVDEC_MAX_DEVICE_IDS 32
+
+
+static int numOfPorts;
+
+
+static char loadOnPorts[VDEC_MAX_PORTS];
+
+static char deviceIdRegistry[DALVDEC_MAX_DEVICE_IDS];
+
+
+#define VDEC_DEVID_FREE 0
+#define VDEC_DEVID_OCCUPIED 1
+
+#define MAX_SUPPORTED_INSTANCES 6
+
+#define MAKEFOURCC(ch0, ch1, ch2, ch3) ((unsigned int)(unsigned char)(ch0) | \
+ ((unsigned int)(unsigned char)(ch1) << 8) | \
+ ((unsigned int)(unsigned char)(ch2) << 16) | \
+ ((unsigned int)(unsigned char)(ch3) << 24))
+
+#define FOURCC_MPEG4 MAKEFOURCC('m', 'p', '4', 'v')
+#define FOURCC_H263 MAKEFOURCC('h', '2', '6', '3')
+#define FOURCC_H264 MAKEFOURCC('h', '2', '6', '4')
+#define FOURCC_VC1 MAKEFOURCC('w', 'm', 'v', '3')
+#define FOURCC_DIVX MAKEFOURCC('D', 'I', 'V', 'X')
+#define FOURCC_SPARK MAKEFOURCC('F', 'L', 'V', '1')
+#define FOURCC_VP6 MAKEFOURCC('V', 'P', '6', '0')
+
+/* static struct vdec_data *multiInstances[MAX_SUPPORTED_INSTANCES];*/
+
+static int totalPlaybackQ6load;
+static int totalTnailQ6load;
+
+#define FLAG_THUMBNAIL_MODE 0x8
+#define MAX_TNAILS 3
+
+#define TRUE 1
+#define FALSE 0
+
+enum {
+ VDEC_DALRPC_INITIALIZE = DAL_OP_FIRST_DEVICE_API,
+ VDEC_DALRPC_SETBUFFERS,
+ VDEC_DALRPC_FREEBUFFERS,
+ VDEC_DALRPC_QUEUE,
+ VDEC_DALRPC_SIGEOFSTREAM,
+ VDEC_DALRPC_FLUSH,
+ VDEC_DALRPC_REUSEFRAMEBUFFER,
+ VDEC_DALRPC_GETDECATTRIBUTES,
+ VDEC_DALRPC_SUSPEND,
+ VDEC_DALRPC_RESUME,
+ VDEC_DALRPC_INITIALIZE_00,
+ VDEC_DALRPC_GETINTERNALBUFFERREQ,
+ VDEC_DALRPC_SETBUFFERS_00,
+ VDEC_DALRPC_FREEBUFFERS_00,
+ VDEC_DALRPC_GETPROPERTY,
+ VDEC_DALRPC_SETPROPERTY,
+ VDEC_DALRPC_GETDECATTRIBUTES_00,
+ VDEC_DALRPC_PERFORMANCE_CHANGE_REQUEST
+};
+
+enum {
+ VDEC_ASYNCMSG_DECODE_DONE = 0xdec0de00,
+ VDEC_ASYNCMSG_REUSE_FRAME,
+};
+
+struct vdec_init_cfg {
+ u32 decode_done_evt;
+ u32 reuse_frame_evt;
+ struct vdec_config cfg;
+};
+
+struct vdec_buffer_status {
+ u32 data;
+ u32 status;
+};
+
+#define VDEC_MSG_MAX 128
+
+struct vdec_msg_list {
+ struct list_head list;
+ struct vdec_msg vdec_msg;
+};
+
+struct vdec_mem_info {
+ u32 buf_type;
+ u32 id;
+ unsigned long phys_addr;
+ unsigned long len;
+ struct file *file;
+};
+
+struct vdec_mem_list {
+ struct list_head list;
+ struct vdec_mem_info mem;
+};
+
+struct videoStreamDetails{
+ int height;
+ int width;
+ unsigned int fourcc;
+ int Q6usage;
+ bool isThisTnail;
+ bool isTnailGranted;
+};
+
+struct vdec_data {
+ struct dal_client *vdec_handle;
+ unsigned int Q6deviceId;
+ struct videoStreamDetails streamDetails;
+ struct list_head vdec_msg_list_head;
+ struct list_head vdec_msg_list_free;
+ wait_queue_head_t vdec_msg_evt;
+ spinlock_t vdec_list_lock;
+ struct list_head vdec_mem_list_head;
+ spinlock_t vdec_mem_list_lock;
+ int mem_initialized;
+ int running;
+ int close_decode;
+};
+
+static struct class *driver_class;
+static dev_t vdec_device_no;
+static struct cdev vdec_cdev;
+static int ref_cnt;
+static DEFINE_MUTEX(vdec_ref_lock);
+
+static DEFINE_MUTEX(idlecount_lock);
+
+static DEFINE_MUTEX(vdec_rm_lock);
+
+static int idlecount;
+static struct wake_lock wakelock;
+static struct wake_lock idlelock;
+
+static void prevent_sleep(void)
+{
+ mutex_lock(&idlecount_lock);
+ if (++idlecount == 1) {
+ wake_lock(&idlelock);
+ wake_lock(&wakelock);
+ }
+ mutex_unlock(&idlecount_lock);
+}
+
+static void allow_sleep(void)
+{
+ mutex_lock(&idlecount_lock);
+ if (--idlecount == 0) {
+ wake_unlock(&idlelock);
+ wake_unlock(&wakelock);
+ }
+ mutex_unlock(&idlecount_lock);
+}
+
+static inline int vdec_check_version(u32 client, u32 server)
+{
+ int ret = -EINVAL;
+ if ((VDEC_GET_MAJOR_VERSION(client) == VDEC_GET_MAJOR_VERSION(server))
+ && (VDEC_GET_MINOR_VERSION(client) <=
+ VDEC_GET_MINOR_VERSION(server)))
+ ret = 0;
+ return ret;
+}
+
+static int vdec_get_msg(struct vdec_data *vd, void *msg)
+{
+ struct vdec_msg_list *l;
+ unsigned long flags;
+ int ret = 0;
+
+ if (!vd->running)
+ return -EPERM;
+
+ spin_lock_irqsave(&vd->vdec_list_lock, flags);
+ list_for_each_entry_reverse(l, &vd->vdec_msg_list_head, list) {
+ if (copy_to_user(msg, &l->vdec_msg, sizeof(struct vdec_msg)))
+ pr_err("vdec_get_msg failed to copy_to_user!\n");
+ if (l->vdec_msg.id == VDEC_MSG_REUSEINPUTBUFFER)
+ TRACE("reuse_input_buffer %d\n", l->vdec_msg.buf_id);
+ else if (l->vdec_msg.id == VDEC_MSG_FRAMEDONE)
+ TRACE("frame_done (stat=%d)\n",
+ l->vdec_msg.vfr_info.status);
+ else
+ TRACE("unknown msg (msgid=%d)\n", l->vdec_msg.id);
+ list_del(&l->list);
+ list_add(&l->list, &vd->vdec_msg_list_free);
+ ret = 1;
+ break;
+ }
+ spin_unlock_irqrestore(&vd->vdec_list_lock, flags);
+
+ if (vd->close_decode)
+ ret = 1;
+
+ return ret;
+}
+
+static void vdec_put_msg(struct vdec_data *vd, struct vdec_msg *msg)
+{
+ struct vdec_msg_list *l;
+ unsigned long flags;
+ int found = 0;
+
+ spin_lock_irqsave(&vd->vdec_list_lock, flags);
+ list_for_each_entry(l, &vd->vdec_msg_list_free, list) {
+ memcpy(&l->vdec_msg, msg, sizeof(struct vdec_msg));
+ list_del(&l->list);
+ list_add(&l->list, &vd->vdec_msg_list_head);
+ found = 1;
+ break;
+ }
+ spin_unlock_irqrestore(&vd->vdec_list_lock, flags);
+
+ if (found)
+ wake_up(&vd->vdec_msg_evt);
+ else
+ pr_err("vdec_put_msg can't find free list!\n");
+}
+
+static struct vdec_mem_list *vdec_get_mem_from_list(struct vdec_data *vd,
+ u32 pmem_id, u32 buf_type)
+{
+ struct vdec_mem_list *l;
+ unsigned long flags;
+ int found = 0;
+
+ spin_lock_irqsave(&vd->vdec_mem_list_lock, flags);
+ list_for_each_entry(l, &vd->vdec_mem_list_head, list) {
+ if (l->mem.buf_type == buf_type && l->mem.id == pmem_id) {
+ found = 1;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&vd->vdec_mem_list_lock, flags);
+
+ if (found)
+ return l;
+ else
+ return NULL;
+
+}
+static int vdec_setproperty(struct vdec_data *vd, void *argp)
+{
+ struct vdec_property_info property;
+ int res;
+
+ if (copy_from_user(&property, argp, sizeof(struct vdec_property_info)))
+ return -1;
+
+ res = dal_call_f6(vd->vdec_handle, VDEC_DALRPC_SETPROPERTY,
+ property.id, &(property.property), sizeof(union vdec_property));
+ if (res)
+ TRACE("Set Property failed");
+ else
+ TRACE("Set Property succeeded");
+ return res;
+}
+static int vdec_getproperty(struct vdec_data *vd, void *argp)
+{
+ int res;
+ union vdec_property property = {0};
+
+ res = dal_call_f11(vd->vdec_handle, VDEC_DALRPC_GETPROPERTY,
+ ((struct vdec_property_info *)argp)->id, &property,
+ sizeof(union vdec_property));
+
+ if (res)
+ TRACE("get Property failed");
+ else
+ TRACE("get Property succeeded");
+
+ res = copy_to_user(
+ (&((struct vdec_property_info *)argp)->property),
+ &property, sizeof(property));
+
+ return res;
+}
+static int vdec_performance_change_request(struct vdec_data *vd, void* argp)
+{
+ u32 request_type;
+ int ret;
+
+ ret = copy_from_user(&request_type, argp, sizeof(request_type));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+ ret = dal_call_f0(vd->vdec_handle,
+ VDEC_DALRPC_PERFORMANCE_CHANGE_REQUEST,
+ request_type);
+ if (ret) {
+ pr_err("%s: remote function failed (%d)\n", __func__, ret);
+ return ret;
+ }
+ return ret;
+}
+
+#ifdef TRACE_PORTS
+static void printportsanddeviceids(void)
+{
+ int i;
+
+ pr_err("\n\n%s:loadOnPorts", __func__);
+ for (i = 0; i < numOfPorts; i++)
+ pr_err("\t%d", loadOnPorts[i]);
+
+ pr_err("\n\n");
+
+ pr_err("\n\n%s:Devids", __func__);
+ for (i = 0; i < DALVDEC_MAX_DEVICE_IDS; i++)
+ pr_err("Devid[%d]:%d\n", i, deviceIdRegistry[i]);
+
+
+ pr_err("\n\n");
+}
+#endif /*TRACE_PORTS*/
+
+
+/*
+ *
+ * This method is used to get the number of ports supported on the Q6
+ *
+ */
+static int vdec_get_numberofq6ports(void)
+{
+ struct dal_client *vdec_handle = NULL;
+ int retval = 0;
+ union vdec_property property = {0};
+
+ vdec_handle = dal_attach(DALDEVICEID_VDEC_DEVICE,
+ DALDEVICEID_VDEC_PORTNAME, 1, NULL, NULL);
+ if (!vdec_handle) {
+ pr_err("%s: failed to attach\n", __func__);
+ return 1;/* default setting */
+ }
+
+ retval = dal_call_f6(vdec_handle, VDEC_DALRPC_GETPROPERTY,
+ VDEC_NUM_DAL_PORTS, (void *)&property, sizeof(union vdec_property));
+ if (retval) {
+ pr_err("%s: Q6get prperty failed\n", __func__);
+ return 1;/* default setting */
+ }
+
+ dal_detach(vdec_handle);
+ return property.num_dal_ports ;
+}
+
+
+/**
+ * This method is used to get the find the least loaded port and a corresponding
+ * free device id in that port.
+ *
+ * Prerequisite: vdec_open should have been called.
+ *
+ * @param[in] deviceid
+ * device id will be populated here.
+ *
+ * @param[in] portname
+ * portname will be populated here.
+ */
+static void vdec_get_next_portanddevid(int *deviceid, char **portname)
+{
+
+ int i = 0;
+ int leastLoad = 0;
+ int leastLoadedIndex = 0;
+
+ if (0 == numOfPorts) {
+ numOfPorts = vdec_get_numberofq6ports();
+ pr_err("%s: Q6get numOfPorts %d\n", __func__, numOfPorts);
+ numOfPorts = 4;
+ /*fix: me currently hard coded to 4 as
+ *the Q6 getproperty is failing
+ */
+ }
+
+ if ((NULL == deviceid) || (NULL == portname))
+ return;
+ else
+ *deviceid = 0; /* init value */
+
+ if (numOfPorts > 1) {
+ /* multi ports mode*/
+
+ /* find the least loaded port*/
+ for (i = 1, leastLoad = loadOnPorts[0], leastLoadedIndex = 0;
+ i < numOfPorts; i++) {
+ if (leastLoad > loadOnPorts[i]) {
+ leastLoadedIndex = i;
+ leastLoad = loadOnPorts[i];
+ }
+ }
+
+ /* register the load */
+ loadOnPorts[leastLoadedIndex]++;
+ *portname = Q6Portnames[leastLoadedIndex];
+
+ /* find a free device id corresponding to the port*/
+ for (i = leastLoadedIndex; i < DALVDEC_MAX_DEVICE_IDS;
+ i += numOfPorts) {
+ if (VDEC_DEVID_FREE == deviceIdRegistry[i]) {
+ deviceIdRegistry[i] = VDEC_DEVID_OCCUPIED;
+ *deviceid = DALDEVICEID_VDEC_DEVICE_0 + i;
+ break;
+ }
+ }
+
+#ifdef TRACE_PORTS
+ printportsanddeviceids();
+#endif /*TRACE_PORTS*/
+ } else if (1 == numOfPorts) {
+ /* single port mode */
+ *deviceid = DALDEVICEID_VDEC_DEVICE;
+ *portname = DALDEVICEID_VDEC_PORTNAME;
+ } else if (numOfPorts <= 0) {
+ pr_err("%s: FATAL error numOfPorts cannot be \
+ less than or equal to zero\n", __func__);
+ }
+
+
+}
+
+
+/**
+ * This method frees up the used dev id and decrements the port load.
+ *
+ */
+
+static void vdec_freeup_portanddevid(int deviceid)
+{
+
+ if (numOfPorts > 1) {
+ /* multi ports mode*/
+ if (VDEC_DEVID_FREE ==
+ deviceIdRegistry[deviceid - DALDEVICEID_VDEC_DEVICE_0])
+ pr_err("device id cannot be already free\n");
+ deviceIdRegistry[deviceid - DALDEVICEID_VDEC_DEVICE_0] =
+ VDEC_DEVID_FREE;
+
+ loadOnPorts[(deviceid - DALDEVICEID_VDEC_DEVICE_0)
+ % numOfPorts]--;
+
+ if (loadOnPorts[(deviceid - DALDEVICEID_VDEC_DEVICE_0)
+ % numOfPorts] < 0)
+ pr_err("Warning:load cannot be negative\n");
+
+ pr_err("dettaching on deviceid %x portname %s\n", deviceid,
+ Q6Portnames[(deviceid - DALDEVICEID_VDEC_DEVICE_0)
+ % numOfPorts]);
+
+#ifdef TRACE_PORTS
+ printportsanddeviceids();
+#endif /*TRACE_PORTS*/
+ } else {
+ /*single port mode, nothing to be done here*/
+ }
+
+}
+
+
+/**
+ * This method validates whether a new instance can be houred or not.
+ *
+ */
+static int vdec_rm_checkWithRm(struct vdec_data *vdecInstance,
+ unsigned int color_format)
+{
+
+ unsigned int maxQ6load = 0;/* in the units of macro blocks per second */
+ unsigned int currentq6load = 0;
+ struct videoStreamDetails *streamDetails = &vdecInstance->streamDetails;
+
+
+
+ if (streamDetails->isThisTnail) {
+ if (totalTnailQ6load < MAX_TNAILS) {
+
+ totalTnailQ6load++;
+ streamDetails->isTnailGranted = TRUE;
+ pr_info("%s: thumbnail granted %d\n", __func__,
+ totalTnailQ6load);
+ return 0;
+
+ } else {
+
+ pr_err("%s: thumbnails load max this instance cannot \
+ be supported\n", __func__);
+ streamDetails->isTnailGranted = FALSE;
+ return -ENOSPC;
+
+ }
+ }
+
+ /* calculate the Q6 percentage instance would need */
+ if ((streamDetails->fourcc == FOURCC_MPEG4) ||
+ (streamDetails->fourcc == FOURCC_H264) ||
+ (streamDetails->fourcc == FOURCC_DIVX) ||
+ (streamDetails->fourcc == FOURCC_VC1) ||
+ (streamDetails->fourcc == FOURCC_SPARK) ||
+ (streamDetails->fourcc == FOURCC_H263)
+ ){
+
+ /* is yamato color format,
+ Rounds the H & W --> mutiple of 32 */
+ if (color_format == YAMATO_COLOR_FORMAT)
+ maxQ6load = MAX_Q6_LOAD_YAMATO;
+ else
+ maxQ6load = MAX_Q6_LOAD; /* 720p */
+
+ } else if (streamDetails->fourcc == FOURCC_VP6) {
+
+ maxQ6load = MAX_Q6_LOAD_VP6; /* FWVGA */
+
+ } else {
+
+ pr_err("%s: unknown fourcc %d maxQ6load %u\n", __func__,
+ streamDetails->fourcc, maxQ6load);
+ return -EINVAL;
+
+ }
+
+ currentq6load = ((streamDetails->height)*(streamDetails->width) / 256);
+ currentq6load = ((currentq6load * 100)/maxQ6load);
+ if ((currentq6load+totalPlaybackQ6load) > 100) {
+ /* reject this instance */
+ pr_err("%s: too much Q6load [cur+tot] = [%d + %d] = %d",
+ __func__, currentq6load, totalPlaybackQ6load,
+ (currentq6load+totalPlaybackQ6load));
+ pr_err("rejecting the instance,[WxH] = [%d x %d],color_fmt=0x%x\n",
+ streamDetails->width, streamDetails->height, color_format);
+ pr_err("VDEC_fmt=%s\n", (char *)(&streamDetails->fourcc));
+ streamDetails->Q6usage = 0;
+ return -ENOSPC;
+ }
+
+ totalPlaybackQ6load += currentq6load;
+ streamDetails->Q6usage = currentq6load;
+
+ pr_info("%s: adding a load [%d%%] bringing total Q6load to [%d%%]\n",
+ __func__, currentq6load, totalPlaybackQ6load);
+
+ return 0;
+}
+
+
+static int vdec_initialize(struct vdec_data *vd, void *argp)
+{
+ struct vdec_config_sps vdec_cfg_sps;
+ struct vdec_init_cfg vi_cfg;
+ struct vdec_buf_req vdec_buf_req;
+ struct u8 *header;
+ int ret = 0;
+
+ ret = copy_from_user(&vdec_cfg_sps,
+ &((struct vdec_init *)argp)->sps_cfg,
+ sizeof(vdec_cfg_sps));
+
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+
+ vi_cfg.decode_done_evt = VDEC_ASYNCMSG_DECODE_DONE;
+ vi_cfg.reuse_frame_evt = VDEC_ASYNCMSG_REUSE_FRAME;
+ memcpy(&vi_cfg.cfg, &vdec_cfg_sps.cfg, sizeof(struct vdec_config));
+
+ /*
+ * restricting the max value of the seq header
+ */
+ if (vdec_cfg_sps.seq.len > VDEC_MAX_SEQ_HEADER_SIZE)
+ vdec_cfg_sps.seq.len = VDEC_MAX_SEQ_HEADER_SIZE;
+
+ header = kmalloc(vdec_cfg_sps.seq.len, GFP_KERNEL);
+ if (!header) {
+ pr_err("%s: kmalloc failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ ret = copy_from_user(header,
+ ((struct vdec_init *)argp)->sps_cfg.seq.header,
+ vdec_cfg_sps.seq.len);
+
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ kfree(header);
+ return ret;
+ }
+
+ TRACE("vi_cfg: handle=%p fourcc=0x%x w=%d h=%d order=%d notify_en=%d "
+ "vc1_rb=%d h264_sd=%d h264_nls=%d pp_flag=%d fruc_en=%d\n",
+ vd->vdec_handle, vi_cfg.cfg.fourcc, vi_cfg.cfg.width,
+ vi_cfg.cfg.height, vi_cfg.cfg.order, vi_cfg.cfg.notify_enable,
+ vi_cfg.cfg.vc1_rowbase, vi_cfg.cfg.h264_startcode_detect,
+ vi_cfg.cfg.h264_nal_len_size, vi_cfg.cfg.postproc_flag,
+ vi_cfg.cfg.fruc_enable);
+
+ vd->streamDetails.height = vi_cfg.cfg.height;
+ vd->streamDetails.width = vi_cfg.cfg.width;
+ vd->streamDetails.fourcc = vi_cfg.cfg.fourcc;
+ if (FLAG_THUMBNAIL_MODE == vi_cfg.cfg.postproc_flag)
+ vd->streamDetails.isThisTnail = TRUE;
+ else
+ vd->streamDetails.isThisTnail = FALSE;
+
+ mutex_lock(&vdec_rm_lock);
+ ret = vdec_rm_checkWithRm(vd, vi_cfg.cfg.color_format);
+ mutex_unlock(&vdec_rm_lock);
+ if (ret)
+ return ret;
+
+ ret = dal_call_f13(vd->vdec_handle, VDEC_DALRPC_INITIALIZE,
+ &vi_cfg, sizeof(vi_cfg),
+ header, vdec_cfg_sps.seq.len,
+ &vdec_buf_req, sizeof(vdec_buf_req));
+
+ kfree(header);
+
+ if (ret)
+ pr_err("%s: remote function failed (%d)\n", __func__, ret);
+ else
+ ret = copy_to_user(((struct vdec_init *)argp)->buf_req,
+ &vdec_buf_req, sizeof(vdec_buf_req));
+
+ vd->close_decode = 0;
+ return ret;
+}
+
+static void vdec_rm_freeupResources(struct vdec_data *vdecInstance)
+{
+ struct videoStreamDetails *streamDetails = &vdecInstance->streamDetails;
+
+
+
+ if ((streamDetails->isThisTnail) &&
+ (streamDetails->isTnailGranted)) {
+
+ totalTnailQ6load--;
+ pr_info("%s: Thumbnail released %d\n", __func__,
+ totalTnailQ6load);
+
+ } else if (streamDetails->Q6usage > 0) {
+
+ totalPlaybackQ6load -= streamDetails->Q6usage;
+ if (totalPlaybackQ6load < 0)
+ pr_err("Warning:Q6load cannot be negative\n");
+
+ pr_info("%s:Releasing [%d%%] of Q6load from a total of [%d%%]\n"
+ , __func__, streamDetails->Q6usage,
+ (streamDetails->Q6usage+totalPlaybackQ6load));
+ }
+
+}
+
+static int vdec_setbuffers(struct vdec_data *vd, void *argp)
+{
+ struct vdec_buffer vmem;
+ struct vdec_mem_list *l;
+ unsigned long vstart;
+ unsigned long flags;
+ struct {
+ uint32_t size;
+ struct vdec_buf_info buf;
+ } rpc;
+ uint32_t res;
+
+ int ret = 0;
+
+ vd->mem_initialized = 0;
+
+ ret = copy_from_user(&vmem, argp, sizeof(vmem));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+
+ l = kzalloc(sizeof(struct vdec_mem_list), GFP_KERNEL);
+ if (!l) {
+ pr_err("%s: kzalloc failed!\n", __func__);
+ return -ENOMEM;
+ }
+
+ l->mem.id = vmem.pmem_id;
+ l->mem.buf_type = vmem.buf.buf_type;
+
+ ret = get_pmem_file(l->mem.id, &l->mem.phys_addr, &vstart,
+ &l->mem.len, &l->mem.file);
+ if (ret) {
+ pr_err("%s: get_pmem_fd failed\n", __func__);
+ goto err_get_pmem_file;
+ }
+
+ TRACE("pmem_id=%d (phys=0x%08lx len=0x%lx) buftype=%d num_buf=%d "
+ "islast=%d src_id=%d offset=0x%08x size=0x%x\n",
+ vmem.pmem_id, l->mem.phys_addr, l->mem.len,
+ vmem.buf.buf_type, vmem.buf.num_buf, vmem.buf.islast,
+ vmem.buf.region.src_id, vmem.buf.region.offset,
+ vmem.buf.region.size);
+
+ /* input buffers */
+ if ((vmem.buf.region.offset + vmem.buf.region.size) > l->mem.len) {
+ pr_err("%s: invalid input buffer offset!\n", __func__);
+ ret = -EINVAL;
+ goto err_bad_offset;
+
+ }
+ vmem.buf.region.offset += l->mem.phys_addr;
+
+ rpc.size = sizeof(vmem.buf);
+ memcpy(&rpc.buf, &vmem.buf, sizeof(struct vdec_buf_info));
+
+
+ ret = dal_call(vd->vdec_handle, VDEC_DALRPC_SETBUFFERS, 5,
+ &rpc, sizeof(rpc), &res, sizeof(res));
+
+ if (ret < 4) {
+ pr_err("%s: remote function failed (%d)\n", __func__, ret);
+ ret = -EIO;
+ goto err_dal_call;
+ }
+
+ spin_lock_irqsave(&vd->vdec_mem_list_lock, flags);
+ list_add(&l->list, &vd->vdec_mem_list_head);
+ spin_unlock_irqrestore(&vd->vdec_mem_list_lock, flags);
+
+ vd->mem_initialized = 1;
+ return ret;
+
+err_dal_call:
+err_bad_offset:
+ put_pmem_file(l->mem.file);
+err_get_pmem_file:
+ kfree(l);
+ return ret;
+}
+
+static int vdec_queue(struct vdec_data *vd, void *argp)
+{
+ struct {
+ uint32_t size;
+ struct vdec_input_buf_info buf_info;
+ uint32_t osize;
+ } rpc;
+ struct vdec_mem_list *l;
+ struct {
+ uint32_t result;
+ uint32_t size;
+ struct vdec_queue_status status;
+ } rpc_res;
+
+ u32 pmem_id;
+ int ret = 0;
+
+ if (!vd->mem_initialized) {
+ pr_err("%s: memory is not being initialized!\n", __func__);
+ return -EPERM;
+ }
+
+ ret = copy_from_user(&rpc.buf_info,
+ &((struct vdec_input_buf *)argp)->buffer,
+ sizeof(rpc.buf_info));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+
+ ret = copy_from_user(&pmem_id,
+ &((struct vdec_input_buf *)argp)->pmem_id,
+ sizeof(u32));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+
+ l = vdec_get_mem_from_list(vd, pmem_id, VDEC_BUFFER_TYPE_INPUT);
+
+ if (NULL == l) {
+ pr_err("%s: not able to find the buffer from list\n", __func__);
+ return -EPERM;
+ }
+
+ if ((rpc.buf_info.size + rpc.buf_info.offset) >= l->mem.len) {
+ pr_err("%s: invalid queue buffer offset!\n", __func__);
+ return -EINVAL;
+ }
+
+ rpc.buf_info.offset += l->mem.phys_addr;
+ rpc.size = sizeof(struct vdec_input_buf_info);
+ rpc.osize = sizeof(struct vdec_queue_status);
+
+ /* complete the writes to the buffer */
+ wmb();
+ ret = dal_call(vd->vdec_handle, VDEC_DALRPC_QUEUE, 8,
+ &rpc, sizeof(rpc), &rpc_res, sizeof(rpc_res));
+ if (ret < 4) {
+ pr_err("%s: remote function failed (%d)\n", __func__, ret);
+ ret = -EIO;
+ }
+ return ret;
+}
+
+static int vdec_reuse_framebuffer(struct vdec_data *vd, void *argp)
+{
+ u32 buf_id;
+ int ret = 0;
+
+ ret = copy_from_user(&buf_id, argp, sizeof(buf_id));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+
+ ret = dal_call_f0(vd->vdec_handle, VDEC_DALRPC_REUSEFRAMEBUFFER,
+ buf_id);
+ if (ret)
+ pr_err("%s: remote function failed (%d)\n", __func__, ret);
+
+ return ret;
+}
+
+static int vdec_flush(struct vdec_data *vd, void *argp)
+{
+ u32 flush_type;
+ int ret = 0;
+
+ if (!vd->mem_initialized) {
+ pr_err("%s: memory is not being initialized!\n", __func__);
+ return -EPERM;
+ }
+
+ ret = copy_from_user(&flush_type, argp, sizeof(flush_type));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+
+ TRACE("flush_type=%d\n", flush_type);
+ ret = dal_call_f0(vd->vdec_handle, VDEC_DALRPC_FLUSH, flush_type);
+ if (ret) {
+ pr_err("%s: remote function failed (%d)\n", __func__, ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int vdec_close(struct vdec_data *vd, void *argp)
+{
+ struct vdec_mem_list *l;
+ int ret = 0;
+
+ pr_info("q6vdec_close()\n");
+ vd->close_decode = 1;
+ wake_up(&vd->vdec_msg_evt);
+
+ ret = dal_call_f0(vd->vdec_handle, DAL_OP_CLOSE, 0);
+ if (ret)
+ pr_err("%s: failed to close daldevice (%d)\n", __func__, ret);
+
+ if (vd->mem_initialized) {
+ list_for_each_entry(l, &vd->vdec_mem_list_head, list)
+ put_pmem_file(l->mem.file);
+ }
+
+ return ret;
+}
+static int vdec_getdecattributes(struct vdec_data *vd, void *argp)
+{
+ struct {
+ uint32_t status;
+ uint32_t size;
+ struct vdec_dec_attributes dec_attr;
+ } rpc;
+ uint32_t inp;
+ int ret = 0;
+ inp = sizeof(struct vdec_dec_attributes);
+
+ ret = dal_call(vd->vdec_handle, VDEC_DALRPC_GETDECATTRIBUTES, 9,
+ &inp, sizeof(inp), &rpc, sizeof(rpc));
+ if (ret < 4 || rpc.size != sizeof(struct vdec_dec_attributes)) {
+ pr_err("%s: remote function failed (%d)\n", __func__, ret);
+ ret = -EIO;
+ } else
+ ret =
+ copy_to_user(((struct vdec_dec_attributes *)argp),
+ &rpc.dec_attr, sizeof(rpc.dec_attr));
+ return ret;
+}
+
+static int vdec_freebuffers(struct vdec_data *vd, void *argp)
+{
+ struct vdec_buffer vmem;
+ struct vdec_mem_list *l;
+ struct {
+ uint32_t size;
+ struct vdec_buf_info buf;
+ } rpc;
+ uint32_t res;
+
+ int ret = 0;
+
+ if (!vd->mem_initialized) {
+ pr_err("%s: memory is not being initialized!\n", __func__);
+ return -EPERM;
+ }
+
+ ret = copy_from_user(&vmem, argp, sizeof(vmem));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+
+ l = vdec_get_mem_from_list(vd, vmem.pmem_id, vmem.buf.buf_type);
+
+ if (NULL == l) {
+ pr_err("%s: not able to find the buffer from list\n", __func__);
+ return -EPERM;
+ }
+
+ /* input buffers */
+ if ((vmem.buf.region.offset + vmem.buf.region.size) > l->mem.len) {
+ pr_err("%s: invalid input buffer offset!\n", __func__);
+ return -EINVAL;
+
+ }
+ vmem.buf.region.offset += l->mem.phys_addr;
+
+ rpc.size = sizeof(vmem.buf);
+ memcpy(&rpc.buf, &vmem.buf, sizeof(struct vdec_buf_info));
+
+ ret = dal_call(vd->vdec_handle, VDEC_DALRPC_FREEBUFFERS, 5,
+ &rpc, sizeof(rpc), &res, sizeof(res));
+ if (ret < 4) {
+ pr_err("%s: remote function failed (%d)\n", __func__, ret);
+ }
+
+ return ret;
+}
+
+static int vdec_getversion(struct vdec_data *vd, void *argp)
+{
+ struct vdec_version ver_info;
+ int ret = 0;
+
+ ver_info.major = VDEC_GET_MAJOR_VERSION(VDEC_INTERFACE_VERSION);
+ ver_info.minor = VDEC_GET_MINOR_VERSION(VDEC_INTERFACE_VERSION);
+
+ ret = copy_to_user(((struct vdec_version *)argp),
+ &ver_info, sizeof(ver_info));
+
+ return ret;
+
+}
+
+static long vdec_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct vdec_data *vd = file->private_data;
+ void __user *argp = (void __user *)arg;
+ int ret = 0;
+
+ if (!vd->running)
+ return -EPERM;
+
+ switch (cmd) {
+ case VDEC_IOCTL_INITIALIZE:
+ ret = vdec_initialize(vd, argp);
+ break;
+
+ case VDEC_IOCTL_SETBUFFERS:
+ ret = vdec_setbuffers(vd, argp);
+ break;
+
+ case VDEC_IOCTL_QUEUE:
+ TRACE("VDEC_IOCTL_QUEUE (pid=%d tid=%d)\n",
+ current->group_leader->pid, current->pid);
+ ret = vdec_queue(vd, argp);
+ break;
+
+ case VDEC_IOCTL_REUSEFRAMEBUFFER:
+ TRACE("VDEC_IOCTL_REUSEFRAMEBUFFER (pid=%d tid=%d)\n",
+ current->group_leader->pid, current->pid);
+ ret = vdec_reuse_framebuffer(vd, argp);
+ break;
+
+ case VDEC_IOCTL_FLUSH:
+ TRACE("IOCTL flush\n");
+ ret = vdec_flush(vd, argp);
+ break;
+
+ case VDEC_IOCTL_EOS:
+ TRACE("VDEC_IOCTL_EOS (pid=%d tid=%d)\n",
+ current->group_leader->pid, current->pid);
+ ret = dal_call_f0(vd->vdec_handle, VDEC_DALRPC_SIGEOFSTREAM, 0);
+ if (ret)
+ pr_err("%s: remote function failed (%d)\n",
+ __func__, ret);
+ break;
+
+ case VDEC_IOCTL_GETMSG:
+ TRACE("VDEC_IOCTL_GETMSG (pid=%d tid=%d)\n",
+ current->group_leader->pid, current->pid);
+ wait_event_interruptible(vd->vdec_msg_evt,
+ vdec_get_msg(vd, argp));
+
+ if (vd->close_decode)
+ ret = -EINTR;
+ else
+ /* order the reads from the buffer */
+ rmb();
+ break;
+
+ case VDEC_IOCTL_CLOSE:
+ ret = vdec_close(vd, argp);
+ break;
+
+ case VDEC_IOCTL_GETDECATTRIBUTES:
+ TRACE("VDEC_IOCTL_GETDECATTRIBUTES (pid=%d tid=%d)\n",
+ current->group_leader->pid, current->pid);
+ ret = vdec_getdecattributes(vd, argp);
+
+ if (ret)
+ pr_err("%s: remote function failed (%d)\n",
+ __func__, ret);
+ break;
+
+ case VDEC_IOCTL_FREEBUFFERS:
+ TRACE("VDEC_IOCTL_FREEBUFFERS (pid=%d tid=%d)\n",
+ current->group_leader->pid, current->pid);
+ ret = vdec_freebuffers(vd, argp);
+
+ if (ret)
+ pr_err("%s: remote function failed (%d)\n",
+ __func__, ret);
+ break;
+ case VDEC_IOCTL_GETVERSION:
+ TRACE("VDEC_IOCTL_GETVERSION (pid=%d tid=%d)\n",
+ current->group_leader->pid, current->pid);
+ ret = vdec_getversion(vd, argp);
+
+ if (ret)
+ pr_err("%s: remote function failed (%d)\n",
+ __func__, ret);
+ break;
+ case VDEC_IOCTL_GETPROPERTY:
+ TRACE("VDEC_IOCTL_GETPROPERTY (pid=%d tid=%d)\n",
+ current->group_leader->pid, current->pid);
+ ret = vdec_getproperty(vd, argp);
+ break;
+ case VDEC_IOCTL_SETPROPERTY:
+ TRACE("VDEC_IOCTL_SETPROPERTY (pid=%d tid=%d)\n",
+ current->group_leader->pid, current->pid);
+ ret = vdec_setproperty(vd, argp);
+ break;
+ case VDEC_IOCTL_PERFORMANCE_CHANGE_REQ:
+ ret = vdec_performance_change_request(vd, argp);
+ break;
+ default:
+ pr_err("%s: invalid ioctl!\n", __func__);
+ ret = -EINVAL;
+ break;
+ }
+
+ TRACE("ioctl done (pid=%d tid=%d)\n",
+ current->group_leader->pid, current->pid);
+
+ return ret;
+}
+
+static void vdec_dcdone_handler(struct vdec_data *vd, void *frame,
+ uint32_t frame_size)
+{
+ struct vdec_msg msg;
+ struct vdec_mem_list *l;
+ unsigned long flags;
+ int found = 0;
+
+ if (frame_size < sizeof(struct vdec_frame_info)) {
+ pr_warning("%s: msg size mismatch %d != %d\n", __func__,
+ frame_size, sizeof(struct vdec_frame_info));
+ return;
+ }
+
+ memcpy(&msg.vfr_info, (struct vdec_frame_info *)frame,
+ sizeof(struct vdec_frame_info));
+
+ if (msg.vfr_info.status == VDEC_FRAME_DECODE_OK) {
+ spin_lock_irqsave(&vd->vdec_mem_list_lock, flags);
+ list_for_each_entry(l, &vd->vdec_mem_list_head, list) {
+ if ((l->mem.buf_type == VDEC_BUFFER_TYPE_OUTPUT) &&
+ (msg.vfr_info.offset >= l->mem.phys_addr) &&
+ (msg.vfr_info.offset <
+ (l->mem.phys_addr + l->mem.len))) {
+ found = 1;
+ msg.vfr_info.offset -= l->mem.phys_addr;
+ msg.vfr_info.data2 = l->mem.id;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&vd->vdec_mem_list_lock, flags);
+ }
+
+ if (found || (msg.vfr_info.status != VDEC_FRAME_DECODE_OK)) {
+ msg.id = VDEC_MSG_FRAMEDONE;
+ vdec_put_msg(vd, &msg);
+ } else {
+ pr_err("%s: invalid phys addr = 0x%x\n",
+ __func__, msg.vfr_info.offset);
+ }
+
+}
+
+static void vdec_reuseibuf_handler(struct vdec_data *vd, void *bufstat,
+ uint32_t bufstat_size)
+{
+ struct vdec_buffer_status *vdec_bufstat;
+ struct vdec_msg msg;
+
+ /* TODO: how do we signal the client? If they are waiting on a
+ * message in an ioctl, they may block forever */
+ if (bufstat_size != sizeof(struct vdec_buffer_status)) {
+ pr_warning("%s: msg size mismatch %d != %d\n", __func__,
+ bufstat_size, sizeof(struct vdec_buffer_status));
+ return;
+ }
+ vdec_bufstat = (struct vdec_buffer_status *)bufstat;
+ msg.id = VDEC_MSG_REUSEINPUTBUFFER;
+ msg.buf_id = vdec_bufstat->data;
+ vdec_put_msg(vd, &msg);
+}
+
+static void callback(void *data, int len, void *cookie)
+{
+ struct vdec_data *vd = (struct vdec_data *)cookie;
+ uint32_t *tmp = (uint32_t *) data;
+
+ if (!vd->mem_initialized) {
+ pr_err("%s:memory not initialize but callback called!\n",
+ __func__);
+ return;
+ }
+
+ TRACE("vdec_async: tmp=0x%08x 0x%08x 0x%08x\n", tmp[0], tmp[1], tmp[2]);
+ switch (tmp[0]) {
+ case VDEC_ASYNCMSG_DECODE_DONE:
+ vdec_dcdone_handler(vd, &tmp[3], tmp[2]);
+ break;
+ case VDEC_ASYNCMSG_REUSE_FRAME:
+ vdec_reuseibuf_handler(vd, &tmp[3], tmp[2]);
+ break;
+ default:
+ pr_err("%s: Unknown async message from DSP id=0x%08x sz=%u\n",
+ __func__, tmp[0], tmp[2]);
+ }
+}
+
+static int vdec_open(struct inode *inode, struct file *file)
+{
+ int ret;
+ int i;
+ struct vdec_msg_list *l;
+ struct vdec_data *vd;
+ struct dal_info version_info;
+ char *portname = NULL;
+
+ pr_info("q6vdec_open()\n");
+ mutex_lock(&vdec_ref_lock);
+ if (ref_cnt >= MAX_SUPPORTED_INSTANCES) {
+ pr_err("%s: Max allowed instances exceeded \n", __func__);
+ mutex_unlock(&vdec_ref_lock);
+ return -EBUSY;
+ }
+ ref_cnt++;
+ mutex_unlock(&vdec_ref_lock);
+
+ vd = kmalloc(sizeof(struct vdec_data), GFP_KERNEL);
+ if (!vd) {
+ pr_err("%s: kmalloc failed\n", __func__);
+ ret = -ENOMEM;
+ goto vdec_open_err_handle_vd;
+ }
+ file->private_data = vd;
+
+ vd->mem_initialized = 0;
+ INIT_LIST_HEAD(&vd->vdec_msg_list_head);
+ INIT_LIST_HEAD(&vd->vdec_msg_list_free);
+ INIT_LIST_HEAD(&vd->vdec_mem_list_head);
+ init_waitqueue_head(&vd->vdec_msg_evt);
+
+ spin_lock_init(&vd->vdec_list_lock);
+ spin_lock_init(&vd->vdec_mem_list_lock);
+ for (i = 0; i < VDEC_MSG_MAX; i++) {
+ l = kzalloc(sizeof(struct vdec_msg_list), GFP_KERNEL);
+ if (!l) {
+ pr_err("%s: kzalloc failed!\n", __func__);
+ ret = -ENOMEM;
+ goto vdec_open_err_handle_list;
+ }
+ list_add(&l->list, &vd->vdec_msg_list_free);
+ }
+
+ memset(&vd->streamDetails, 0, sizeof(struct videoStreamDetails));
+
+ mutex_lock(&vdec_ref_lock);
+ vdec_get_next_portanddevid(&vd->Q6deviceId, &portname);
+ mutex_unlock(&vdec_ref_lock);
+
+ if ((0 == vd->Q6deviceId) || (NULL == portname)) {
+ pr_err("%s: FATAL error portname %s or deviceId %d not picked properly\n",
+ __func__, portname, vd->Q6deviceId);
+ ret = -EIO;
+ goto vdec_open_err_handle_list;
+ } else {
+ pr_err("attaching on deviceid %x portname %s\n",
+ vd->Q6deviceId, portname);
+ vd->vdec_handle = dal_attach(vd->Q6deviceId,
+ portname, 1, callback, vd);
+ }
+
+ if (!vd->vdec_handle) {
+ pr_err("%s: failed to attach\n", __func__);
+ ret = -EIO;
+ goto vdec_open_err_handle_list;
+ }
+ ret = dal_call_f9(vd->vdec_handle, DAL_OP_INFO,
+ &version_info, sizeof(struct dal_info));
+
+ if (ret) {
+ pr_err("%s: failed to get version \n", __func__);
+ goto vdec_open_err_handle_version;
+ }
+
+ TRACE("q6vdec_open() interface version 0x%x\n", version_info.version);
+ if (vdec_check_version(VDEC_INTERFACE_VERSION,
+ version_info.version)) {
+ pr_err("%s: driver version mismatch !\n", __func__);
+ goto vdec_open_err_handle_version;
+ }
+
+ vd->running = 1;
+ prevent_sleep();
+
+ return 0;
+vdec_open_err_handle_version:
+ dal_detach(vd->vdec_handle);
+vdec_open_err_handle_list:
+ {
+ struct vdec_msg_list *l, *n;
+ list_for_each_entry_safe(l, n, &vd->vdec_msg_list_free, list) {
+ list_del(&l->list);
+ kfree(l);
+ }
+ }
+vdec_open_err_handle_vd:
+ mutex_lock(&vdec_ref_lock);
+ vdec_freeup_portanddevid(vd->Q6deviceId);
+ ref_cnt--;
+ mutex_unlock(&vdec_ref_lock);
+ kfree(vd);
+ return ret;
+}
+
+static int vdec_release(struct inode *inode, struct file *file)
+{
+ int ret;
+ struct vdec_msg_list *l, *n;
+ struct vdec_mem_list *m, *k;
+ struct vdec_data *vd = file->private_data;
+
+ vd->running = 0;
+ wake_up_all(&vd->vdec_msg_evt);
+
+ if (!vd->close_decode)
+ vdec_close(vd, NULL);
+
+ ret = dal_detach(vd->vdec_handle);
+ if (ret)
+ printk(KERN_INFO "%s: failed to detach (%d)\n", __func__, ret);
+
+ list_for_each_entry_safe(l, n, &vd->vdec_msg_list_free, list) {
+ list_del(&l->list);
+ kfree(l);
+ }
+
+ list_for_each_entry_safe(l, n, &vd->vdec_msg_list_head, list) {
+ list_del(&l->list);
+ kfree(l);
+ }
+
+ list_for_each_entry_safe(m, k, &vd->vdec_mem_list_head, list) {
+ list_del(&m->list);
+ kfree(m);
+ }
+ mutex_lock(&vdec_ref_lock);
+ BUG_ON(ref_cnt <= 0);
+ ref_cnt--;
+ vdec_freeup_portanddevid(vd->Q6deviceId);
+ mutex_unlock(&vdec_ref_lock);
+
+ mutex_lock(&vdec_rm_lock);
+ vdec_rm_freeupResources(vd);
+ mutex_unlock(&vdec_rm_lock);
+
+
+ kfree(vd);
+ allow_sleep();
+ return 0;
+}
+
+static const struct file_operations vdec_fops = {
+ .owner = THIS_MODULE,
+ .open = vdec_open,
+ .release = vdec_release,
+ .unlocked_ioctl = vdec_ioctl,
+};
+
+static int __init vdec_init(void)
+{
+ struct device *class_dev;
+ int rc = 0;
+
+ wake_lock_init(&idlelock, WAKE_LOCK_IDLE, "vdec_idle");
+ wake_lock_init(&wakelock, WAKE_LOCK_SUSPEND, "vdec_suspend");
+
+ rc = alloc_chrdev_region(&vdec_device_no, 0, 1, "vdec");
+ if (rc < 0) {
+ pr_err("%s: alloc_chrdev_region failed %d\n", __func__, rc);
+ return rc;
+ }
+
+ driver_class = class_create(THIS_MODULE, "vdec");
+ if (IS_ERR(driver_class)) {
+ rc = -ENOMEM;
+ pr_err("%s: class_create failed %d\n", __func__, rc);
+ goto vdec_init_err_unregister_chrdev_region;
+ }
+ class_dev = device_create(driver_class, NULL,
+ vdec_device_no, NULL, "vdec");
+ if (!class_dev) {
+ pr_err("%s: class_device_create failed %d\n", __func__, rc);
+ rc = -ENOMEM;
+ goto vdec_init_err_class_destroy;
+ }
+
+ cdev_init(&vdec_cdev, &vdec_fops);
+ vdec_cdev.owner = THIS_MODULE;
+ rc = cdev_add(&vdec_cdev, MKDEV(MAJOR(vdec_device_no), 0), 1);
+
+ if (rc < 0) {
+ pr_err("%s: cdev_add failed %d\n", __func__, rc);
+ goto vdec_init_err_class_device_destroy;
+ }
+
+ memset(&deviceIdRegistry, 0, sizeof(deviceIdRegistry));
+ memset(&loadOnPorts, 0, sizeof(loadOnPorts));
+ numOfPorts = 0;
+
+ return 0;
+
+vdec_init_err_class_device_destroy:
+ device_destroy(driver_class, vdec_device_no);
+vdec_init_err_class_destroy:
+ class_destroy(driver_class);
+vdec_init_err_unregister_chrdev_region:
+ unregister_chrdev_region(vdec_device_no, 1);
+ return rc;
+}
+
+static void __exit vdec_exit(void)
+{
+ device_destroy(driver_class, vdec_device_no);
+ class_destroy(driver_class);
+ unregister_chrdev_region(vdec_device_no, 1);
+}
+
+MODULE_DESCRIPTION("video decoder driver for QSD platform");
+MODULE_VERSION("2.00");
+
+module_init(vdec_init);
+module_exit(vdec_exit);
diff --git a/arch/arm/mach-msm/qdsp6/msm_q6venc.c b/arch/arm/mach-msm/qdsp6/msm_q6venc.c
new file mode 100644
index 0000000..bd5d3f6
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/msm_q6venc.c
@@ -0,0 +1,1195 @@
+/* Copyright (c) 2008-2009, 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/slab.h>
+#include <linux/cdev.h>
+#include <linux/file.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/wakelock.h>
+#include <linux/android_pmem.h>
+#include <linux/msm_q6venc.h>
+#include "dal.h"
+
+#define DALDEVICEID_VENC_DEVICE 0x0200002D
+#define DALDEVICEID_VENC_PORTNAME "DAL_AQ_VID"
+
+#define VENC_NAME "q6venc"
+#define VENC_MSG_MAX 128
+
+#define VENC_INTERFACE_VERSION 0x00020000
+#define MAJOR_MASK 0xFFFF0000
+#define MINOR_MASK 0x0000FFFF
+#define VENC_GET_MAJOR_VERSION(version) ((version & MAJOR_MASK)>>16)
+#define VENC_GET_MINOR_VERSION(version) (version & MINOR_MASK)
+
+enum {
+ VENC_BUFFER_TYPE_INPUT,
+ VENC_BUFFER_TYPE_OUTPUT,
+ VENC_BUFFER_TYPE_QDSP6,
+ VENC_BUFFER_TYPE_HDR
+};
+enum {
+ VENC_DALRPC_GET_SYNTAX_HEADER = DAL_OP_FIRST_DEVICE_API,
+ VENC_DALRPC_UPDATE_INTRA_REFRESH,
+ VENC_DALRPC_UPDATE_FRAME_RATE,
+ VENC_DALRPC_UPDATE_BITRATE,
+ VENC_DALRPC_UPDATE_QP_RANGE,
+ VENC_DALRPC_UPDATE_INTRA_PERIOD,
+ VENC_DALRPC_REQUEST_IFRAME,
+ VENC_DALRPC_START,
+ VENC_DALRPC_STOP,
+ VENC_DALRPC_SUSPEND,
+ VENC_DALRPC_RESUME,
+ VENC_DALRPC_FLUSH,
+ VENC_DALRPC_QUEUE_INPUT,
+ VENC_DALRPC_QUEUE_OUTPUT
+};
+struct venc_input_payload {
+ u32 data;
+};
+struct venc_output_payload {
+ u32 size;
+ long long time_stamp;
+ u32 flags;
+ u32 data;
+ u32 client_data_from_input;
+};
+union venc_payload {
+ struct venc_input_payload input_payload;
+ struct venc_output_payload output_payload;
+};
+struct venc_msg_type {
+ u32 event;
+ u32 status;
+ union venc_payload payload;
+};
+struct venc_input_buf {
+ struct venc_buf_type yuv_buf;
+ u32 data_size;
+ long long time_stamp;
+ u32 flags;
+ u32 dvs_offsetx;
+ u32 dvs_offsety;
+ u32 client_data;
+ u32 op_client_data;
+};
+struct venc_output_buf {
+ struct venc_buf_type bit_stream_buf;
+ u32 client_data;
+};
+
+struct venc_msg_list {
+ struct list_head list;
+ struct venc_msg msg_data;
+};
+struct venc_buf {
+ int fd;
+ u32 src;
+ u32 offset;
+ u32 size;
+ u32 btype;
+ unsigned long paddr;
+ struct file *file;
+};
+struct venc_pmem_list {
+ struct list_head list;
+ struct venc_buf buf;
+};
+struct venc_dev {
+ bool is_active;
+ bool pmem_freed;
+ enum venc_state_type state;
+ struct list_head venc_msg_list_head;
+ struct list_head venc_msg_list_free;
+ spinlock_t venc_msg_list_lock;
+ struct list_head venc_pmem_list_head;
+ spinlock_t venc_pmem_list_lock;
+ struct dal_client *q6_handle;
+ wait_queue_head_t venc_msg_evt;
+ struct device *class_devp;
+};
+
+#define DEBUG_VENC 0
+#if DEBUG_VENC
+#define TRACE(fmt, x...) \
+ do { pr_debug("%s:%d " fmt, __func__, __LINE__, ##x); } while (0)
+#else
+#define TRACE(fmt, x...) do { } while (0)
+#endif
+
+static struct cdev cdev;
+static dev_t venc_dev_num;
+static struct class *venc_class;
+static struct venc_dev *venc_device_p;
+static int venc_ref;
+
+static DEFINE_MUTEX(idlecount_lock);
+static int idlecount;
+static struct wake_lock wakelock;
+static struct wake_lock idlelock;
+
+static void prevent_sleep(void)
+{
+ mutex_lock(&idlecount_lock);
+ if (++idlecount == 1) {
+ wake_lock(&idlelock);
+ wake_lock(&wakelock);
+ }
+ mutex_unlock(&idlecount_lock);
+}
+
+static void allow_sleep(void)
+{
+ mutex_lock(&idlecount_lock);
+ if (--idlecount == 0) {
+ wake_unlock(&idlelock);
+ wake_unlock(&wakelock);
+ }
+ mutex_unlock(&idlecount_lock);
+}
+
+static inline int venc_check_version(u32 client, u32 server)
+{
+ int ret = -EINVAL;
+
+ if ((VENC_GET_MAJOR_VERSION(client) == VENC_GET_MAJOR_VERSION(server))
+ && (VENC_GET_MINOR_VERSION(client) <=
+ VENC_GET_MINOR_VERSION(server)))
+ ret = 0;
+
+ return ret;
+}
+
+static int venc_get_msg(struct venc_dev *dvenc, void *msg)
+{
+ struct venc_msg_list *l;
+ unsigned long flags;
+ int ret = 0;
+ struct venc_msg qdsp_msg;
+
+ if (!dvenc->is_active)
+ return -EPERM;
+ spin_lock_irqsave(&dvenc->venc_msg_list_lock, flags);
+ list_for_each_entry_reverse(l, &dvenc->venc_msg_list_head, list) {
+ memcpy(&qdsp_msg, &l->msg_data, sizeof(struct venc_msg));
+ list_del(&l->list);
+ list_add(&l->list, &dvenc->venc_msg_list_free);
+ ret = 1;
+ break;
+ }
+ spin_unlock_irqrestore(&dvenc->venc_msg_list_lock, flags);
+ if (copy_to_user(msg, &qdsp_msg, sizeof(struct venc_msg)))
+ pr_err("%s failed to copy_to_user\n", __func__);
+ return ret;
+}
+
+static void venc_put_msg(struct venc_dev *dvenc, struct venc_msg *msg)
+{
+ struct venc_msg_list *l;
+ unsigned long flags;
+ int found = 0;
+
+ spin_lock_irqsave(&dvenc->venc_msg_list_lock, flags);
+ list_for_each_entry(l, &dvenc->venc_msg_list_free, list) {
+ memcpy(&l->msg_data, msg, sizeof(struct venc_msg));
+ list_del(&l->list);
+ list_add(&l->list, &dvenc->venc_msg_list_head);
+ found = 1;
+ break;
+ }
+ spin_unlock_irqrestore(&dvenc->venc_msg_list_lock, flags);
+ if (found)
+ wake_up(&dvenc->venc_msg_evt);
+ else
+ pr_err("%s: failed to find a free node\n", __func__);
+
+}
+
+static struct venc_pmem_list *venc_add_pmem_to_list(struct venc_dev *dvenc,
+ struct venc_pmem *mptr,
+ u32 btype)
+{
+ int ret = 0;
+ unsigned long flags;
+ unsigned long len;
+ unsigned long vaddr;
+ struct venc_pmem_list *plist = NULL;
+
+ plist = kzalloc(sizeof(struct venc_pmem_list), GFP_KERNEL);
+ if (!plist) {
+ pr_err("%s: kzalloc failed\n", __func__);
+ return NULL;
+ }
+
+ ret = get_pmem_file(mptr->fd, &(plist->buf.paddr),
+ &vaddr, &len, &(plist->buf.file));
+ if (ret) {
+ pr_err("%s: get_pmem_file failed for fd=%d offset=%d\n",
+ __func__, mptr->fd, mptr->offset);
+ goto err_venc_add_pmem;
+ } else if (mptr->offset >= len) {
+ pr_err("%s: invalid offset (%d > %ld) for fd=%d\n",
+ __func__, mptr->offset, len, mptr->fd);
+ ret = -EINVAL;
+ goto err_venc_get_pmem;
+ }
+
+ plist->buf.fd = mptr->fd;
+ plist->buf.paddr += mptr->offset;
+ plist->buf.size = mptr->size;
+ plist->buf.btype = btype;
+ plist->buf.offset = mptr->offset;
+ plist->buf.src = mptr->src;
+
+ spin_lock_irqsave(&dvenc->venc_pmem_list_lock, flags);
+ list_add(&plist->list, &dvenc->venc_pmem_list_head);
+ spin_unlock_irqrestore(&dvenc->venc_pmem_list_lock, flags);
+ return plist;
+
+err_venc_get_pmem:
+ put_pmem_file(plist->buf.file);
+err_venc_add_pmem:
+ kfree(plist);
+ return NULL;
+}
+
+static struct venc_pmem_list *venc_get_pmem_from_list(
+ struct venc_dev *dvenc, u32 pmem_fd,
+ u32 offset, u32 btype)
+{
+ struct venc_pmem_list *plist;
+ unsigned long flags;
+ struct file *file;
+ int found = 0;
+
+ file = fget(pmem_fd);
+ if (!file) {
+ pr_err("%s: invalid encoder buffer fd(%d)\n", __func__,
+ pmem_fd);
+ return NULL;
+ }
+ spin_lock_irqsave(&dvenc->venc_pmem_list_lock, flags);
+ list_for_each_entry(plist, &dvenc->venc_pmem_list_head, list) {
+ if (plist->buf.btype == btype && plist->buf.file == file &&
+ plist->buf.offset == offset) {
+ found = 1;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&dvenc->venc_pmem_list_lock, flags);
+ fput(file);
+ if (found)
+ return plist;
+
+ else
+ return NULL;
+}
+
+static int venc_set_buffer(struct venc_dev *dvenc, void *argp,
+ u32 btype)
+{
+ struct venc_pmem pmem;
+ struct venc_pmem_list *plist;
+ int ret = 0;
+
+ ret = copy_from_user(&pmem, argp, sizeof(pmem));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+ plist = venc_add_pmem_to_list(dvenc, &pmem, btype);
+ if (plist == NULL) {
+ pr_err("%s: buffer add_to_pmem_list failed\n",
+ __func__);
+ return -EPERM;
+ }
+ return ret;
+}
+
+static int venc_assign_q6_buffers(struct venc_dev *dvenc,
+ struct venc_buffers *pbufs,
+ struct venc_nonio_buf_config *pcfg)
+{
+ int ret = 0;
+ struct venc_pmem_list *plist;
+
+ plist = venc_add_pmem_to_list(dvenc, &(pbufs->recon_buf[0]),
+ VENC_BUFFER_TYPE_QDSP6);
+ if (plist == NULL) {
+ pr_err("%s: recon_buf0 failed to add_to_pmem_list\n",
+ __func__);
+ return -EPERM;
+ }
+ pcfg->recon_buf1.region = pbufs->recon_buf[0].src;
+ pcfg->recon_buf1.phys = plist->buf.paddr;
+ pcfg->recon_buf1.size = plist->buf.size;
+ pcfg->recon_buf1.offset = 0;
+
+ plist = venc_add_pmem_to_list(dvenc, &(pbufs->recon_buf[1]),
+ VENC_BUFFER_TYPE_QDSP6);
+ if (plist == NULL) {
+ pr_err("%s: recons_buf1 failed to add_to_pmem_list\n",
+ __func__);
+ return -EPERM;
+ }
+ pcfg->recon_buf2.region = pbufs->recon_buf[1].src;
+ pcfg->recon_buf2.phys = plist->buf.paddr;
+ pcfg->recon_buf2.size = plist->buf.size;
+ pcfg->recon_buf2.offset = 0;
+
+ plist = venc_add_pmem_to_list(dvenc, &(pbufs->wb_buf),
+ VENC_BUFFER_TYPE_QDSP6);
+ if (plist == NULL) {
+ pr_err("%s: wb_buf failed to add_to_pmem_list\n",
+ __func__);
+ return -EPERM;
+ }
+ pcfg->wb_buf.region = pbufs->wb_buf.src;
+ pcfg->wb_buf.phys = plist->buf.paddr;
+ pcfg->wb_buf.size = plist->buf.size;
+ pcfg->wb_buf.offset = 0;
+
+ plist = venc_add_pmem_to_list(dvenc, &(pbufs->cmd_buf),
+ VENC_BUFFER_TYPE_QDSP6);
+ if (plist == NULL) {
+ pr_err("%s: cmd_buf failed to add_to_pmem_list\n",
+ __func__);
+ return -EPERM;
+ }
+ pcfg->cmd_buf.region = pbufs->cmd_buf.src;
+ pcfg->cmd_buf.phys = plist->buf.paddr;
+ pcfg->cmd_buf.size = plist->buf.size;
+ pcfg->cmd_buf.offset = 0;
+
+ plist = venc_add_pmem_to_list(dvenc, &(pbufs->vlc_buf),
+ VENC_BUFFER_TYPE_QDSP6);
+ if (plist == NULL) {
+ pr_err("%s: vlc_buf failed to add_to_pmem_list"
+ " failed\n", __func__);
+ return -EPERM;
+ }
+ pcfg->vlc_buf.region = pbufs->vlc_buf.src;
+ pcfg->vlc_buf.phys = plist->buf.paddr;
+ pcfg->vlc_buf.size = plist->buf.size;
+ pcfg->vlc_buf.offset = 0;
+
+ return ret;
+}
+
+static int venc_start(struct venc_dev *dvenc, void *argp)
+{
+ int ret = 0;
+ struct venc_q6_config q6_config;
+ struct venc_init_config vconfig;
+
+ dvenc->state = VENC_STATE_START;
+ ret = copy_from_user(&vconfig, argp, sizeof(struct venc_init_config));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+ memcpy(&q6_config, &(vconfig.q6_config), sizeof(q6_config));
+ ret = venc_assign_q6_buffers(dvenc, &(vconfig.q6_bufs),
+ &(q6_config.buf_params));
+ if (ret != 0) {
+ pr_err("%s: assign_q6_buffers failed\n", __func__);
+ return -EPERM;
+ }
+
+ q6_config.callback_event = dvenc->q6_handle;
+ TRACE("%s: parameters: handle:%p, config:%p, callback:%p \n", __func__,
+ dvenc->q6_handle, &q6_config, q6_config.callback_event);
+ TRACE("%s: parameters:recon1:0x%x, recon2:0x%x,"
+ " wb_buf:0x%x, cmd:0x%x, vlc:0x%x\n", __func__,
+ q6_config.buf_params.recon_buf1.phys,
+ q6_config.buf_params.recon_buf2.phys,
+ q6_config.buf_params.wb_buf.phys,
+ q6_config.buf_params.cmd_buf.phys,
+ q6_config.buf_params.vlc_buf.phys);
+ TRACE("%s: size of param:%d \n", __func__, sizeof(q6_config));
+ ret = dal_call_f5(dvenc->q6_handle, VENC_DALRPC_START, &q6_config,
+ sizeof(q6_config));
+ if (ret != 0) {
+ pr_err("%s: remote function failed (%d)\n", __func__, ret);
+ return ret;
+ }
+ return ret;
+}
+
+static int venc_encode_frame(struct venc_dev *dvenc, void *argp)
+{
+ int ret = 0;
+ struct venc_pmem buf;
+ struct venc_input_buf q6_input;
+ struct venc_pmem_list *plist;
+ struct venc_buffer input;
+
+ ret = copy_from_user(&input, argp, sizeof(struct venc_buffer));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+ ret = copy_from_user(&buf,
+ ((struct venc_buffer *)argp)->ptr_buffer,
+ sizeof(struct venc_pmem));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+
+ plist = venc_get_pmem_from_list(dvenc, buf.fd, buf.offset,
+ VENC_BUFFER_TYPE_INPUT);
+ if (NULL == plist) {
+ plist = venc_add_pmem_to_list(dvenc, &buf,
+ VENC_BUFFER_TYPE_INPUT);
+ if (plist == NULL) {
+ pr_err("%s: buffer add_to_pmem_list failed\n",
+ __func__);
+ return -EPERM;
+ }
+ }
+
+ q6_input.flags = 0;
+ if (input.flags & VENC_FLAG_EOS)
+ q6_input.flags |= 0x00000001;
+ q6_input.yuv_buf.region = plist->buf.src;
+ q6_input.yuv_buf.phys = plist->buf.paddr;
+ q6_input.yuv_buf.size = plist->buf.size;
+ q6_input.yuv_buf.offset = 0;
+ q6_input.data_size = plist->buf.size;
+ q6_input.client_data = (u32)input.client_data;
+ q6_input.time_stamp = input.time_stamp;
+ q6_input.dvs_offsetx = 0;
+ q6_input.dvs_offsety = 0;
+
+ TRACE("Pushing down input phys=0x%x fd= %d, client_data: 0x%x,"
+ " time_stamp:%lld \n", q6_input.yuv_buf.phys, plist->buf.fd,
+ input.client_data, input.time_stamp);
+ ret = dal_call_f5(dvenc->q6_handle, VENC_DALRPC_QUEUE_INPUT,
+ &q6_input, sizeof(q6_input));
+
+ if (ret != 0)
+ pr_err("%s: Q6 queue_input failed (%d)\n", __func__,
+ (int)ret);
+ return ret;
+}
+
+static int venc_fill_output(struct venc_dev *dvenc, void *argp)
+{
+ int ret = 0;
+ struct venc_pmem buf;
+ struct venc_output_buf q6_output;
+ struct venc_pmem_list *plist;
+ struct venc_buffer output;
+
+ ret = copy_from_user(&output, argp, sizeof(struct venc_buffer));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+ ret = copy_from_user(&buf,
+ ((struct venc_buffer *)argp)->ptr_buffer,
+ sizeof(struct venc_pmem));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+ plist = venc_get_pmem_from_list(dvenc, buf.fd, buf.offset,
+ VENC_BUFFER_TYPE_OUTPUT);
+ if (NULL == plist) {
+ plist = venc_add_pmem_to_list(dvenc, &buf,
+ VENC_BUFFER_TYPE_OUTPUT);
+ if (NULL == plist) {
+ pr_err("%s: output buffer failed to add_to_pmem_list"
+ "\n", __func__);
+ return -EPERM;
+ }
+ }
+ q6_output.bit_stream_buf.region = plist->buf.src;
+ q6_output.bit_stream_buf.phys = (u32)plist->buf.paddr;
+ q6_output.bit_stream_buf.size = plist->buf.size;
+ q6_output.bit_stream_buf.offset = 0;
+ q6_output.client_data = (u32)output.client_data;
+ ret =
+ dal_call_f5(dvenc->q6_handle, VENC_DALRPC_QUEUE_OUTPUT, &q6_output,
+ sizeof(q6_output));
+ if (ret != 0)
+ pr_err("%s: remote function failed (%d)\n", __func__, ret);
+ return ret;
+}
+
+static int venc_stop(struct venc_dev *dvenc)
+{
+ int ret = 0;
+ struct venc_msg msg;
+
+ ret = dal_call_f0(dvenc->q6_handle, VENC_DALRPC_STOP, 1);
+ if (ret) {
+ pr_err("%s: remote runction failed (%d)\n", __func__, ret);
+ msg.msg_code = VENC_MSG_STOP;
+ msg.msg_data_size = 0;
+ msg.status_code = VENC_S_EFAIL;
+ venc_put_msg(dvenc, &msg);
+ }
+ return ret;
+}
+
+static int venc_pause(struct venc_dev *dvenc)
+{
+ int ret = 0;
+ struct venc_msg msg;
+
+ ret = dal_call_f0(dvenc->q6_handle, VENC_DALRPC_SUSPEND, 1);
+ if (ret) {
+ pr_err("%s: remote function failed (%d)\n", __func__, ret);
+ msg.msg_code = VENC_MSG_PAUSE;
+ msg.status_code = VENC_S_EFAIL;
+ msg.msg_data_size = 0;
+ venc_put_msg(dvenc, &msg);
+ }
+ return ret;
+}
+
+static int venc_resume(struct venc_dev *dvenc)
+{
+ int ret = 0;
+ struct venc_msg msg;
+
+ ret = dal_call_f0(dvenc->q6_handle, VENC_DALRPC_RESUME, 1);
+ if (ret) {
+ pr_err("%s: remote function failed (%d)\n", __func__, ret);
+ msg.msg_code = VENC_MSG_RESUME;
+ msg.msg_data_size = 0;
+ msg.status_code = VENC_S_EFAIL;
+ venc_put_msg(dvenc, &msg);
+ }
+ return ret;
+}
+
+static int venc_flush(struct venc_dev *dvenc, void *argp)
+{
+ int ret = 0;
+ struct venc_msg msg;
+ union venc_msg_data smsg;
+ int status = VENC_S_SUCCESS;
+ struct venc_buffer_flush flush;
+
+ if (copy_from_user(&flush, argp, sizeof(struct venc_buffer_flush)))
+ return -EFAULT;
+ if (flush.flush_mode == VENC_FLUSH_ALL) {
+ ret = dal_call_f0(dvenc->q6_handle, VENC_DALRPC_FLUSH, 1);
+ if (ret)
+ status = VENC_S_EFAIL;
+ } else
+ status = VENC_S_ENOTSUPP;
+
+ if (status != VENC_S_SUCCESS) {
+ if ((flush.flush_mode == VENC_FLUSH_INPUT) ||
+ (flush.flush_mode == VENC_FLUSH_ALL)) {
+ smsg.flush_ret.flush_mode = VENC_FLUSH_INPUT;
+ msg.msg_data = smsg;
+ msg.status_code = status;
+ msg.msg_code = VENC_MSG_FLUSH;
+ msg.msg_data_size = sizeof(union venc_msg_data);
+ venc_put_msg(dvenc, &msg);
+ }
+ if (flush.flush_mode == VENC_FLUSH_OUTPUT ||
+ (flush.flush_mode == VENC_FLUSH_ALL)) {
+ smsg.flush_ret.flush_mode = VENC_FLUSH_OUTPUT;
+ msg.msg_data = smsg;
+ msg.status_code = status;
+ msg.msg_code = VENC_MSG_FLUSH;
+ msg.msg_data_size = sizeof(union venc_msg_data);
+ venc_put_msg(dvenc, &msg);
+ }
+ return -EIO;
+ }
+ return ret;
+}
+
+static int venc_get_sequence_hdr(struct venc_dev *dvenc, void *argp)
+{
+ pr_err("%s not supported\n", __func__);
+ return -EIO;
+}
+
+static int venc_set_qp_range(struct venc_dev *dvenc, void *argp)
+{
+ int ret = 0;
+ struct venc_qp_range qp;
+
+ ret = copy_from_user(&qp, argp, sizeof(struct venc_qp_range));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+
+ if (dvenc->state == VENC_STATE_START ||
+ dvenc->state == VENC_STATE_PAUSE) {
+ ret =
+ dal_call_f5(dvenc->q6_handle, VENC_DALRPC_UPDATE_QP_RANGE,
+ &qp, sizeof(struct venc_qp_range));
+ if (ret) {
+ pr_err("%s: remote function failed (%d) \n", __func__,
+ ret);
+ return ret;
+ }
+ }
+ return ret;
+}
+
+static int venc_set_intra_period(struct venc_dev *dvenc, void *argp)
+{
+ int ret = 0;
+ u32 pnum = 0;
+
+ ret = copy_from_user(&pnum, argp, sizeof(int));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+ if (dvenc->state == VENC_STATE_START ||
+ dvenc->state == VENC_STATE_PAUSE) {
+ ret = dal_call_f0(dvenc->q6_handle,
+ VENC_DALRPC_UPDATE_INTRA_PERIOD, pnum);
+ if (ret)
+ pr_err("%s: remote function failed (%d)\n", __func__,
+ ret);
+ }
+ return ret;
+}
+
+static int venc_set_intra_refresh(struct venc_dev *dvenc, void *argp)
+{
+ int ret = 0;
+ u32 mb_num = 0;
+
+ ret = copy_from_user(&mb_num, argp, sizeof(int));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+ if (dvenc->state == VENC_STATE_START ||
+ dvenc->state == VENC_STATE_PAUSE) {
+ ret = dal_call_f0(dvenc->q6_handle,
+ VENC_DALRPC_UPDATE_INTRA_REFRESH, mb_num);
+ if (ret)
+ pr_err("%s: remote function failed (%d)\n", __func__,
+ ret);
+ }
+ return ret;
+}
+
+static int venc_set_frame_rate(struct venc_dev *dvenc, void *argp)
+{
+ int ret = 0;
+ struct venc_frame_rate pdata;
+ ret = copy_from_user(&pdata, argp, sizeof(struct venc_frame_rate));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+ if (dvenc->state == VENC_STATE_START ||
+ dvenc->state == VENC_STATE_PAUSE) {
+ ret = dal_call_f5(dvenc->q6_handle,
+ VENC_DALRPC_UPDATE_FRAME_RATE,
+ (void *)&(pdata),
+ sizeof(struct venc_frame_rate));
+ if (ret)
+ pr_err("%s: remote function failed (%d)\n", __func__,
+ ret);
+ }
+ return ret;
+}
+
+static int venc_set_target_bitrate(struct venc_dev *dvenc, void *argp)
+{
+ int ret = 0;
+ u32 pdata = 0;
+
+ ret = copy_from_user(&pdata, argp, sizeof(int));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+ if (dvenc->state == VENC_STATE_START ||
+ dvenc->state == VENC_STATE_PAUSE) {
+ ret = dal_call_f0(dvenc->q6_handle,
+ VENC_DALRPC_UPDATE_BITRATE, pdata);
+ if (ret)
+ pr_err("%s: remote function failed (%d)\n", __func__,
+ ret);
+ }
+ return ret;
+}
+
+static int venc_request_iframe(struct venc_dev *dvenc)
+{
+ int ret = 0;
+
+ if (dvenc->state != VENC_STATE_START)
+ return -EINVAL;
+
+ ret = dal_call_f0(dvenc->q6_handle, VENC_DALRPC_REQUEST_IFRAME, 1);
+ if (ret)
+ pr_err("%s: remote function failed (%d)\n", __func__, ret);
+ return ret;
+}
+
+static int venc_stop_read_msg(struct venc_dev *dvenc)
+{
+ struct venc_msg msg;
+ int ret = 0;
+
+ msg.status_code = 0;
+ msg.msg_code = VENC_MSG_STOP_READING_MSG;
+ msg.msg_data_size = 0;
+ venc_put_msg(dvenc, &msg);
+ return ret;
+}
+
+static int venc_q6_stop(struct venc_dev *dvenc)
+{
+ int ret = 0;
+ struct venc_pmem_list *plist;
+ unsigned long flags;
+
+ wake_up(&dvenc->venc_msg_evt);
+ spin_lock_irqsave(&dvenc->venc_pmem_list_lock, flags);
+ if (!dvenc->pmem_freed) {
+ list_for_each_entry(plist, &dvenc->venc_pmem_list_head, list)
+ put_pmem_file(plist->buf.file);
+ dvenc->pmem_freed = 1;
+ }
+ spin_unlock_irqrestore(&dvenc->venc_pmem_list_lock, flags);
+
+ dvenc->state = VENC_STATE_STOP;
+ return ret;
+}
+
+static int venc_translate_error(enum venc_status_code q6_status)
+{
+ int ret = 0;
+
+ switch (q6_status) {
+ case VENC_STATUS_SUCCESS:
+ ret = VENC_S_SUCCESS;
+ break;
+ case VENC_STATUS_ERROR:
+ ret = VENC_S_EFAIL;
+ break;
+ case VENC_STATUS_INVALID_STATE:
+ ret = VENC_S_EINVALSTATE;
+ break;
+ case VENC_STATUS_FLUSHING:
+ ret = VENC_S_EFLUSHED;
+ break;
+ case VENC_STATUS_INVALID_PARAM:
+ ret = VENC_S_EBADPARAM;
+ break;
+ case VENC_STATUS_CMD_QUEUE_FULL:
+ ret = VENC_S_ECMDQFULL;
+ break;
+ case VENC_STATUS_CRITICAL:
+ ret = VENC_S_EFATAL;
+ break;
+ case VENC_STATUS_INSUFFICIENT_RESOURCES:
+ ret = VENC_S_ENOHWRES;
+ break;
+ case VENC_STATUS_TIMEOUT:
+ ret = VENC_S_ETIMEOUT;
+ break;
+ }
+ if (q6_status != VENC_STATUS_SUCCESS)
+ pr_err("%s: Q6 failed (%d)", __func__, (int)q6_status);
+ return ret;
+}
+
+static void venc_q6_callback(void *data, int len, void *cookie)
+{
+ int status = 0;
+ struct venc_dev *dvenc = (struct venc_dev *)cookie;
+ struct venc_msg_type *q6_msg = NULL;
+ struct venc_msg msg, msg1;
+ union venc_msg_data smsg1, smsg2;
+ unsigned long msg_code = 0;
+ struct venc_input_payload *pload1;
+ struct venc_output_payload *pload2;
+ uint32_t * tmp = (uint32_t *) data;
+
+ if (dvenc == NULL) {
+ pr_err("%s: empty driver parameter\n", __func__);
+ return;
+ }
+ if (tmp[2] == sizeof(struct venc_msg_type)) {
+ q6_msg = (struct venc_msg_type *)&tmp[3];
+ } else {
+ pr_err("%s: callback with empty message (%d, %d)\n",
+ __func__, tmp[2], sizeof(struct venc_msg_type));
+ return;
+ }
+ msg.msg_data_size = 0;
+ status = venc_translate_error(q6_msg->status);
+ switch ((enum venc_event_type_enum)q6_msg->event) {
+ case VENC_EVENT_START_STATUS:
+ dvenc->state = VENC_STATE_START;
+ msg_code = VENC_MSG_START;
+ break;
+ case VENC_EVENT_STOP_STATUS:
+ venc_q6_stop(dvenc);
+ msg_code = VENC_MSG_STOP;
+ break;
+ case VENC_EVENT_SUSPEND_STATUS:
+ dvenc->state = VENC_STATE_PAUSE;
+ msg_code = VENC_MSG_PAUSE;
+ break;
+ case VENC_EVENT_RESUME_STATUS:
+ dvenc->state = VENC_STATE_START;
+ msg_code = VENC_MSG_RESUME;
+ break;
+ case VENC_EVENT_FLUSH_STATUS:
+ smsg1.flush_ret.flush_mode = VENC_FLUSH_INPUT;
+ msg1.status_code = status;
+ msg1.msg_code = VENC_MSG_FLUSH;
+ msg1.msg_data = smsg1;
+ msg1.msg_data_size = sizeof(union venc_msg_data);
+ venc_put_msg(dvenc, &msg1);
+ smsg2.flush_ret.flush_mode = VENC_FLUSH_OUTPUT;
+ msg_code = VENC_MSG_FLUSH;
+ msg.msg_data = smsg2;
+ msg.msg_data_size = sizeof(union venc_msg_data);
+ break;
+ case VENC_EVENT_RELEASE_INPUT:
+ pload1 = &((q6_msg->payload).input_payload);
+ TRACE("Release_input: data: 0x%x \n", pload1->data);
+ if (pload1 != NULL) {
+ msg.msg_data.buf.client_data = pload1->data;
+ msg_code = VENC_MSG_INPUT_BUFFER_DONE;
+ msg.msg_data_size = sizeof(union venc_msg_data);
+ }
+ break;
+ case VENC_EVENT_DELIVER_OUTPUT:
+ pload2 = &((q6_msg->payload).output_payload);
+ smsg1.buf.flags = 0;
+ if (pload2->flags & VENC_FLAG_SYNC_FRAME)
+ smsg1.buf.flags |= VENC_FLAG_SYNC_FRAME;
+ if (pload2->flags & VENC_FLAG_CODEC_CONFIG)
+ smsg1.buf.flags |= VENC_FLAG_CODEC_CONFIG;
+ if (pload2->flags & VENC_FLAG_END_OF_FRAME)
+ smsg1.buf.flags |= VENC_FLAG_END_OF_FRAME;
+ if (pload2->flags & VENC_FLAG_EOS)
+ smsg1.buf.flags |= VENC_FLAG_EOS;
+ smsg1.buf.len = pload2->size;
+ smsg1.buf.offset = 0;
+ smsg1.buf.time_stamp = pload2->time_stamp;
+ smsg1.buf.client_data = pload2->data;
+ msg_code = VENC_MSG_OUTPUT_BUFFER_DONE;
+ msg.msg_data = smsg1;
+ msg.msg_data_size = sizeof(union venc_msg_data);
+ break;
+ default:
+ pr_err("%s: invalid response from Q6 (%d)\n", __func__,
+ (int)q6_msg->event);
+ return;
+ }
+ msg.status_code = status;
+ msg.msg_code = msg_code;
+ venc_put_msg(dvenc, &msg);
+ return;
+}
+
+static int venc_get_version(struct venc_dev *dvenc, void *argp)
+{
+ struct venc_version ver_info;
+ int ret = 0;
+
+ ver_info.major = VENC_GET_MAJOR_VERSION(VENC_INTERFACE_VERSION);
+ ver_info.minor = VENC_GET_MINOR_VERSION(VENC_INTERFACE_VERSION);
+
+ ret = copy_to_user(((struct venc_version *)argp),
+ &ver_info, sizeof(ver_info));
+ if (ret)
+ pr_err("%s failed to copy_to_user\n", __func__);
+
+ return ret;
+
+}
+
+static long q6venc_ioctl(struct file *file, u32 cmd,
+ unsigned long arg)
+{
+ long ret = 0;
+ void __user *argp = (void __user *)arg;
+ struct venc_dev *dvenc = file->private_data;
+
+ if (!dvenc || !dvenc->is_active)
+ return -EPERM;
+
+ switch (cmd) {
+ case VENC_IOCTL_SET_INPUT_BUFFER:
+ ret = venc_set_buffer(dvenc, argp, VENC_BUFFER_TYPE_INPUT);
+ break;
+ case VENC_IOCTL_SET_OUTPUT_BUFFER:
+ ret = venc_set_buffer(dvenc, argp, VENC_BUFFER_TYPE_OUTPUT);
+ break;
+ case VENC_IOCTL_GET_SEQUENCE_HDR:
+ ret = venc_get_sequence_hdr(dvenc, argp);
+ break;
+ case VENC_IOCTL_SET_QP_RANGE:
+ ret = venc_set_qp_range(dvenc, argp);
+ break;
+ case VENC_IOCTL_SET_INTRA_PERIOD:
+ ret = venc_set_intra_period(dvenc, argp);
+ break;
+ case VENC_IOCTL_SET_INTRA_REFRESH:
+ ret = venc_set_intra_refresh(dvenc, argp);
+ break;
+ case VENC_IOCTL_SET_FRAME_RATE:
+ ret = venc_set_frame_rate(dvenc, argp);
+ break;
+ case VENC_IOCTL_SET_TARGET_BITRATE:
+ ret = venc_set_target_bitrate(dvenc, argp);
+ break;
+ case VENC_IOCTL_CMD_REQUEST_IFRAME:
+ if (dvenc->state == VENC_STATE_START)
+ ret = venc_request_iframe(dvenc);
+ break;
+ case VENC_IOCTL_CMD_START:
+ ret = venc_start(dvenc, argp);
+ break;
+ case VENC_IOCTL_CMD_STOP:
+ ret = venc_stop(dvenc);
+ break;
+ case VENC_IOCTL_CMD_PAUSE:
+ ret = venc_pause(dvenc);
+ break;
+ case VENC_IOCTL_CMD_RESUME:
+ ret = venc_resume(dvenc);
+ break;
+ case VENC_IOCTL_CMD_ENCODE_FRAME:
+ ret = venc_encode_frame(dvenc, argp);
+ break;
+ case VENC_IOCTL_CMD_FILL_OUTPUT_BUFFER:
+ ret = venc_fill_output(dvenc, argp);
+ break;
+ case VENC_IOCTL_CMD_FLUSH:
+ ret = venc_flush(dvenc, argp);
+ break;
+ case VENC_IOCTL_CMD_READ_NEXT_MSG:
+ wait_event_interruptible(dvenc->venc_msg_evt,
+ venc_get_msg(dvenc, argp));
+ break;
+ case VENC_IOCTL_CMD_STOP_READ_MSG:
+ ret = venc_stop_read_msg(dvenc);
+ break;
+ case VENC_IOCTL_GET_VERSION:
+ ret = venc_get_version(dvenc, argp);
+ break;
+ default:
+ pr_err("%s: invalid ioctl code (%d)\n", __func__, cmd);
+ ret = -ENOTTY;
+ break;
+ }
+ return ret;
+}
+
+static int q6venc_open(struct inode *inode, struct file *file)
+{
+ int i;
+ int ret = 0;
+ struct venc_dev *dvenc;
+ struct venc_msg_list *plist, *tmp;
+ struct dal_info version_info;
+
+ dvenc = kzalloc(sizeof(struct venc_dev), GFP_KERNEL);
+ if (!dvenc) {
+ pr_err("%s: unable to allocate memory for struct venc_dev\n",
+ __func__);
+ return -ENOMEM;
+ }
+ file->private_data = dvenc;
+ INIT_LIST_HEAD(&dvenc->venc_msg_list_head);
+ INIT_LIST_HEAD(&dvenc->venc_msg_list_free);
+ INIT_LIST_HEAD(&dvenc->venc_pmem_list_head);
+ init_waitqueue_head(&dvenc->venc_msg_evt);
+ spin_lock_init(&dvenc->venc_msg_list_lock);
+ spin_lock_init(&dvenc->venc_pmem_list_lock);
+ venc_ref++;
+ for (i = 0; i < VENC_MSG_MAX; i++) {
+ plist = kzalloc(sizeof(struct venc_msg_list), GFP_KERNEL);
+ if (!plist) {
+ pr_err("%s: kzalloc failed\n", __func__);
+ ret = -ENOMEM;
+ goto err_venc_create_msg_list;
+ }
+ list_add(&plist->list, &dvenc->venc_msg_list_free);
+ }
+ dvenc->q6_handle =
+ dal_attach(DALDEVICEID_VENC_DEVICE, DALDEVICEID_VENC_PORTNAME, 1,
+ venc_q6_callback, (void *)dvenc);
+ if (!(dvenc->q6_handle)) {
+ pr_err("%s: daldevice_attach failed (%d)\n", __func__, ret);
+ goto err_venc_dal_attach;
+ }
+ ret = dal_call_f9(dvenc->q6_handle, DAL_OP_INFO, &version_info,
+ sizeof(struct dal_info));
+ if (ret) {
+ pr_err("%s: failed to get version\n", __func__);
+ goto err_venc_dal_open;
+ }
+ if (venc_check_version(VENC_INTERFACE_VERSION, version_info.version)) {
+ pr_err("%s: driver version mismatch\n", __func__);
+ goto err_venc_dal_open;
+ }
+ ret = dal_call_f0(dvenc->q6_handle, DAL_OP_OPEN, 1);
+ if (ret) {
+ pr_err("%s: dal_call_open failed (%d)\n", __func__, ret);
+ goto err_venc_dal_open;
+ }
+ dvenc->state = VENC_STATE_STOP;
+ dvenc->is_active = 1;
+ prevent_sleep();
+ return ret;
+err_venc_dal_open:
+ dal_detach(dvenc->q6_handle);
+err_venc_dal_attach:
+ list_for_each_entry_safe(plist, tmp, &dvenc->venc_msg_list_free, list) {
+ list_del(&plist->list);
+ kfree(plist);
+ }
+err_venc_create_msg_list:
+ kfree(dvenc);
+ venc_ref--;
+ return ret;
+}
+
+static int q6venc_release(struct inode *inode, struct file *file)
+{
+ int ret = 0;
+ struct venc_msg_list *l, *n;
+ struct venc_pmem_list *plist, *m;
+ struct venc_dev *dvenc;
+ unsigned long flags;
+
+ venc_ref--;
+ dvenc = file->private_data;
+ dvenc->is_active = 0;
+ wake_up_all(&dvenc->venc_msg_evt);
+ dal_call_f0(dvenc->q6_handle, VENC_DALRPC_STOP, 1);
+ dal_call_f0(dvenc->q6_handle, DAL_OP_CLOSE, 1);
+ dal_detach(dvenc->q6_handle);
+ list_for_each_entry_safe(l, n, &dvenc->venc_msg_list_free, list) {
+ list_del(&l->list);
+ kfree(l);
+ }
+ list_for_each_entry_safe(l, n, &dvenc->venc_msg_list_head, list) {
+ list_del(&l->list);
+ kfree(l);
+ }
+ spin_lock_irqsave(&dvenc->venc_pmem_list_lock, flags);
+ if (!dvenc->pmem_freed) {
+ list_for_each_entry(plist, &dvenc->venc_pmem_list_head, list)
+ put_pmem_file(plist->buf.file);
+ dvenc->pmem_freed = 1;
+ }
+ spin_unlock_irqrestore(&dvenc->venc_pmem_list_lock, flags);
+
+ list_for_each_entry_safe(plist, m, &dvenc->venc_pmem_list_head, list) {
+ list_del(&plist->list);
+ kfree(plist);
+ }
+ kfree(dvenc);
+ allow_sleep();
+ return ret;
+}
+
+const struct file_operations q6venc_fops = {
+ .owner = THIS_MODULE,
+ .open = q6venc_open,
+ .release = q6venc_release,
+ .unlocked_ioctl = q6venc_ioctl,
+};
+
+static int __init q6venc_init(void)
+{
+ int ret = 0;
+
+ wake_lock_init(&idlelock, WAKE_LOCK_IDLE, "venc_idle");
+ wake_lock_init(&wakelock, WAKE_LOCK_SUSPEND, "venc_suspend");
+
+ venc_device_p = kzalloc(sizeof(struct venc_dev), GFP_KERNEL);
+ if (!venc_device_p) {
+ pr_err("%s: unable to allocate memory for venc_device_p\n",
+ __func__);
+ return -ENOMEM;
+ }
+ ret = alloc_chrdev_region(&venc_dev_num, 0, 1, VENC_NAME);
+ if (ret < 0) {
+ pr_err("%s: alloc_chrdev_region failed (%d)\n", __func__,
+ ret);
+ return ret;
+ }
+ venc_class = class_create(THIS_MODULE, VENC_NAME);
+ if (IS_ERR(venc_class)) {
+ ret = PTR_ERR(venc_class);
+ pr_err("%s: failed to create venc_class (%d)\n",
+ __func__, ret);
+ goto err_venc_class_create;
+ }
+ venc_device_p->class_devp =
+ device_create(venc_class, NULL, venc_dev_num, NULL,
+ VENC_NAME);
+ if (IS_ERR(venc_device_p->class_devp)) {
+ ret = PTR_ERR(venc_device_p->class_devp);
+ pr_err("%s: failed to create class_device (%d)\n", __func__,
+ ret);
+ goto err_venc_class_device_create;
+ }
+ cdev_init(&cdev, &q6venc_fops);
+ cdev.owner = THIS_MODULE;
+ ret = cdev_add(&cdev, venc_dev_num, 1);
+ if (ret < 0) {
+ pr_err("%s: cdev_add failed (%d)\n", __func__, ret);
+ goto err_venc_cdev_add;
+ }
+ init_waitqueue_head(&venc_device_p->venc_msg_evt);
+ return ret;
+
+err_venc_cdev_add:
+ device_destroy(venc_class, venc_dev_num);
+err_venc_class_device_create:
+ class_destroy(venc_class);
+err_venc_class_create:
+ unregister_chrdev_region(venc_dev_num, 1);
+ return ret;
+}
+
+static void __exit q6venc_exit(void)
+{
+ cdev_del(&(cdev));
+ device_destroy(venc_class, venc_dev_num);
+ class_destroy(venc_class);
+ unregister_chrdev_region(venc_dev_num, 1);
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Video encoder driver for QDSP6");
+MODULE_VERSION("2.0");
+module_init(q6venc_init);
+module_exit(q6venc_exit);
diff --git a/arch/arm/mach-msm/qdsp6/pcm_in.c b/arch/arm/mach-msm/qdsp6/pcm_in.c
new file mode 100644
index 0000000..c6bddb8
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/pcm_in.c
@@ -0,0 +1,263 @@
+/* arch/arm/mach-msm/qdsp6/pcm_in.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Copyright (C) 2009 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.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+
+#include <linux/msm_audio.h>
+
+#include <mach/msm_qdsp6_audio.h>
+#include <mach/debug_mm.h>
+
+struct pcm {
+ struct audio_client *ac;
+ uint32_t sample_rate;
+ uint32_t channel_count;
+ uint32_t buffer_size;
+ uint32_t rec_mode;
+};
+
+#define BUFSZ (256)
+
+void audio_client_dump(struct audio_client *ac);
+
+static long q6_in_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct pcm *pcm = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_SET_VOLUME:
+ pr_debug("[%s:%s] SET_VOLUME\n", __MM_FILE__, __func__);
+ break;
+ case AUDIO_GET_STATS: {
+ struct msm_audio_stats stats;
+ pr_debug("[%s:%s] GET_STATS\n", __MM_FILE__, __func__);
+ memset(&stats, 0, sizeof(stats));
+ if (copy_to_user((void*) arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ }
+ case AUDIO_START: {
+ uint32_t acdb_id;
+ pr_debug("[%s:%s] AUDIO_START\n", __MM_FILE__, __func__);
+ rc = 0;
+
+ if (arg == 0) {
+ acdb_id = 0;
+ } else if (copy_from_user(&acdb_id, (void*) arg, sizeof(acdb_id))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ if (pcm->ac) {
+ pr_err("[%s:%s] active session already existing\n",
+ __MM_FILE__, __func__);
+ rc = -EBUSY;
+ } else {
+ pcm->ac = q6audio_open_pcm(pcm->buffer_size,
+ pcm->sample_rate, pcm->channel_count,
+ pcm->rec_mode, acdb_id);
+ if (!pcm->ac) {
+ pr_err("[%s:%s] pcm open session failed\n",
+ __MM_FILE__, __func__);
+ rc = -ENOMEM;
+ }
+ }
+ break;
+ }
+ case AUDIO_STOP:
+ pr_debug("[%s:%s] AUDIO_STOP\n", __MM_FILE__, __func__);
+ break;
+ case AUDIO_FLUSH:
+ break;
+ case AUDIO_SET_CONFIG: {
+ struct msm_audio_config config;
+ if (copy_from_user(&config, (void*) arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ pr_debug("[%s:%s] SET_CONFIG: samplerate = %d, channels = %d\n",
+ __MM_FILE__, __func__, config.sample_rate,
+ config.channel_count);
+ if (!config.channel_count || config.channel_count > 2) {
+ rc = -EINVAL;
+ pr_err("[%s:%s] invalid channelcount %d\n",
+ __MM_FILE__, __func__, config.channel_count);
+ break;
+ }
+ if (config.sample_rate < 8000 || config.sample_rate > 48000) {
+ rc = -EINVAL;
+ pr_err("[%s:%s] invalid samplerate %d\n", __MM_FILE__,
+ __func__, config.sample_rate);
+ break;
+ }
+ if (config.buffer_size < 128 || config.buffer_size > 8192) {
+ rc = -EINVAL;
+ pr_err("[%s:%s] invalid buffsize %d\n", __MM_FILE__,
+ __func__, config.buffer_size);
+ break;
+ }
+
+ pcm->sample_rate = config.sample_rate;
+ pcm->channel_count = config.channel_count;
+ pcm->buffer_size = config.buffer_size;
+ break;
+ }
+ case AUDIO_SET_INCALL: {
+ struct msm_voicerec_mode voicerec_mode;
+ pr_debug("[%s:%s] SET_INCALL\n", __MM_FILE__, __func__);
+ if (copy_from_user(&voicerec_mode, (void *)arg,
+ sizeof(struct msm_voicerec_mode)))
+ return -EFAULT;
+ if (voicerec_mode.rec_mode != AUDIO_FLAG_READ &&
+ voicerec_mode.rec_mode != AUDIO_FLAG_INCALL_MIXED) {
+ pcm->rec_mode = AUDIO_FLAG_READ;
+ pr_err("[%s:%s] invalid rec_mode\n", __MM_FILE__,
+ __func__);
+ rc = -EINVAL;
+ } else
+ pcm->rec_mode = voicerec_mode.rec_mode;
+ break;
+ }
+ case AUDIO_GET_CONFIG: {
+ struct msm_audio_config config;
+ config.buffer_size = pcm->buffer_size;
+ config.buffer_count = 2;
+ config.sample_rate = pcm->sample_rate;
+ config.channel_count = pcm->channel_count;
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+ config.unused[2] = 0;
+ if (copy_to_user((void*) arg, &config, sizeof(config))) {
+ rc = -EFAULT;
+ }
+ pr_debug("[%s:%s] GET_CONFIG: samplerate = %d, channels = %d\n",
+ __MM_FILE__, __func__, config.sample_rate,
+ config.channel_count);
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ }
+ pr_debug("[%s:%s] rc = %d\n", __MM_FILE__, __func__, rc);
+ return rc;
+}
+
+static int q6_in_open(struct inode *inode, struct file *file)
+{
+ struct pcm *pcm;
+
+ pr_info("[%s:%s] open\n", __MM_FILE__, __func__);
+ pcm = kzalloc(sizeof(struct pcm), GFP_KERNEL);
+
+ if (!pcm)
+ return -ENOMEM;
+
+ pcm->channel_count = 1;
+ pcm->sample_rate = 8000;
+ pcm->buffer_size = BUFSZ;
+ pcm->rec_mode = AUDIO_FLAG_READ;
+ file->private_data = pcm;
+ return 0;
+}
+
+static ssize_t q6_in_read(struct file *file, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct pcm *pcm = file->private_data;
+ struct audio_client *ac;
+ struct audio_buffer *ab;
+ const char __user *start = buf;
+ int xfer;
+ int res;
+
+ pr_debug("[%s:%s] count = %d\n", __MM_FILE__, __func__, count);
+ ac = pcm->ac;
+ if (!ac) {
+ res = -ENODEV;
+ goto fail;
+ }
+ while (count > 0) {
+ ab = ac->buf + ac->cpu_buf;
+
+ if (ab->used)
+ if (!wait_event_timeout(ac->wait, (ab->used == 0), 5*HZ)) {
+ audio_client_dump(ac);
+ pr_err("[%s:%s] timeout. dsp dead?\n",
+ __MM_FILE__, __func__);
+ q6audio_dsp_not_responding();
+ }
+ pr_debug("[%s:%s] ab->data = %p, cpu_buf = %d", __MM_FILE__,
+ __func__, ab->data, ac->cpu_buf);
+ xfer = count;
+ if (xfer > ab->size)
+ xfer = ab->size;
+
+ if (copy_to_user(buf, ab->data, xfer)) {
+ res = -EFAULT;
+ goto fail;
+ }
+
+ buf += xfer;
+ count -= xfer;
+
+ ab->used = 1;
+ q6audio_read(ac, ab);
+ ac->cpu_buf ^= 1;
+ }
+fail:
+ res = buf - start;
+ return res;
+}
+
+static int q6_in_release(struct inode *inode, struct file *file)
+{
+
+ int rc = 0;
+ struct pcm *pcm = file->private_data;
+ if (pcm->ac)
+ rc = q6audio_close(pcm->ac);
+ kfree(pcm);
+ pr_info("[%s:%s] release\n", __MM_FILE__, __func__);
+ return rc;
+}
+
+static struct file_operations q6_in_fops = {
+ .owner = THIS_MODULE,
+ .open = q6_in_open,
+ .read = q6_in_read,
+ .release = q6_in_release,
+ .unlocked_ioctl = q6_in_ioctl,
+};
+
+struct miscdevice q6_in_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_pcm_in",
+ .fops = &q6_in_fops,
+};
+
+static int __init q6_in_init(void) {
+ return misc_register(&q6_in_misc);
+}
+
+device_initcall(q6_in_init);
diff --git a/arch/arm/mach-msm/qdsp6/pcm_out.c b/arch/arm/mach-msm/qdsp6/pcm_out.c
new file mode 100644
index 0000000..2e91cb2
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/pcm_out.c
@@ -0,0 +1,276 @@
+/* arch/arm/mach-msm/qdsp6/pcm_out.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Author: Brian Swetland <swetland@google.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/slab.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+
+#include <linux/msm_audio.h>
+
+#include <mach/msm_qdsp6_audio.h>
+#include <mach/debug_mm.h>
+
+void audio_client_dump(struct audio_client *ac);
+
+#define BUFSZ (3072)
+
+struct pcm {
+ struct mutex lock;
+ struct audio_client *ac;
+ uint32_t sample_rate;
+ uint32_t channel_count;
+ size_t buffer_size;
+};
+
+static long pcm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct pcm *pcm = file->private_data;
+ int rc = 0;
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ memset(&stats, 0, sizeof(stats));
+ if (copy_to_user((void*) arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ }
+
+ mutex_lock(&pcm->lock);
+ switch (cmd) {
+ case AUDIO_SET_VOLUME: {
+ int vol;
+ if (!pcm->ac) {
+ pr_err("%s: cannot set volume before AUDIO_START!\n",
+ __func__);
+ rc = -EINVAL;
+ break;
+ }
+ if (copy_from_user(&vol, (void*) arg, sizeof(vol))) {
+ rc = -EFAULT;
+ break;
+ }
+ pr_debug("[%s:%s] SET_VOLUME: vol = %d\n", __MM_FILE__,
+ __func__, vol);
+ rc = q6audio_set_stream_volume(pcm->ac, vol);
+ break;
+ }
+ case AUDIO_START: {
+ uint32_t acdb_id;
+ pr_debug("[%s:%s] AUDIO_START\n", __MM_FILE__, __func__);
+ if (arg == 0) {
+ acdb_id = 0;
+ } else if (copy_from_user(&acdb_id, (void*) arg, sizeof(acdb_id))) {
+ pr_info("[%s:%s] copy acdb_id from user failed\n",
+ __MM_FILE__, __func__);
+ rc = -EFAULT;
+ break;
+ }
+ if (pcm->ac) {
+ pr_err("[%s:%s] active session already existing\n",
+ __MM_FILE__, __func__);
+ rc = -EBUSY;
+ } else {
+ pcm->ac = q6audio_open_pcm(pcm->buffer_size,
+ pcm->sample_rate,
+ pcm->channel_count,
+ AUDIO_FLAG_WRITE, acdb_id);
+ if (!pcm->ac) {
+ pr_err("[%s:%s] pcm open session failed\n",
+ __MM_FILE__, __func__);
+ rc = -ENOMEM;
+ }
+ }
+ break;
+ }
+ case AUDIO_STOP:
+ pr_debug("[%s:%s] AUDIO_STOP\n", __MM_FILE__, __func__);
+ break;
+ case AUDIO_FLUSH:
+ break;
+ case AUDIO_SET_CONFIG: {
+ struct msm_audio_config config;
+ if (pcm->ac) {
+ rc = -EBUSY;
+ pr_err("[%s:%s] active session already existing\n",
+ __MM_FILE__, __func__);
+ break;
+ }
+ if (copy_from_user(&config, (void*) arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ pr_debug("[%s:%s] SET_CONFIG: samplerate = %d, channels = %d\n",
+ __MM_FILE__, __func__, config.sample_rate,
+ config.channel_count);
+ if (config.channel_count < 1 || config.channel_count > 2) {
+ rc = -EINVAL;
+ pr_err("[%s:%s] invalid channelcount %d\n",
+ __MM_FILE__, __func__, config.channel_count);
+ break;
+ }
+ if (config.sample_rate < 8000 || config.sample_rate > 48000) {
+ rc = -EINVAL;
+ pr_err("[%s:%s] invalid samplerate %d\n", __MM_FILE__,
+ __func__, config.sample_rate);
+ break;
+ }
+ if (config.buffer_size < 128 || config.buffer_size > 8192) {
+ rc = -EINVAL;
+ pr_err("[%s:%s] invalid buffsize %d\n", __MM_FILE__,
+ __func__, config.buffer_size);
+ break;
+ }
+ pcm->sample_rate = config.sample_rate;
+ pcm->channel_count = config.channel_count;
+ pcm->buffer_size = config.buffer_size;
+ break;
+ }
+ case AUDIO_GET_CONFIG: {
+ struct msm_audio_config config;
+ config.buffer_size = pcm->buffer_size;
+ config.buffer_count = 2;
+ config.sample_rate = pcm->sample_rate;
+ config.channel_count = pcm->channel_count;
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+ config.unused[2] = 0;
+ if (copy_to_user((void*) arg, &config, sizeof(config))) {
+ rc = -EFAULT;
+ }
+ pr_debug("[%s:%s] GET_CONFIG: samplerate = %d, channels = %d\n",
+ __MM_FILE__, __func__, config.sample_rate,
+ config.channel_count);
+ break;
+ }
+ case AUDIO_SET_EQ: {
+ struct msm_audio_eq_stream_config eq_config;
+ pr_debug("[%s:%s] SET_EQ\n", __MM_FILE__, __func__);
+ if (copy_from_user(&eq_config, (void *) arg,
+ sizeof(eq_config))) {
+ rc = -EFAULT;
+ break;
+ }
+ rc = q6audio_set_stream_eq_pcm(pcm->ac, (void *) &eq_config);
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&pcm->lock);
+ pr_debug("[%s:%s] rc = %d\n", __MM_FILE__, __func__, rc);
+ return rc;
+}
+
+static int pcm_open(struct inode *inode, struct file *file)
+{
+ struct pcm *pcm;
+
+ pr_info("[%s:%s] open\n", __MM_FILE__, __func__);
+ pcm = kzalloc(sizeof(struct pcm), GFP_KERNEL);
+
+ if (!pcm)
+ return -ENOMEM;
+
+ mutex_init(&pcm->lock);
+ pcm->channel_count = 2;
+ pcm->sample_rate = 44100;
+ pcm->buffer_size = BUFSZ;
+ file->private_data = pcm;
+ return 0;
+}
+
+static ssize_t pcm_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct pcm *pcm = file->private_data;
+ struct audio_client *ac;
+ struct audio_buffer *ab;
+ const char __user *start = buf;
+ int xfer;
+
+ pr_debug("[%s:%s] count = %d\n", __MM_FILE__, __func__, count);
+ if (!pcm->ac)
+ pcm_ioctl(file, AUDIO_START, 0);
+
+ ac = pcm->ac;
+ if (!ac)
+ return -ENODEV;
+
+ while (count > 0) {
+ ab = ac->buf + ac->cpu_buf;
+
+ if (ab->used)
+ if (!wait_event_timeout(ac->wait, (ab->used == 0), 5*HZ)) {
+ audio_client_dump(ac);
+ pr_err("[%s:%s] timeout. dsp dead?\n",
+ __MM_FILE__, __func__);
+ q6audio_dsp_not_responding();
+ }
+ pr_debug("[%s:%s] ab->data = %p, cpu_buf = %d", __MM_FILE__,
+ __func__, ab->data, ac->cpu_buf);
+ xfer = count;
+ if (xfer > ab->size)
+ xfer = ab->size;
+
+ if (copy_from_user(ab->data, buf, xfer))
+ return -EFAULT;
+
+ buf += xfer;
+ count -= xfer;
+
+ ab->used = 1;
+ ab->actual_size = xfer;
+ q6audio_write(ac, ab);
+ ac->cpu_buf ^= 1;
+ }
+
+ return buf - start;
+}
+
+static int pcm_release(struct inode *inode, struct file *file)
+{
+ struct pcm *pcm = file->private_data;
+ if (pcm->ac)
+ q6audio_close(pcm->ac);
+ kfree(pcm);
+ pr_info("[%s:%s] release\n", __MM_FILE__, __func__);
+ return 0;
+}
+
+static struct file_operations pcm_fops = {
+ .owner = THIS_MODULE,
+ .open = pcm_open,
+ .write = pcm_write,
+ .release = pcm_release,
+ .unlocked_ioctl = pcm_ioctl,
+};
+
+struct miscdevice pcm_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_pcm_out",
+ .fops = &pcm_fops,
+};
+
+static int __init pcm_init(void) {
+ return misc_register(&pcm_misc);
+}
+
+device_initcall(pcm_init);
diff --git a/arch/arm/mach-msm/qdsp6/q6audio.c b/arch/arm/mach-msm/qdsp6/q6audio.c
new file mode 100644
index 0000000..bf6f115
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/q6audio.c
@@ -0,0 +1,2155 @@
+/*
+ * Copyright (C) 2009 Google, Inc.
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ * Author: Brian Swetland <swetland@google.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/slab.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+
+#include <linux/delay.h>
+#include <linux/wakelock.h>
+#include <linux/android_pmem.h>
+#include <linux/firmware.h>
+#include <linux/miscdevice.h>
+
+#include "dal.h"
+#include "dal_audio.h"
+#include "dal_audio_format.h"
+#include "dal_acdb.h"
+#include "dal_adie.h"
+#include <mach/msm_qdsp6_audio.h>
+
+#include <linux/msm_audio_aac.h>
+
+#include <linux/gpio.h>
+
+#include "q6audio_devices.h"
+#include <mach/debug_mm.h>
+
+
+struct q6_hw_info {
+ int min_gain;
+ int max_gain;
+};
+
+/* TODO: provide mechanism to configure from board file */
+
+static struct q6_hw_info q6_audio_hw[Q6_HW_COUNT] = {
+ [Q6_HW_HANDSET] = {
+ .min_gain = -400,
+ .max_gain = 1100,
+ },
+ [Q6_HW_HEADSET] = {
+ .min_gain = -1100,
+ .max_gain = 400,
+ },
+ [Q6_HW_SPEAKER] = {
+ .min_gain = -1000,
+ .max_gain = 500,
+ },
+ [Q6_HW_TTY] = {
+ .min_gain = 0,
+ .max_gain = 0,
+ },
+ [Q6_HW_BT_SCO] = {
+ .min_gain = -1100,
+ .max_gain = 400,
+ },
+ [Q6_HW_BT_A2DP] = {
+ .min_gain = -1100,
+ .max_gain = 400,
+ },
+};
+
+static struct wake_lock wakelock;
+static struct wake_lock idlelock;
+static int idlecount;
+static DEFINE_MUTEX(idlecount_lock);
+
+void audio_prevent_sleep(void)
+{
+ mutex_lock(&idlecount_lock);
+ if (++idlecount == 1) {
+ wake_lock(&wakelock);
+ wake_lock(&idlelock);
+ }
+ mutex_unlock(&idlecount_lock);
+}
+
+void audio_allow_sleep(void)
+{
+ mutex_lock(&idlecount_lock);
+ if (--idlecount == 0) {
+ wake_unlock(&idlelock);
+ wake_unlock(&wakelock);
+ }
+ mutex_unlock(&idlecount_lock);
+}
+
+static struct clk *icodec_rx_clk;
+static struct clk *icodec_tx_clk;
+static struct clk *ecodec_clk;
+static struct clk *sdac_clk;
+
+static struct q6audio_analog_ops default_analog_ops;
+static struct q6audio_analog_ops *analog_ops = &default_analog_ops;
+static uint32_t tx_clk_freq = 8000;
+static int tx_mute_status = 0;
+static int rx_vol_level = 100;
+static uint32_t tx_acdb = 0;
+static uint32_t rx_acdb = 0;
+
+void q6audio_register_analog_ops(struct q6audio_analog_ops *ops)
+{
+ analog_ops = ops;
+}
+
+static struct q6_device_info *q6_lookup_device(uint32_t device_id,
+ uint32_t acdb_id)
+{
+ struct q6_device_info *di = q6_audio_devices;
+
+ pr_debug("[%s:%s] device_id = 0x%x, acdb_id = %d\n", __MM_FILE__,
+ __func__, device_id, acdb_id);
+ if (acdb_id) {
+ for (;;) {
+ if (di->cad_id == acdb_id && di->id == device_id)
+ return di;
+ if (di->id == 0) {
+ pr_err("[%s:%s] bogus id 0x%08x\n",
+ __MM_FILE__, __func__, device_id);
+ return di;
+ }
+ di++;
+ }
+ } else {
+ for (;;) {
+ if (di->id == device_id)
+ return di;
+ if (di->id == 0) {
+ pr_err("[%s:%s] bogus id 0x%08x\n",
+ __MM_FILE__, __func__, device_id);
+ return di;
+ }
+ di++;
+ }
+ }
+}
+
+static uint32_t q6_device_to_codec(uint32_t device_id)
+{
+ struct q6_device_info *di = q6_lookup_device(device_id, 0);
+ return di->codec;
+}
+
+static uint32_t q6_device_to_dir(uint32_t device_id)
+{
+ struct q6_device_info *di = q6_lookup_device(device_id, 0);
+ return di->dir;
+}
+
+static uint32_t q6_device_to_cad_id(uint32_t device_id)
+{
+ struct q6_device_info *di = q6_lookup_device(device_id, 0);
+ return di->cad_id;
+}
+
+static uint32_t q6_device_to_path(uint32_t device_id, uint32_t acdb_id)
+{
+ struct q6_device_info *di = q6_lookup_device(device_id, acdb_id);
+ return di->path;
+}
+
+static uint32_t q6_device_to_rate(uint32_t device_id)
+{
+ struct q6_device_info *di = q6_lookup_device(device_id, 0);
+ return di->rate;
+}
+
+int q6_device_volume(uint32_t device_id, int level)
+{
+ struct q6_device_info *di = q6_lookup_device(device_id, 0);
+ struct q6_hw_info *hw;
+
+ hw = &q6_audio_hw[di->hw];
+
+ return hw->min_gain + ((hw->max_gain - hw->min_gain) * level) / 100;
+}
+
+static inline int adie_open(struct dal_client *client)
+{
+ pr_debug("[%s:%s]\n", __MM_FILE__, __func__);
+ return dal_call_f0(client, DAL_OP_OPEN, 0);
+}
+
+static inline int adie_close(struct dal_client *client)
+{
+ pr_debug("[%s:%s]\n", __MM_FILE__, __func__);
+ return dal_call_f0(client, DAL_OP_CLOSE, 0);
+}
+
+static inline int adie_set_path(struct dal_client *client,
+ uint32_t id, uint32_t path_type)
+{
+ pr_debug("[%s:%s] id = 0x%x, path_type = %d\n", __MM_FILE__,
+ __func__, id, path_type);
+ return dal_call_f1(client, ADIE_OP_SET_PATH, id, path_type);
+}
+
+static inline int adie_set_path_freq_plan(struct dal_client *client,
+ uint32_t path_type, uint32_t plan)
+{
+ pr_debug("[%s:%s] path_type = %d, plan = %d\n", __MM_FILE__,
+ __func__, path_type, plan);
+ return dal_call_f1(client, ADIE_OP_SET_PATH_FREQUENCY_PLAN,
+ path_type, plan);
+}
+
+static inline int adie_proceed_to_stage(struct dal_client *client,
+ uint32_t path_type, uint32_t stage)
+{
+ pr_debug("[%s:%s] path_type = %d, stage = 0x%x\n", __MM_FILE__,
+ __func__, path_type, stage);
+ return dal_call_f1(client, ADIE_OP_PROCEED_TO_STAGE,
+ path_type, stage);
+}
+
+static inline int adie_mute_path(struct dal_client *client,
+ uint32_t path_type, uint32_t mute_state)
+{
+ pr_debug("[%s:%s] path_type = %d, mute = %d\n", __MM_FILE__, __func__,
+ path_type, mute_state);
+ return dal_call_f1(client, ADIE_OP_MUTE_PATH, path_type, mute_state);
+}
+
+static int adie_refcount;
+
+static struct dal_client *adie;
+static struct dal_client *adsp;
+static struct dal_client *acdb;
+
+static int adie_enable(void)
+{
+ adie_refcount++;
+ if (adie_refcount == 1)
+ adie_open(adie);
+ return 0;
+}
+
+static int adie_disable(void)
+{
+ adie_refcount--;
+ if (adie_refcount == 0)
+ adie_close(adie);
+ return 0;
+}
+
+/* 4k PMEM used for exchanging acdb device config tables
+ * and stream format descriptions with the DSP.
+ */
+static char *audio_data;
+static int32_t audio_phys;
+
+#define SESSION_MIN 0
+#define SESSION_MAX 64
+
+static DEFINE_MUTEX(session_lock);
+static DEFINE_MUTEX(audio_lock);
+
+static struct audio_client *session[SESSION_MAX];
+
+static int session_alloc(struct audio_client *ac)
+{
+ int n;
+
+ mutex_lock(&session_lock);
+ for (n = SESSION_MIN; n < SESSION_MAX; n++) {
+ if (!session[n]) {
+ session[n] = ac;
+ mutex_unlock(&session_lock);
+ pr_debug("[%s:%s] session = %d\n", __MM_FILE__,
+ __func__, n);
+ return n;
+ }
+ }
+ mutex_unlock(&session_lock);
+ return -ENOMEM;
+}
+
+static void session_free(int n, struct audio_client *ac)
+{
+ mutex_lock(&session_lock);
+ if (session[n] == ac) {
+ session[n] = 0;
+ pr_debug("[%s:%s] session = %d\n", __MM_FILE__, __func__, n);
+ }
+ mutex_unlock(&session_lock);
+}
+
+static void audio_client_free(struct audio_client *ac)
+{
+ pr_debug("[%s:%s] ac = %p\n", __MM_FILE__, __func__, ac);
+ session_free(ac->session, ac);
+
+ if (ac->buf[0].data) {
+ iounmap(ac->buf[0].data);
+ pmem_kfree(ac->buf[0].phys);
+ }
+ if (ac->buf[1].data) {
+ iounmap(ac->buf[1].data);
+ pmem_kfree(ac->buf[1].phys);
+ }
+ kfree(ac);
+}
+
+static struct audio_client *audio_client_alloc(unsigned bufsz)
+{
+ struct audio_client *ac;
+ int n;
+
+ pr_debug("[%s:%s] bufsz = %d\n", __MM_FILE__, __func__, bufsz);
+ ac = kzalloc(sizeof(*ac), GFP_KERNEL);
+ if (!ac)
+ return 0;
+
+ n = session_alloc(ac);
+ if (n < 0)
+ goto fail_session;
+ ac->session = n;
+
+ if (bufsz > 0) {
+ ac->buf[0].phys = pmem_kalloc(bufsz,
+ PMEM_MEMTYPE_EBI1|PMEM_ALIGNMENT_4K);
+ ac->buf[0].data = ioremap(ac->buf[0].phys, bufsz);
+ if (!ac->buf[0].data)
+ goto fail;
+ ac->buf[1].phys = pmem_kalloc(bufsz,
+ PMEM_MEMTYPE_EBI1|PMEM_ALIGNMENT_4K);
+ ac->buf[1].data = ioremap(ac->buf[1].phys, bufsz);
+ if (!ac->buf[1].data)
+ goto fail;
+
+ ac->buf[0].size = bufsz;
+ ac->buf[1].size = bufsz;
+ }
+
+ init_waitqueue_head(&ac->wait);
+ ac->client = adsp;
+
+ return ac;
+
+fail:
+ session_free(n, ac);
+fail_session:
+ audio_client_free(ac);
+ return 0;
+}
+
+void audio_client_dump(struct audio_client *ac)
+{
+ dal_trace_dump(ac->client);
+}
+
+static int audio_ioctl(struct audio_client *ac, void *ptr, uint32_t len)
+{
+ struct adsp_command_hdr *hdr = ptr;
+ uint32_t tmp;
+ int r;
+
+ hdr->size = len - sizeof(u32);
+ hdr->dst = AUDIO_ADDR(ac->session, 0, AUDIO_DOMAIN_DSP);
+ hdr->src = AUDIO_ADDR(ac->session, 0, AUDIO_DOMAIN_APP);
+ hdr->context = ac->session;
+ ac->cb_status = -EBUSY;
+ r = dal_call(ac->client, AUDIO_OP_CONTROL, 5, ptr, len, &tmp, sizeof(tmp));
+ if (r != 4)
+ return -EIO;
+ if (!wait_event_timeout(ac->wait, (ac->cb_status != -EBUSY), 5*HZ)) {
+ dal_trace_dump(ac->client);
+ pr_err("[%s:%s] timeout. dsp dead?\n", __MM_FILE__, __func__);
+ q6audio_dsp_not_responding();
+ }
+ return ac->cb_status;
+}
+
+static int audio_command(struct audio_client *ac, uint32_t cmd)
+{
+ struct adsp_command_hdr rpc;
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.opcode = cmd;
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+static int audio_open_control(struct audio_client *ac)
+{
+ struct adsp_open_command rpc;
+
+ pr_debug("[%s:%s] ac = %p\n", __MM_FILE__, __func__, ac);
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_DEVICE;
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+static int audio_out_open(struct audio_client *ac, uint32_t bufsz,
+ uint32_t rate, uint32_t channels)
+{
+ struct adsp_open_command rpc;
+
+ memset(&rpc, 0, sizeof(rpc));
+
+ rpc.format.standard.format = ADSP_AUDIO_FORMAT_PCM;
+ rpc.format.standard.channels = channels;
+ rpc.format.standard.bits_per_sample = 16;
+ rpc.format.standard.sampling_rate = rate;
+ rpc.format.standard.is_signed = 1;
+ rpc.format.standard.is_interleaved = 1;
+
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_WRITE;
+ rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT;
+ rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_PLAYBACK;
+ rpc.buf_max_size = bufsz;
+
+ pr_debug("[%s:%s]ac = %p\n", __MM_FILE__, __func__, ac);
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+static int audio_in_open(struct audio_client *ac, uint32_t bufsz,
+ uint32_t flags, uint32_t rate, uint32_t channels)
+{
+ struct adsp_open_command rpc;
+
+ memset(&rpc, 0, sizeof(rpc));
+
+ rpc.format.standard.format = ADSP_AUDIO_FORMAT_PCM;
+ rpc.format.standard.channels = channels;
+ rpc.format.standard.bits_per_sample = 16;
+ rpc.format.standard.sampling_rate = rate;
+ rpc.format.standard.is_signed = 1;
+ rpc.format.standard.is_interleaved = 1;
+
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_READ;
+ rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT;
+ if (flags == AUDIO_FLAG_READ)
+ rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD;
+ else
+ rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_MIXED_RECORD;
+
+ rpc.buf_max_size = bufsz;
+
+ pr_debug("[%s:%s] ac = %p\n", __MM_FILE__, __func__, ac);
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+static int audio_auxpcm_out_open(struct audio_client *ac,
+ uint32_t rate, uint32_t channels)
+{
+ struct adsp_open_command rpc;
+
+ memset(&rpc, 0, sizeof(rpc));
+
+ rpc.format.standard.format = ADSP_AUDIO_FORMAT_PCM;
+ rpc.format.standard.channels = channels;
+ rpc.format.standard.bits_per_sample = 16;
+ rpc.format.standard.sampling_rate = rate;
+ rpc.format.standard.is_signed = 1;
+ rpc.format.standard.is_interleaved = 1;
+
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_READ;
+ rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT;
+ rpc.mode = ADSP_AUDIO_OPEN_STREAM_MODE_AUX_PCM;
+ rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD;
+
+ pr_debug("[%s:%s] ac = %p\n", __MM_FILE__, __func__, ac);
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+static int audio_auxpcm_in_open(struct audio_client *ac, uint32_t rate,
+ uint32_t channels)
+{
+ struct adsp_open_command rpc;
+
+ memset(&rpc, 0, sizeof(rpc));
+
+ rpc.format.standard.format = ADSP_AUDIO_FORMAT_PCM;
+ rpc.format.standard.channels = channels;
+ rpc.format.standard.bits_per_sample = 16;
+ rpc.format.standard.sampling_rate = rate;
+ rpc.format.standard.is_signed = 1;
+ rpc.format.standard.is_interleaved = 1;
+
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_WRITE;
+ rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT;
+ rpc.mode = ADSP_AUDIO_OPEN_STREAM_MODE_AUX_PCM;
+ rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_PLAYBACK;
+
+ pr_debug("[%s:%s] ac = %p\n", __MM_FILE__, __func__, ac);
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+static int audio_mp3_open(struct audio_client *ac, uint32_t bufsz,
+ uint32_t rate, uint32_t channels)
+{
+ struct adsp_open_command rpc;
+
+ memset(&rpc, 0, sizeof(rpc));
+
+ rpc.format.standard.format = ADSP_AUDIO_FORMAT_MP3;
+ rpc.format.standard.channels = channels;
+ rpc.format.standard.bits_per_sample = 16;
+ rpc.format.standard.sampling_rate = rate;
+ rpc.format.standard.is_signed = 1;
+ rpc.format.standard.is_interleaved = 0;
+
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_WRITE;
+ rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT;
+ rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_PLAYBACK;
+ rpc.buf_max_size = bufsz;
+
+ pr_debug("[%s:%s] ac = %p\n", __MM_FILE__, __func__, ac);
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+static int audio_dtmf_open(struct audio_client *ac,
+ uint32_t rate, uint32_t channels)
+{
+ struct adsp_open_command rpc;
+
+ memset(&rpc, 0, sizeof(rpc));
+
+ rpc.format.standard.format = ADSP_AUDIO_FORMAT_DTMF;
+ rpc.format.standard.channels = channels;
+ rpc.format.standard.bits_per_sample = 16;
+ rpc.format.standard.sampling_rate = rate;
+ rpc.format.standard.is_signed = 1;
+ rpc.format.standard.is_interleaved = 0;
+
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_WRITE;
+ rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT;
+ rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_PLAYBACK;
+
+ pr_debug("[%s:%s] ac = %p\n", __MM_FILE__, __func__, ac);
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+static int audio_aac_open(struct audio_client *ac, uint32_t bufsz,
+ uint32_t sample_rate, uint32_t channels,
+ uint32_t bit_rate, uint32_t flags,
+ uint32_t stream_format)
+{
+ struct adsp_open_command rpc;
+ int audio_object_type;
+ int index = sizeof(u32);
+ u32 *aac_type = NULL;
+
+ memset(&rpc, 0, sizeof(rpc));
+
+ rpc.format.binary.format = ADSP_AUDIO_FORMAT_MPEG4_AAC;
+ /* only 48k sample rate is supported */
+ sample_rate = 3;
+ /* AAC OBJECT LC */
+ audio_object_type = 2;
+
+ aac_type = (u32 *)rpc.format.binary.data;
+ switch (stream_format) {
+ case AUDIO_AAC_FORMAT_ADTS:
+ /* AAC Encoder expect MPEG4_ADTS media type */
+ *aac_type = ADSP_AUDIO_AAC_MPEG4_ADTS;
+ break;
+ case AUDIO_AAC_FORMAT_RAW:
+ /* for ADIF recording */
+ *aac_type = ADSP_AUDIO_AAC_RAW;
+ break;
+ }
+
+ rpc.format.binary.data[index++] = (u8)(
+ ((audio_object_type & 0x1F) << 3) |
+ ((sample_rate >> 1) & 0x7));
+ rpc.format.binary.data[index] = (u8)(
+ ((sample_rate & 0x1) << 7) |
+ ((channels & 0x7) << 3));
+ rpc.format.binary.num_bytes = index + 1;
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_READ;
+ rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT;
+
+ if (flags == AUDIO_FLAG_READ)
+ rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD;
+ else
+ rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_MIXED_RECORD;
+
+ rpc.buf_max_size = bufsz;
+ rpc.config.aac.bit_rate = bit_rate;
+ rpc.config.aac.encoder_mode = ADSP_AUDIO_ENC_AAC_LC_ONLY_MODE;
+ pr_debug("[%s:%s] ac = %p\n", __MM_FILE__, __func__, ac);
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+static int audio_qcp_open(struct audio_client *ac, uint32_t bufsz,
+ uint32_t min_rate, uint32_t max_rate,
+ uint32_t flags, uint32_t format)
+{
+ struct adsp_open_command rpc;
+
+ memset(&rpc, 0, sizeof(rpc));
+
+ rpc.format.standard.format = format;
+ rpc.format.standard.channels = 1;
+ rpc.format.standard.bits_per_sample = 16;
+ rpc.format.standard.sampling_rate = 8000;
+ rpc.format.standard.is_signed = 1;
+ rpc.format.standard.is_interleaved = 0;
+
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_READ;
+ rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT;
+
+ if (flags == AUDIO_FLAG_READ)
+ rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD;
+ else
+ rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_MIXED_RECORD;
+ rpc.buf_max_size = bufsz;
+ rpc.config.evrc.min_rate = min_rate;
+ rpc.config.evrc.max_rate = max_rate;
+
+ pr_debug("[%s:%s] ac = %p\n", __MM_FILE__, __func__, ac);
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+static int audio_amrnb_open(struct audio_client *ac, uint32_t bufsz,
+ uint32_t enc_mode, uint32_t flags,
+ uint32_t dtx_enable)
+{
+ struct adsp_open_command rpc;
+
+ memset(&rpc, 0, sizeof(rpc));
+
+ rpc.format.standard.format = ADSP_AUDIO_FORMAT_AMRNB_FS;
+ rpc.format.standard.channels = 1;
+ rpc.format.standard.bits_per_sample = 16;
+ rpc.format.standard.sampling_rate = 8000;
+ rpc.format.standard.is_signed = 1;
+ rpc.format.standard.is_interleaved = 0;
+
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_READ;
+ rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT;
+
+ if (flags == AUDIO_FLAG_READ)
+ rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD;
+ else
+ rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_MIXED_RECORD;
+
+ rpc.buf_max_size = bufsz;
+ rpc.config.amr.mode = enc_mode;
+ rpc.config.amr.dtx_mode = dtx_enable;
+ rpc.config.amr.enable = 1;
+
+ pr_debug("[%s:%s] ac = %p\n", __MM_FILE__, __func__, ac);
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+
+
+static int audio_close(struct audio_client *ac)
+{
+ pr_debug("[%s:%s] ac = %p\n", __MM_FILE__, __func__, ac);
+ audio_command(ac, ADSP_AUDIO_IOCTL_CMD_STREAM_STOP);
+ audio_command(ac, ADSP_AUDIO_IOCTL_CMD_CLOSE);
+ return 0;
+}
+
+static int audio_set_table(struct audio_client *ac,
+ uint32_t device_id, int size)
+{
+ struct adsp_set_dev_cfg_table_command rpc;
+
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_SET_DEVICE_CONFIG_TABLE;
+ if (q6_device_to_dir(device_id) == Q6_TX) {
+ if (tx_clk_freq > 16000)
+ rpc.hdr.data = 48000;
+ else if (tx_clk_freq > 8000)
+ rpc.hdr.data = 16000;
+ else
+ rpc.hdr.data = 8000;
+ }
+ rpc.device_id = device_id;
+ rpc.phys_addr = audio_phys;
+ rpc.phys_size = size;
+ rpc.phys_used = size;
+
+ pr_debug("[%s:%s] ac = %p, device_id = 0x%x, size = %d\n", __MM_FILE__,
+ __func__, ac, device_id, size);
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+int q6audio_read(struct audio_client *ac, struct audio_buffer *ab)
+{
+ struct adsp_buffer_command rpc;
+ uint32_t res;
+ int r;
+
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.hdr.size = sizeof(rpc) - sizeof(u32);
+ rpc.hdr.dst = AUDIO_ADDR(ac->session, 0, AUDIO_DOMAIN_DSP);
+ rpc.hdr.src = AUDIO_ADDR(ac->session, 0, AUDIO_DOMAIN_APP);
+ rpc.hdr.context = ac->session;
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_DATA_TX;
+ rpc.buffer.addr = ab->phys;
+ rpc.buffer.max_size = ab->size;
+ rpc.buffer.actual_size = ab->actual_size;
+
+ pr_debug("[%s:%s] ac = %p\n", __MM_FILE__, __func__, ac);
+ r = dal_call(ac->client, AUDIO_OP_DATA, 5, &rpc, sizeof(rpc),
+ &res, sizeof(res));
+ return 0;
+}
+
+int q6audio_write(struct audio_client *ac, struct audio_buffer *ab)
+{
+ struct adsp_buffer_command rpc;
+ uint32_t res;
+ int r;
+
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.hdr.size = sizeof(rpc) - sizeof(u32);
+ rpc.hdr.dst = AUDIO_ADDR(ac->session, 0, AUDIO_DOMAIN_DSP);
+ rpc.hdr.src = AUDIO_ADDR(ac->session, 0, AUDIO_DOMAIN_APP);
+ rpc.hdr.context = ac->session;
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_DATA_RX;
+ rpc.buffer.addr = ab->phys;
+ rpc.buffer.max_size = ab->size;
+ rpc.buffer.actual_size = ab->actual_size;
+
+ pr_debug("[%s:%s] ac = %p\n", __MM_FILE__, __func__, ac);
+ r = dal_call(ac->client, AUDIO_OP_DATA, 5, &rpc, sizeof(rpc),
+ &res, sizeof(res));
+ return 0;
+}
+
+static int audio_rx_volume(struct audio_client *ac, uint32_t dev_id, int32_t volume)
+{
+ struct adsp_set_dev_volume_command rpc;
+
+ pr_debug("[%s:%s] volume = %d\n", __MM_FILE__, __func__, volume);
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_VOL;
+ rpc.device_id = dev_id;
+ rpc.path = ADSP_PATH_RX;
+ rpc.volume = volume;
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+static int audio_rx_mute(struct audio_client *ac, uint32_t dev_id, int mute)
+{
+ struct adsp_set_dev_mute_command rpc;
+
+ pr_debug("[%s:%s] mute = %d, dev_id = 0x%x\n", __MM_FILE__,
+ __func__, mute, dev_id);
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_MUTE;
+ rpc.device_id = dev_id;
+ rpc.path = ADSP_PATH_RX;
+ rpc.mute = !!mute;
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+static int audio_tx_mute(struct audio_client *ac, uint32_t dev_id, int mute)
+{
+ struct adsp_set_dev_mute_command rpc;
+
+ pr_debug("[%s:%s] mute = %d\n", __MM_FILE__, __func__, mute);
+ if (mute < 0 || mute > 3) {
+ pr_err("[%s:%s] invalid mute status %d\n", __MM_FILE__,
+ __func__, mute);
+ return -EINVAL;
+ }
+
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_MUTE;
+ if ((mute == STREAM_UNMUTE) || (mute == STREAM_MUTE)) {
+ rpc.device_id = ADSP_AUDIO_DEVICE_ID_VOICE;
+ rpc.path = ADSP_PATH_TX_CNG_DIS;
+ } else {
+ rpc.device_id = dev_id;
+ rpc.path = ADSP_PATH_TX;
+ }
+ mute &= 0x01;
+ rpc.mute = !!mute;
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+static int audio_stream_volume(struct audio_client *ac, int volume)
+{
+ struct adsp_set_volume_command rpc;
+ int rc;
+
+ pr_debug("[%s:%s] volume = %d\n", __MM_FILE__, __func__, volume);
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_SET_STREAM_VOL;
+ rpc.volume = volume;
+ rc = audio_ioctl(ac, &rpc, sizeof(rpc));
+ return rc;
+}
+
+static int audio_stream_mute(struct audio_client *ac, int mute)
+{
+ struct adsp_set_mute_command rpc;
+ int rc;
+
+ pr_debug("[%s:%s] mute = %d\n", __MM_FILE__, __func__, mute);
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_SET_STREAM_MUTE;
+ rpc.mute = mute;
+ rc = audio_ioctl(ac, &rpc, sizeof(rpc));
+ return rc;
+}
+
+static void callback(void *data, int len, void *cookie)
+{
+ struct adsp_event_hdr *e = data;
+ struct audio_client *ac;
+ struct adsp_buffer_event *abe = data;
+
+ if (e->context >= SESSION_MAX) {
+ pr_err("[%s:%s] bogus session %d\n", __MM_FILE__, __func__,
+ e->context);
+ return;
+ }
+ ac = session[e->context];
+ if (!ac) {
+ pr_err("[%s:%s] unknown session %d\n", __MM_FILE__, __func__,
+ e->context);
+ return;
+ }
+
+ if (e->event_id == ADSP_AUDIO_IOCTL_CMD_STREAM_EOS) {
+ pr_debug("[%s:%s] CB Stream eos, ac = %p\n",
+ __MM_FILE__, __func__, ac);
+ if (e->status)
+ pr_err("[%s:%s] playback status %d\n", __MM_FILE__,
+ __func__, e->status);
+ if (ac->cb_status == -EBUSY) {
+ ac->cb_status = e->status;
+ wake_up(&ac->wait);
+ }
+ return;
+ }
+
+ if (e->event_id == ADSP_AUDIO_EVT_STATUS_BUF_DONE) {
+ pr_debug("[%s:%s] CB done, ac = %p, status = %d\n",
+ __MM_FILE__, __func__, ac, e->status);
+ if (e->status)
+ pr_err("[%s:%s] buffer status %d\n", __MM_FILE__,
+ __func__, e->status);
+
+ ac->buf[ac->dsp_buf].actual_size = abe->buffer.actual_size;
+ ac->buf[ac->dsp_buf].used = 0;
+ ac->dsp_buf ^= 1;
+ wake_up(&ac->wait);
+ return;
+ }
+
+ pr_debug("[%s:%s] ac = %p, event_id = 0x%x, status = %d\n",
+ __MM_FILE__, __func__, ac, e->event_id, e->status);
+ if (e->status)
+ pr_warning("audio_cb: s=%d e=%08x status=%d\n",
+ e->context, e->event_id, e->status);
+ if (ac->cb_status == -EBUSY) {
+ ac->cb_status = e->status;
+ wake_up(&ac->wait);
+ }
+}
+
+static void audio_init(struct dal_client *client)
+{
+ u32 tmp[3];
+
+ pr_debug("[%s:%s]\n", __MM_FILE__, __func__);
+ tmp[0] = 2 * sizeof(u32);
+ tmp[1] = 0;
+ tmp[2] = 0;
+ dal_call(client, AUDIO_OP_INIT, 5, tmp, sizeof(tmp),
+ tmp, sizeof(u32));
+}
+
+static struct audio_client *ac_control;
+
+static int q6audio_init(void)
+{
+ struct audio_client *ac = 0;
+ int res;
+
+ pr_debug("[%s:%s]\n", __MM_FILE__, __func__);
+ mutex_lock(&audio_lock);
+ if (ac_control) {
+ res = 0;
+ goto done;
+ }
+
+ pr_info("[%s:%s] codecs\n", __MM_FILE__, __func__);
+ icodec_rx_clk = clk_get(0, "icodec_rx_clk");
+ icodec_tx_clk = clk_get(0, "icodec_tx_clk");
+ ecodec_clk = clk_get(0, "ecodec_clk");
+ sdac_clk = clk_get(0, "sdac_clk");
+ audio_phys = pmem_kalloc(4096, PMEM_MEMTYPE_EBI1|PMEM_ALIGNMENT_4K);
+ audio_data = ioremap(audio_phys, 4096);
+
+ pr_info("[%s:%s] attach ADSP\n", __MM_FILE__, __func__);
+ adsp = dal_attach(AUDIO_DAL_DEVICE, AUDIO_DAL_PORT, 1,
+ callback, 0);
+ if (!adsp) {
+ pr_err("[%s:%s] cannot attach to adsp\n", __MM_FILE__,
+ __func__);
+ res = -ENODEV;
+ goto done;
+ }
+ pr_info("[%s:%s] INIT\n", __MM_FILE__, __func__);
+ audio_init(adsp);
+ dal_trace(adsp);
+
+ ac = audio_client_alloc(0);
+ if (!ac) {
+ pr_err("[%s:%s] cannot allocate client\n",
+ __MM_FILE__, __func__);
+ res = -ENOMEM;
+ goto done;
+ }
+
+ pr_info("[%s:%s] OPEN control\n", __MM_FILE__, __func__);
+ if (audio_open_control(ac)) {
+ pr_err("[%s:%s] cannot open control channel\n",
+ __MM_FILE__, __func__);
+ res = -ENODEV;
+ goto done;
+ }
+
+ pr_info("[%s:%s] attach ACDB\n", __MM_FILE__, __func__);
+ acdb = dal_attach(ACDB_DAL_DEVICE, ACDB_DAL_PORT, 0, 0, 0);
+ if (!acdb) {
+ pr_err("[%s:%s] cannot attach to acdb channel\n",
+ __MM_FILE__, __func__);
+ res = -ENODEV;
+ goto done;
+ }
+
+ pr_info("[%s:%s] attach ADIE\n", __MM_FILE__, __func__);
+ adie = dal_attach(ADIE_DAL_DEVICE, ADIE_DAL_PORT, 0, 0, 0);
+ if (!adie) {
+ pr_err("[%s:%s] cannot attach to adie\n",
+ __MM_FILE__, __func__);
+ res = -ENODEV;
+ goto done;
+ }
+ if (analog_ops->init)
+ analog_ops->init();
+
+ res = 0;
+ ac_control = ac;
+
+ wake_lock_init(&idlelock, WAKE_LOCK_IDLE, "audio_pcm_idle");
+ wake_lock_init(&wakelock, WAKE_LOCK_SUSPEND, "audio_pcm_suspend");
+done:
+ if ((res < 0) && ac)
+ audio_client_free(ac);
+ mutex_unlock(&audio_lock);
+
+ pr_debug("[%s:%s] res = %d\n", __MM_FILE__, __func__, res);
+ return res;
+}
+
+struct audio_config_data {
+ uint32_t device_id;
+ uint32_t sample_rate;
+ uint32_t offset;
+ uint32_t length;
+};
+
+struct audio_config_database {
+ uint8_t magic[8];
+ uint32_t entry_count;
+ uint32_t unused;
+ struct audio_config_data entry[0];
+};
+
+void *acdb_data;
+const struct firmware *acdb_fw;
+extern struct miscdevice q6_control_device;
+
+static int acdb_get_config_table(uint32_t device_id, uint32_t sample_rate)
+{
+ struct acdb_cmd_device_table rpc;
+ struct acdb_result res;
+ int r;
+
+ pr_debug("[%s:%s] device_id = 0x%x, samplerate = %d\n", __MM_FILE__,
+ __func__, device_id, sample_rate);
+ if (q6audio_init())
+ return 0;
+
+ memset(audio_data, 0, 4096);
+ memset(&rpc, 0, sizeof(rpc));
+
+ rpc.size = sizeof(rpc) - (2 * sizeof(uint32_t));
+ rpc.command_id = ACDB_GET_DEVICE_TABLE;
+ rpc.device_id = device_id;
+ rpc.sample_rate_id = sample_rate;
+ rpc.total_bytes = 4096;
+ rpc.unmapped_buf = audio_phys;
+ rpc.res_size = sizeof(res) - (2 * sizeof(uint32_t));
+
+ r = dal_call(acdb, ACDB_OP_IOCTL, 8, &rpc, sizeof(rpc),
+ &res, sizeof(res));
+
+ if ((r == sizeof(res)) && (res.dal_status == 0))
+ return res.used_bytes;
+
+ return -EIO;
+}
+
+static uint32_t audio_rx_path_id = ADIE_PATH_HANDSET_RX;
+static uint32_t audio_rx_device_id = ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR;
+static uint32_t audio_rx_device_group = -1;
+static uint32_t audio_tx_path_id = ADIE_PATH_HANDSET_TX;
+static uint32_t audio_tx_device_id = ADSP_AUDIO_DEVICE_ID_HANDSET_MIC;
+static uint32_t audio_tx_device_group = -1;
+
+static int qdsp6_devchg_notify(struct audio_client *ac,
+ uint32_t dev_type, uint32_t dev_id)
+{
+ struct adsp_device_switch_command rpc;
+
+ if (dev_type != ADSP_AUDIO_RX_DEVICE &&
+ dev_type != ADSP_AUDIO_TX_DEVICE)
+ return -EINVAL;
+
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_PREPARE;
+ if (dev_type == ADSP_AUDIO_RX_DEVICE) {
+ rpc.old_device = audio_rx_device_id;
+ rpc.new_device = dev_id;
+ } else {
+ rpc.old_device = audio_tx_device_id;
+ rpc.new_device = dev_id;
+ }
+ rpc.device_class = 0;
+ rpc.device_type = dev_type;
+ pr_debug("[%s:%s] dev_id = 0x%x\n", __MM_FILE__, __func__, dev_id);
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+static int qdsp6_standby(struct audio_client *ac)
+{
+ pr_debug("[%s:%s]\n", __MM_FILE__, __func__);
+ return audio_command(ac, ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_STANDBY);
+}
+
+static int qdsp6_start(struct audio_client *ac)
+{
+ pr_debug("[%s:%s]\n", __MM_FILE__, __func__);
+ return audio_command(ac, ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_COMMIT);
+}
+
+static void audio_rx_analog_enable(int en)
+{
+ pr_debug("[%s:%s] audio_rx_device_id = 0x%x, en = %d\n", __MM_FILE__,
+ __func__, audio_rx_device_id, en);
+ switch (audio_rx_device_id) {
+ case ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_MONO:
+ case ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_STEREO:
+ case ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_SPKR:
+ if (analog_ops->headset_enable)
+ analog_ops->headset_enable(en);
+ break;
+ case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_MONO_HEADSET:
+ case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_STEREO_HEADSET:
+ case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_MONO_HEADSET:
+ case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_STEREO_HEADSET:
+ if (analog_ops->headset_enable)
+ analog_ops->headset_enable(en);
+ if (analog_ops->speaker_enable)
+ analog_ops->speaker_enable(en);
+ break;
+ case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO:
+ case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO:
+ if (analog_ops->speaker_enable)
+ analog_ops->speaker_enable(en);
+ break;
+ case ADSP_AUDIO_DEVICE_ID_BT_SCO_SPKR:
+ if (analog_ops->bt_sco_enable)
+ analog_ops->bt_sco_enable(en);
+ break;
+ case ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR:
+ if (analog_ops->receiver_enable)
+ analog_ops->receiver_enable(en);
+ break;
+ }
+}
+
+static void audio_tx_analog_enable(int en)
+{
+ pr_debug("[%s:%s] audio_tx_device_id = 0x%x, en = %d\n", __MM_FILE__,
+ __func__, audio_tx_device_id, en);
+ switch (audio_tx_device_id) {
+ case ADSP_AUDIO_DEVICE_ID_HANDSET_MIC:
+ case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MIC:
+ if (analog_ops->int_mic_enable)
+ analog_ops->int_mic_enable(en);
+ break;
+ case ADSP_AUDIO_DEVICE_ID_HEADSET_MIC:
+ case ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_MIC:
+ case ADSP_AUDIO_DEVICE_ID_HANDSET_DUAL_MIC:
+ case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_DUAL_MIC:
+ if (analog_ops->ext_mic_enable)
+ analog_ops->ext_mic_enable(en);
+ break;
+ case ADSP_AUDIO_DEVICE_ID_BT_SCO_MIC:
+ if (analog_ops->bt_sco_enable)
+ analog_ops->bt_sco_enable(en);
+ break;
+ }
+}
+
+static int audio_update_acdb(uint32_t adev, uint32_t acdb_id)
+{
+ uint32_t sample_rate;
+ int sz;
+
+ pr_debug("[%s:%s] adev = 0x%x, acdb_id = 0x%x\n", __MM_FILE__,
+ __func__, adev, acdb_id);
+ if (q6_device_to_dir(adev) == Q6_RX) {
+ rx_acdb = acdb_id;
+ sample_rate = q6_device_to_rate(adev);
+ } else {
+
+ tx_acdb = acdb_id;
+ if (tx_clk_freq > 16000)
+ sample_rate = 48000;
+ else if (tx_clk_freq > 8000)
+ sample_rate = 16000;
+ else
+ sample_rate = 8000;
+ }
+
+ if (acdb_id == 0)
+ acdb_id = q6_device_to_cad_id(adev);
+
+ sz = acdb_get_config_table(acdb_id, sample_rate);
+ audio_set_table(ac_control, adev, sz);
+
+ return 0;
+}
+
+static void adie_rx_path_enable(uint32_t acdb_id)
+{
+ pr_debug("[%s:%s]\n", __MM_FILE__, __func__);
+ if (audio_rx_path_id) {
+ adie_enable();
+ adie_set_path(adie, audio_rx_path_id, ADIE_PATH_RX);
+ adie_set_path_freq_plan(adie, ADIE_PATH_RX, 48000);
+
+ adie_proceed_to_stage(adie, ADIE_PATH_RX,
+ ADIE_STAGE_DIGITAL_READY);
+ adie_proceed_to_stage(adie, ADIE_PATH_RX,
+ ADIE_STAGE_DIGITAL_ANALOG_READY);
+ }
+}
+
+static void q6_rx_path_enable(int reconf, uint32_t acdb_id)
+{
+ pr_debug("[%s:%s]\n", __MM_FILE__, __func__);
+ if (!reconf)
+ qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, audio_rx_device_id);
+ audio_update_acdb(audio_rx_device_id, acdb_id);
+ qdsp6_standby(ac_control);
+ qdsp6_start(ac_control);
+}
+
+static void _audio_rx_path_enable(int reconf, uint32_t acdb_id)
+{
+ pr_debug("[%s:%s] reconf = %d\n", __MM_FILE__, __func__, reconf);
+ q6_rx_path_enable(reconf, acdb_id);
+ if (audio_rx_path_id)
+ adie_rx_path_enable(acdb_id);
+ audio_rx_analog_enable(1);
+}
+
+static void _audio_tx_path_enable(int reconf, uint32_t acdb_id)
+{
+ pr_debug("[%s:%s] reconf = %d, tx_clk_freq = %d\n", __MM_FILE__,
+ __func__, reconf, tx_clk_freq);
+ audio_tx_analog_enable(1);
+
+ if (audio_tx_path_id) {
+ adie_enable();
+ adie_set_path(adie, audio_tx_path_id, ADIE_PATH_TX);
+
+ if (tx_clk_freq > 16000)
+ adie_set_path_freq_plan(adie, ADIE_PATH_TX, 48000);
+ else if (tx_clk_freq > 8000)
+ adie_set_path_freq_plan(adie, ADIE_PATH_TX, 16000);
+ else
+ adie_set_path_freq_plan(adie, ADIE_PATH_TX, 8000);
+
+ adie_proceed_to_stage(adie, ADIE_PATH_TX,
+ ADIE_STAGE_DIGITAL_READY);
+ adie_proceed_to_stage(adie, ADIE_PATH_TX,
+ ADIE_STAGE_DIGITAL_ANALOG_READY);
+ }
+
+
+ if (!reconf)
+ qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE,
+ audio_tx_device_id);
+ audio_update_acdb(audio_tx_device_id, acdb_id);
+ qdsp6_standby(ac_control);
+ qdsp6_start(ac_control);
+
+ audio_tx_mute(ac_control, audio_tx_device_id, tx_mute_status);
+}
+
+static void _audio_rx_path_disable(void)
+{
+ pr_debug("[%s:%s]\n", __MM_FILE__, __func__);
+ audio_rx_analog_enable(0);
+
+ if (audio_rx_path_id) {
+ adie_proceed_to_stage(adie, ADIE_PATH_RX,
+ ADIE_STAGE_ANALOG_OFF);
+ adie_proceed_to_stage(adie, ADIE_PATH_RX,
+ ADIE_STAGE_DIGITAL_OFF);
+ adie_disable();
+ }
+}
+
+static void _audio_tx_path_disable(void)
+{
+ pr_debug("[%s:%s]\n", __MM_FILE__, __func__);
+ audio_tx_analog_enable(0);
+
+ if (audio_tx_path_id) {
+ adie_proceed_to_stage(adie, ADIE_PATH_TX,
+ ADIE_STAGE_ANALOG_OFF);
+ adie_proceed_to_stage(adie, ADIE_PATH_TX,
+ ADIE_STAGE_DIGITAL_OFF);
+ adie_disable();
+ }
+}
+
+static int icodec_rx_clk_refcount;
+static int icodec_tx_clk_refcount;
+static int ecodec_clk_refcount;
+static int sdac_clk_refcount;
+
+static void ecodec_clk_enable(void)
+{
+ ecodec_clk_refcount++;
+ if (ecodec_clk_refcount == 1) {
+ clk_set_rate(ecodec_clk, 2048000);
+ clk_enable(ecodec_clk);
+ }
+}
+static void ecodec_clk_disable(int group_reset, int path)
+{
+ ecodec_clk_refcount--;
+ if (ecodec_clk_refcount == 0) {
+ clk_disable(ecodec_clk);
+ if (group_reset) {
+ if (path == ADSP_PATH_TX)
+ audio_tx_device_group = -1;
+ else
+ audio_rx_device_group = -1;
+ }
+ }
+}
+static void _audio_rx_clk_enable(void)
+{
+ uint32_t device_group = q6_device_to_codec(audio_rx_device_id);
+
+ pr_debug("[%s:%s] rx_clk_refcount = %d\n", __MM_FILE__, __func__,
+ icodec_rx_clk_refcount);
+ switch(device_group) {
+ case Q6_ICODEC_RX:
+ icodec_rx_clk_refcount++;
+ if (icodec_rx_clk_refcount == 1) {
+ clk_set_rate(icodec_rx_clk, 12288000);
+ clk_enable(icodec_rx_clk);
+ }
+ break;
+ case Q6_ECODEC_RX:
+ ecodec_clk_enable();
+ break;
+ case Q6_SDAC_RX:
+ sdac_clk_refcount++;
+ if (sdac_clk_refcount == 1) {
+ clk_set_rate(sdac_clk, 12288000);
+ clk_enable(sdac_clk);
+ }
+ break;
+ default:
+ return;
+ }
+ audio_rx_device_group = device_group;
+}
+
+static void _audio_tx_clk_enable(void)
+{
+ uint32_t device_group = q6_device_to_codec(audio_tx_device_id);
+ uint32_t icodec_tx_clk_rate;
+
+ pr_debug("[%s:%s] tx_clk_refcount = %d\n", __MM_FILE__, __func__,
+ icodec_tx_clk_refcount);
+ switch (device_group) {
+ case Q6_ICODEC_TX:
+ icodec_tx_clk_refcount++;
+ if (icodec_tx_clk_refcount == 1) {
+ if (tx_clk_freq > 16000)
+ icodec_tx_clk_rate = 48000;
+ else if (tx_clk_freq > 8000)
+ icodec_tx_clk_rate = 16000;
+ else
+ icodec_tx_clk_rate = 8000;
+
+ clk_set_rate(icodec_tx_clk, icodec_tx_clk_rate * 256);
+ clk_enable(icodec_tx_clk);
+ }
+ break;
+ case Q6_ECODEC_TX:
+ ecodec_clk_enable();
+ break;
+ case Q6_SDAC_TX:
+ /* TODO: In QCT BSP, clk rate was set to 20480000 */
+ sdac_clk_refcount++;
+ if (sdac_clk_refcount == 1) {
+ clk_set_rate(sdac_clk, 12288000);
+ clk_enable(sdac_clk);
+ }
+ break;
+ default:
+ return;
+ }
+ audio_tx_device_group = device_group;
+}
+
+static void _audio_rx_clk_disable(void)
+{
+ pr_debug("[%s:%s] rx_clk_refcount = %d\n", __MM_FILE__, __func__,
+ icodec_rx_clk_refcount);
+ switch (audio_rx_device_group) {
+ case Q6_ICODEC_RX:
+ icodec_rx_clk_refcount--;
+ if (icodec_rx_clk_refcount == 0) {
+ clk_disable(icodec_rx_clk);
+ audio_rx_device_group = -1;
+ }
+ break;
+ case Q6_ECODEC_RX:
+ ecodec_clk_disable(1, ADSP_PATH_RX);
+ break;
+ case Q6_SDAC_RX:
+ sdac_clk_refcount--;
+ if (sdac_clk_refcount == 0) {
+ clk_disable(sdac_clk);
+ audio_rx_device_group = -1;
+ }
+ break;
+ default:
+ pr_err("[%s:%s] invalid rx device group %d\n", __MM_FILE__,
+ __func__, audio_rx_device_group);
+ break;
+ }
+}
+
+static void _audio_tx_clk_disable(void)
+{
+ pr_debug("[%s:%s] tx_clk_refcount = %d\n", __MM_FILE__, __func__,
+ icodec_tx_clk_refcount);
+ switch (audio_tx_device_group) {
+ case Q6_ICODEC_TX:
+ icodec_tx_clk_refcount--;
+ if (icodec_tx_clk_refcount == 0) {
+ clk_disable(icodec_tx_clk);
+ audio_tx_device_group = -1;
+ }
+ break;
+ case Q6_ECODEC_TX:
+ ecodec_clk_disable(1, ADSP_PATH_TX);
+ break;
+ case Q6_SDAC_TX:
+ sdac_clk_refcount--;
+ if (sdac_clk_refcount == 0) {
+ clk_disable(sdac_clk);
+ audio_tx_device_group = -1;
+ }
+ break;
+ default:
+ pr_err("[%s:%s] invalid tx device group %d\n",
+ __MM_FILE__, __func__, audio_tx_device_group);
+ break;
+ }
+}
+
+static void _audio_rx_clk_reinit(uint32_t rx_device, uint32_t acdb_id)
+{
+ uint32_t device_group = q6_device_to_codec(rx_device);
+
+ pr_debug("[%s:%s] rx_device = 0x%x\n", __MM_FILE__, __func__,
+ rx_device);
+ if (device_group != audio_rx_device_group)
+ _audio_rx_clk_disable();
+
+ audio_rx_device_id = rx_device;
+ audio_rx_path_id = q6_device_to_path(rx_device, acdb_id);
+
+ if (device_group != audio_rx_device_group)
+ _audio_rx_clk_enable();
+
+}
+
+static void _audio_tx_clk_reinit(uint32_t tx_device, uint32_t acdb_id)
+{
+ uint32_t device_group = q6_device_to_codec(tx_device);
+
+ pr_debug("[%s:%s] tx_device = 0x%x\n", __MM_FILE__, __func__,
+ tx_device);
+ if (device_group != audio_tx_device_group)
+ _audio_tx_clk_disable();
+
+ audio_tx_device_id = tx_device;
+ audio_tx_path_id = q6_device_to_path(tx_device, acdb_id);
+
+ if (device_group != audio_tx_device_group)
+ _audio_tx_clk_enable();
+}
+
+static DEFINE_MUTEX(audio_path_lock);
+static int audio_rx_path_refcount;
+static int audio_tx_path_refcount;
+
+static int audio_rx_path_enable(int en, uint32_t acdb_id)
+{
+ pr_debug("[%s:%s] en = %d\n", __MM_FILE__, __func__, en);
+ mutex_lock(&audio_path_lock);
+ if (en) {
+ audio_rx_path_refcount++;
+ if (audio_rx_path_refcount == 1) {
+ _audio_rx_clk_enable();
+ _audio_rx_path_enable(0, acdb_id);
+ }
+ } else {
+ audio_rx_path_refcount--;
+ if (audio_rx_path_refcount == 0) {
+ _audio_rx_path_disable();
+ _audio_rx_clk_disable();
+ }
+ }
+ mutex_unlock(&audio_path_lock);
+ return 0;
+}
+
+static int audio_tx_path_enable(int en, uint32_t acdb_id)
+{
+ pr_debug("[%s:%s] en = %d\n", __MM_FILE__, __func__, en);
+ mutex_lock(&audio_path_lock);
+ if (en) {
+ audio_tx_path_refcount++;
+ if (audio_tx_path_refcount == 1) {
+ _audio_tx_clk_enable();
+ _audio_tx_path_enable(0, acdb_id);
+ }
+ } else {
+ audio_tx_path_refcount--;
+ if (audio_tx_path_refcount == 0) {
+ _audio_tx_path_disable();
+ _audio_tx_clk_disable();
+ }
+ }
+ mutex_unlock(&audio_path_lock);
+ return 0;
+}
+
+int q6audio_update_acdb(uint32_t id_src, uint32_t id_dst)
+{
+ int res;
+
+ pr_debug("[%s:%s] id_src = 0x%x\n, id_dst = 0x%x\n", __MM_FILE__,
+ __func__, id_src, id_dst);
+ if (q6audio_init())
+ return 0;
+
+ mutex_lock(&audio_path_lock);
+
+ if (q6_device_to_dir(id_dst) == Q6_RX)
+ qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, id_dst);
+ else
+ qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE, id_dst);
+ res = audio_update_acdb(id_dst, id_src);
+ if (res)
+ goto done;
+
+ qdsp6_standby(ac_control);
+ qdsp6_start(ac_control);
+done:
+ mutex_unlock(&audio_path_lock);
+ return res;
+}
+
+int q6audio_set_tx_mute(int mute)
+{
+ uint32_t adev;
+ int rc;
+
+ if (q6audio_init())
+ return 0;
+
+ mutex_lock(&audio_path_lock);
+
+ if (mute == tx_mute_status) {
+ mutex_unlock(&audio_path_lock);
+ return 0;
+ }
+
+ adev = audio_tx_device_id;
+ rc = audio_tx_mute(ac_control, adev, mute);
+
+ /* DSP caches the requested MUTE state when it cannot apply the state
+ immediately. In that case, it returns EUNSUPPORTED and applies the
+ cached state later */
+ if ((rc == ADSP_AUDIO_STATUS_SUCCESS) ||
+ (rc == ADSP_AUDIO_STATUS_EUNSUPPORTED)) {
+ pr_debug("[%s:%s] return status = %d\n",
+ __MM_FILE__, __func__, rc);
+ tx_mute_status = mute;
+ }
+ mutex_unlock(&audio_path_lock);
+ return 0;
+}
+
+int q6audio_set_stream_volume(struct audio_client *ac, int vol)
+{
+ if (vol > 1200 || vol < -4000) {
+ pr_err("[%s:%s] unsupported volume level %d\n", __MM_FILE__,
+ __func__, vol);
+ return -EINVAL;
+ }
+ mutex_lock(&audio_path_lock);
+ audio_stream_mute(ac, 0);
+ audio_stream_volume(ac, vol);
+ mutex_unlock(&audio_path_lock);
+ return 0;
+}
+
+int q6audio_set_rx_volume(int level)
+{
+ uint32_t adev;
+ int vol;
+
+ pr_debug("[%s:%s] level = %d\n", __MM_FILE__, __func__, level);
+ if (q6audio_init())
+ return 0;
+
+ if (level < 0 || level > 100)
+ return -EINVAL;
+
+ mutex_lock(&audio_path_lock);
+ adev = ADSP_AUDIO_DEVICE_ID_VOICE;
+
+ if (level) {
+ vol = q6_device_volume(audio_rx_device_id, level);
+ audio_rx_mute(ac_control, adev, 0);
+ audio_rx_volume(ac_control, adev, vol);
+ } else
+ audio_rx_mute(ac_control, adev, 1);
+
+ rx_vol_level = level;
+ mutex_unlock(&audio_path_lock);
+ return 0;
+}
+
+static void do_rx_routing(uint32_t device_id, uint32_t acdb_id)
+{
+ pr_debug("[%s:%s] device_id = 0x%x, acdb_id = 0x%x\n", __MM_FILE__,
+ __func__, device_id, acdb_id);
+ if (device_id == audio_rx_device_id &&
+ audio_rx_path_id == q6_device_to_path(device_id, acdb_id)) {
+ if (acdb_id != rx_acdb) {
+ qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, device_id);
+ audio_update_acdb(device_id, acdb_id);
+ qdsp6_standby(ac_control);
+ qdsp6_start(ac_control);
+ }
+ return;
+ }
+
+ if (audio_rx_path_refcount > 0) {
+ qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, device_id);
+ _audio_rx_path_disable();
+ _audio_rx_clk_reinit(device_id, acdb_id);
+ _audio_rx_path_enable(1, acdb_id);
+ } else {
+ qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE,
+ device_id);
+ audio_update_acdb(device_id, acdb_id);
+ qdsp6_standby(ac_control);
+ qdsp6_start(ac_control);
+ audio_rx_device_id = device_id;
+ audio_rx_path_id = q6_device_to_path(device_id, acdb_id);
+ }
+}
+
+static void do_tx_routing(uint32_t device_id, uint32_t acdb_id)
+{
+ pr_debug("[%s:%s] device_id = 0x%x, acdb_id = 0x%x\n", __MM_FILE__,
+ __func__, device_id, acdb_id);
+ if (device_id == audio_tx_device_id &&
+ audio_tx_path_id == q6_device_to_path(device_id, acdb_id)) {
+ if (acdb_id != tx_acdb) {
+ qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE,
+ device_id);
+ audio_update_acdb(device_id, acdb_id);
+ qdsp6_standby(ac_control);
+ qdsp6_start(ac_control);
+ }
+ return;
+ }
+
+ if (audio_tx_path_refcount > 0) {
+ qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE, device_id);
+ _audio_tx_path_disable();
+ _audio_tx_clk_reinit(device_id, acdb_id);
+ _audio_tx_path_enable(1, acdb_id);
+ } else {
+ qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE,
+ device_id);
+ audio_update_acdb(device_id, acdb_id);
+ qdsp6_standby(ac_control);
+ qdsp6_start(ac_control);
+ audio_tx_device_id = device_id;
+ audio_tx_path_id = q6_device_to_path(device_id, acdb_id);
+ tx_acdb = acdb_id;
+ }
+}
+
+int q6audio_do_routing(uint32_t device_id, uint32_t acdb_id)
+{
+ if (q6audio_init())
+ return 0;
+
+ mutex_lock(&audio_path_lock);
+
+ switch(q6_device_to_dir(device_id)) {
+ case Q6_RX:
+ do_rx_routing(device_id, acdb_id);
+ break;
+ case Q6_TX:
+ do_tx_routing(device_id, acdb_id);
+ break;
+ }
+
+ mutex_unlock(&audio_path_lock);
+ return 0;
+}
+
+int q6audio_set_route(const char *name)
+{
+ uint32_t route;
+ if (!strcmp(name, "speaker")) {
+ route = ADIE_PATH_SPEAKER_STEREO_RX;
+ } else if (!strcmp(name, "headphones")) {
+ route = ADIE_PATH_HEADSET_STEREO_RX;
+ } else if (!strcmp(name, "handset")) {
+ route = ADIE_PATH_HANDSET_RX;
+ } else {
+ return -EINVAL;
+ }
+
+ mutex_lock(&audio_path_lock);
+ if (route == audio_rx_path_id)
+ goto done;
+
+ audio_rx_path_id = route;
+
+ if (audio_rx_path_refcount > 0) {
+ _audio_rx_path_disable();
+ _audio_rx_path_enable(1, 0);
+ }
+ if (audio_tx_path_refcount > 0) {
+ _audio_tx_path_disable();
+ _audio_tx_path_enable(1, 0);
+ }
+done:
+ mutex_unlock(&audio_path_lock);
+ return 0;
+}
+
+static int audio_stream_equalizer(struct audio_client *ac, void *eq_config)
+{
+ int i;
+ struct adsp_set_equalizer_command rpc;
+ struct adsp_audio_eq_stream_config *eq_cfg;
+ eq_cfg = (struct adsp_audio_eq_stream_config *) eq_config;
+
+ memset(&rpc, 0, sizeof(rpc));
+
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_SET_SESSION_EQ_CONFIG;
+ rpc.enable = eq_cfg->enable;
+ rpc.num_bands = eq_cfg->num_bands;
+ for (i = 0; i < eq_cfg->num_bands; i++) {
+ rpc.eq_bands[i].band_idx = eq_cfg->eq_bands[i].band_idx;
+ rpc.eq_bands[i].filter_type = eq_cfg->eq_bands[i].filter_type;
+ rpc.eq_bands[i].center_freq_hz =
+ eq_cfg->eq_bands[i].center_freq_hz;
+ rpc.eq_bands[i].filter_gain = eq_cfg->eq_bands[i].filter_gain;
+ rpc.eq_bands[i].q_factor = eq_cfg->eq_bands[i].q_factor;
+ }
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+int q6audio_set_stream_eq_pcm(struct audio_client *ac, void *eq_config)
+{
+ int rc = 0;
+ mutex_lock(&audio_path_lock);
+ rc = audio_stream_equalizer(ac, eq_config);
+ mutex_unlock(&audio_path_lock);
+ return rc;
+}
+
+struct audio_client *q6audio_open_auxpcm(uint32_t rate,
+ uint32_t channels, uint32_t flags, uint32_t acdb_id)
+{
+ int rc, retry = 5;
+ struct audio_client *ac;
+
+ pr_debug("[%s:%s] rate = %d, channels = %d\n", __MM_FILE__, __func__,
+ rate, channels);
+ if (q6audio_init())
+ return NULL;
+ ac = audio_client_alloc(0);
+ if (!ac)
+ return NULL;
+
+ ac->flags = flags;
+
+ mutex_lock(&audio_path_lock);
+
+ if (ac->flags & AUDIO_FLAG_WRITE) {
+ audio_tx_path_refcount++;
+ if (audio_tx_path_refcount == 1) {
+ tx_clk_freq = rate;
+ _audio_tx_clk_enable();
+ _audio_tx_path_enable(0, acdb_id);
+ }
+ } else {
+ audio_rx_path_refcount++;
+ if (audio_rx_path_refcount == 1) {
+ _audio_rx_clk_enable();
+ _audio_rx_path_enable(0, acdb_id);
+ }
+ }
+
+ ecodec_clk_enable();
+
+ for (retry = 5;; retry--) {
+ if (ac->flags & AUDIO_FLAG_WRITE)
+ rc = audio_auxpcm_out_open(ac, rate, channels);
+ else
+ rc = audio_auxpcm_in_open(ac, rate, channels);
+ if (rc == 0)
+ break;
+ if (retry == 0)
+ q6audio_dsp_not_responding();
+
+ pr_err("[%s:%s] open pcm error %d, retrying\n",
+ __MM_FILE__, __func__, rc);
+ msleep(1);
+ }
+
+ mutex_unlock(&audio_path_lock);
+
+ for (retry = 5;; retry--) {
+ rc = audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START);
+ if (rc == 0)
+ break;
+ if (retry == 0)
+ q6audio_dsp_not_responding();
+
+ pr_err("[%s:%s] stream start error %d, retrying\n",
+ __MM_FILE__, __func__, rc);
+ }
+ audio_prevent_sleep();
+ return ac;
+
+}
+
+struct audio_client *q6audio_open_pcm(uint32_t bufsz, uint32_t rate,
+ uint32_t channels, uint32_t flags, uint32_t acdb_id)
+{
+ int rc, retry = 5;
+ struct audio_client *ac;
+
+ pr_debug("[%s:%s] bufsz = %d, rate = %d, channels = %d\n", __MM_FILE__,
+ __func__, bufsz, rate, channels);
+ if (q6audio_init())
+ return 0;
+
+ ac = audio_client_alloc(bufsz);
+ if (!ac)
+ return 0;
+
+ ac->flags = flags;
+
+ mutex_lock(&audio_path_lock);
+
+ if (ac->flags & AUDIO_FLAG_WRITE) {
+ audio_rx_path_refcount++;
+ if (audio_rx_path_refcount == 1) {
+ _audio_rx_clk_enable();
+ q6_rx_path_enable(0, acdb_id);
+ adie_rx_path_enable(acdb_id);
+ }
+ } else {
+ /* TODO: consider concurrency with voice call */
+ audio_tx_path_refcount++;
+ if (audio_tx_path_refcount == 1) {
+ tx_clk_freq = rate;
+ _audio_tx_clk_enable();
+ _audio_tx_path_enable(0, acdb_id);
+ }
+ }
+
+ for (retry = 5;;retry--) {
+ if (ac->flags & AUDIO_FLAG_WRITE)
+ rc = audio_out_open(ac, bufsz, rate, channels);
+ else
+ rc = audio_in_open(ac, bufsz, flags, rate, channels);
+ if (rc == 0)
+ break;
+ if (retry == 0)
+ q6audio_dsp_not_responding();
+
+ pr_err("[%s:%s] open pcm error %d, retrying\n",
+ __MM_FILE__, __func__, rc);
+ msleep(1);
+ }
+
+ if (ac->flags & AUDIO_FLAG_WRITE) {
+ if (audio_rx_path_refcount == 1)
+ audio_rx_analog_enable(1);
+ }
+ mutex_unlock(&audio_path_lock);
+
+ for (retry = 5;;retry--) {
+ rc = audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START);
+ if (rc == 0)
+ break;
+ if (retry == 0)
+ q6audio_dsp_not_responding();
+
+ pr_err("[%s:%s] stream start error %d, retrying\n",
+ __MM_FILE__, __func__, rc);
+ }
+
+ if (!(ac->flags & AUDIO_FLAG_WRITE)) {
+ ac->buf[0].used = 1;
+ ac->buf[1].used = 1;
+ q6audio_read(ac, &ac->buf[0]);
+ q6audio_read(ac, &ac->buf[1]);
+ }
+
+ audio_prevent_sleep();
+ return ac;
+}
+
+int q6audio_close(struct audio_client *ac)
+{
+ audio_close(ac);
+ if (ac->flags & AUDIO_FLAG_WRITE)
+ audio_rx_path_enable(0, 0);
+ else
+ audio_tx_path_enable(0, 0);
+ audio_client_free(ac);
+ audio_allow_sleep();
+ pr_debug("[%s:%s] ac = %p\n", __MM_FILE__, __func__, ac);
+ return 0;
+}
+
+int q6audio_auxpcm_close(struct audio_client *ac)
+{
+ audio_close(ac);
+ if (ac->flags & AUDIO_FLAG_WRITE) {
+ audio_tx_path_enable(0, 0);
+ ecodec_clk_disable(0, ADSP_PATH_RX);
+ } else {
+ audio_rx_path_enable(0, 0);
+ ecodec_clk_disable(0, ADSP_PATH_TX);
+ }
+
+ audio_client_free(ac);
+ audio_allow_sleep();
+ pr_debug("[%s:%s] ac = %p\n", __MM_FILE__, __func__, ac);
+ return 0;
+}
+struct audio_client *q6voice_open(uint32_t flags)
+{
+ struct audio_client *ac;
+
+ pr_debug("[%s:%s] flags = %d\n", __MM_FILE__, __func__, flags);
+ if (q6audio_init())
+ return 0;
+
+ ac = audio_client_alloc(0);
+ if (!ac)
+ return 0;
+
+ ac->flags = flags;
+ if (ac->flags & AUDIO_FLAG_WRITE)
+ audio_rx_path_enable(1, rx_acdb);
+ else {
+ if (!audio_tx_path_refcount)
+ tx_clk_freq = 8000;
+ audio_tx_path_enable(1, tx_acdb);
+ }
+
+ return ac;
+}
+
+int q6voice_close(struct audio_client *ac)
+{
+ if (ac->flags & AUDIO_FLAG_WRITE)
+ audio_rx_path_enable(0, 0);
+ else
+ audio_tx_path_enable(0, 0);
+
+ tx_mute_status = 0;
+ audio_client_free(ac);
+ pr_debug("[%s:%s]\n", __MM_FILE__, __func__);
+ return 0;
+}
+
+struct audio_client *q6audio_open_mp3(uint32_t bufsz, uint32_t rate,
+ uint32_t channels, uint32_t acdb_id)
+{
+ struct audio_client *ac;
+
+ pr_debug("[%s:%s] bufsz = %d, rate = %d\n, channels = %d",
+ __MM_FILE__, __func__, bufsz, rate, channels);
+
+ if (q6audio_init())
+ return 0;
+
+ ac = audio_client_alloc(bufsz);
+ if (!ac)
+ return 0;
+
+ ac->flags = AUDIO_FLAG_WRITE;
+ audio_rx_path_enable(1, acdb_id);
+
+ audio_mp3_open(ac, bufsz, rate, channels);
+ audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START);
+
+ mutex_lock(&audio_path_lock);
+ audio_rx_mute(ac_control, audio_rx_device_id, 0);
+ audio_rx_volume(ac_control, audio_rx_device_id,
+ q6_device_volume(audio_rx_device_id, rx_vol_level));
+ mutex_unlock(&audio_path_lock);
+ return ac;
+}
+
+struct audio_client *q6audio_open_dtmf(uint32_t rate,
+ uint32_t channels, uint32_t acdb_id)
+{
+ struct audio_client *ac;
+
+ pr_debug("[%s:%s] rate = %d\n, channels = %d", __MM_FILE__, __func__,
+ rate, channels);
+ if (q6audio_init())
+ return 0;
+
+ ac = audio_client_alloc(0);
+ if (!ac)
+ return 0;
+
+ ac->flags = AUDIO_FLAG_WRITE;
+ audio_rx_path_enable(1, acdb_id);
+
+ audio_dtmf_open(ac, rate, channels);
+ audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START);
+
+ mutex_lock(&audio_path_lock);
+ audio_rx_mute(ac_control, audio_rx_device_id, 0);
+ audio_rx_volume(ac_control, audio_rx_device_id,
+ q6_device_volume(audio_rx_device_id, rx_vol_level));
+ mutex_unlock(&audio_path_lock);
+
+ return ac;
+}
+
+int q6audio_play_dtmf(struct audio_client *ac, uint16_t dtmf_hi,
+ uint16_t dtmf_low, uint16_t duration, uint16_t rx_gain)
+{
+ struct adsp_audio_dtmf_start_command dtmf_cmd;
+
+ pr_debug("[%s:%s] high = %d, low = %d\n", __MM_FILE__, __func__,
+ dtmf_hi, dtmf_low);
+
+ dtmf_cmd.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_SESSION_DTMF_START;
+ dtmf_cmd.hdr.response_type = ADSP_AUDIO_RESPONSE_COMMAND;
+ dtmf_cmd.tone1_hz = dtmf_hi;
+ dtmf_cmd.tone2_hz = dtmf_low;
+ dtmf_cmd.duration_usec = duration * 1000;
+ dtmf_cmd.gain_mb = rx_gain;
+
+ return audio_ioctl(ac, &dtmf_cmd,
+ sizeof(struct adsp_audio_dtmf_start_command));
+
+}
+
+int q6audio_mp3_close(struct audio_client *ac)
+{
+ pr_debug("[%s:%s]\n", __MM_FILE__, __func__);
+ audio_close(ac);
+ audio_rx_path_enable(0, 0);
+ audio_client_free(ac);
+ return 0;
+}
+
+
+struct audio_client *q6audio_open_aac(uint32_t bufsz, uint32_t samplerate,
+ uint32_t channels, uint32_t bitrate,
+ uint32_t stream_format, uint32_t flags,
+ uint32_t acdb_id)
+{
+ struct audio_client *ac;
+
+ pr_debug("[%s:%s] bufsz = %d, samplerate = %d, channels = %d\n",
+ __MM_FILE__, __func__, bufsz, samplerate, channels);
+
+ if (q6audio_init())
+ return 0;
+
+ ac = audio_client_alloc(bufsz);
+ if (!ac)
+ return 0;
+
+ ac->flags = flags;
+
+ if (ac->flags & AUDIO_FLAG_WRITE)
+ audio_rx_path_enable(1, acdb_id);
+ else{
+ if (!audio_tx_path_refcount)
+ tx_clk_freq = 48000;
+ audio_tx_path_enable(1, acdb_id);
+ }
+
+ audio_aac_open(ac, bufsz, samplerate, channels, bitrate, flags,
+ stream_format);
+ audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START);
+
+ if (!(ac->flags & AUDIO_FLAG_WRITE)) {
+ ac->buf[0].used = 1;
+ ac->buf[1].used = 1;
+ q6audio_read(ac, &ac->buf[0]);
+ q6audio_read(ac, &ac->buf[1]);
+ }
+ audio_prevent_sleep();
+ return ac;
+}
+
+
+struct audio_client *q6audio_open_qcp(uint32_t bufsz, uint32_t min_rate,
+ uint32_t max_rate, uint32_t flags,
+ uint32_t format, uint32_t acdb_id)
+{
+ struct audio_client *ac;
+
+ pr_debug("[%s:%s] bufsz = %d\n", __MM_FILE__, __func__, bufsz);
+
+ if (q6audio_init())
+ return 0;
+
+ ac = audio_client_alloc(bufsz);
+ if (!ac)
+ return 0;
+
+ ac->flags = flags;
+
+ if (ac->flags & AUDIO_FLAG_WRITE)
+ audio_rx_path_enable(1, acdb_id);
+ else{
+ if (!audio_tx_path_refcount)
+ tx_clk_freq = 8000;
+ audio_tx_path_enable(1, acdb_id);
+ }
+
+ audio_qcp_open(ac, bufsz, min_rate, max_rate, flags, format);
+ audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START);
+
+ if (!(ac->flags & AUDIO_FLAG_WRITE)) {
+ ac->buf[0].used = 1;
+ ac->buf[1].used = 1;
+ q6audio_read(ac, &ac->buf[0]);
+ q6audio_read(ac, &ac->buf[1]);
+ }
+ audio_prevent_sleep();
+ return ac;
+}
+
+struct audio_client *q6audio_open_amrnb(uint32_t bufsz, uint32_t enc_mode,
+ uint32_t dtx_mode_enable,
+ uint32_t flags, uint32_t acdb_id)
+{
+ struct audio_client *ac;
+
+ pr_debug("[%s:%s] bufsz = %d, dtx_mode = %d\n", __MM_FILE__,
+ __func__, bufsz, dtx_mode_enable);
+
+ if (q6audio_init())
+ return 0;
+
+ ac = audio_client_alloc(bufsz);
+ if (!ac)
+ return 0;
+
+ ac->flags = flags;
+ if (ac->flags & AUDIO_FLAG_WRITE)
+ audio_rx_path_enable(1, acdb_id);
+ else{
+ if (!audio_tx_path_refcount)
+ tx_clk_freq = 8000;
+ audio_tx_path_enable(1, acdb_id);
+ }
+
+ audio_amrnb_open(ac, bufsz, enc_mode, flags, dtx_mode_enable);
+ audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START);
+
+ if (!(ac->flags & AUDIO_FLAG_WRITE)) {
+ ac->buf[0].used = 1;
+ ac->buf[1].used = 1;
+ q6audio_read(ac, &ac->buf[0]);
+ q6audio_read(ac, &ac->buf[1]);
+ }
+ audio_prevent_sleep();
+ return ac;
+}
+
+int q6audio_async(struct audio_client *ac)
+{
+ struct adsp_command_hdr rpc;
+ pr_debug("[%s:%s] ac = %p\n", __MM_FILE__, __func__, ac);
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.opcode = ADSP_AUDIO_IOCTL_CMD_STREAM_EOS;
+ rpc.response_type = ADSP_AUDIO_RESPONSE_ASYNC;
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
diff --git a/arch/arm/mach-msm/qdsp6/q6audio_devices.h b/arch/arm/mach-msm/qdsp6/q6audio_devices.h
new file mode 100644
index 0000000..d316ab0
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/q6audio_devices.h
@@ -0,0 +1,334 @@
+/* arch/arm/mach-msm/qdsp6/q6audio_devices.h
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Author: Brian Swetland <swetland@google.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.
+ *
+ */
+
+struct q6_device_info {
+ uint32_t id;
+ uint32_t cad_id;
+ uint32_t path;
+ uint32_t rate;
+ uint8_t dir;
+ uint8_t codec;
+ uint8_t hw;
+};
+
+#define Q6_ICODEC_RX 0
+#define Q6_ICODEC_TX 1
+#define Q6_ECODEC_RX 2
+#define Q6_ECODEC_TX 3
+#define Q6_SDAC_RX 6
+#define Q6_SDAC_TX 7
+#define Q6_CODEC_NONE 255
+
+#define Q6_TX 1
+#define Q6_RX 2
+#define Q6_TX_RX 3
+
+#define Q6_HW_HANDSET 0
+#define Q6_HW_HEADSET 1
+#define Q6_HW_SPEAKER 2
+#define Q6_HW_TTY 3
+#define Q6_HW_BT_SCO 4
+#define Q6_HW_BT_A2DP 5
+
+#define Q6_HW_COUNT 6
+
+#define CAD_HW_DEVICE_ID_HANDSET_MIC 0x01
+#define CAD_HW_DEVICE_ID_HANDSET_SPKR 0x02
+#define CAD_HW_DEVICE_ID_HEADSET_MIC 0x03
+#define CAD_HW_DEVICE_ID_HEADSET_SPKR_MONO 0x04
+#define CAD_HW_DEVICE_ID_HEADSET_SPKR_STEREO 0x05
+#define CAD_HW_DEVICE_ID_SPKR_PHONE_MIC 0x06
+#define CAD_HW_DEVICE_ID_SPKR_PHONE_MONO 0x07
+#define CAD_HW_DEVICE_ID_SPKR_PHONE_STEREO 0x08
+#define CAD_HW_DEVICE_ID_BT_SCO_MIC 0x09
+#define CAD_HW_DEVICE_ID_BT_SCO_SPKR 0x0A
+#define CAD_HW_DEVICE_ID_BT_A2DP_SPKR 0x0B
+#define CAD_HW_DEVICE_ID_TTY_HEADSET_MIC 0x0C
+#define CAD_HW_DEVICE_ID_TTY_HEADSET_SPKR 0x0D
+
+#define CAD_HW_DEVICE_ID_DEFAULT_TX 0x0E
+#define CAD_HW_DEVICE_ID_DEFAULT_RX 0x0F
+
+
+#define CAD_HW_DEVICE_ID_SPKR_PHONE_DUAL_MIC_BROADSIDE 0x2B
+#define CAD_HW_DEVICE_ID_SPKR_PHONE_DUAL_MIC_ENDFIRE 0x2D
+#define CAD_HW_DEVICE_ID_HANDSET_DUAL_MIC_BROADSIDE 0x2C
+#define CAD_HW_DEVICE_ID_HANDSET_DUAL_MIC_ENDFIRE 0x2E
+
+/* Logical Device to indicate A2DP routing */
+#define CAD_HW_DEVICE_ID_BT_A2DP_TX 0x10
+#define CAD_HW_DEVICE_ID_HEADSET_MONO_PLUS_SPKR_MONO_RX 0x11
+#define CAD_HW_DEVICE_ID_HEADSET_MONO_PLUS_SPKR_STEREO_RX 0x12
+#define CAD_HW_DEVICE_ID_HEADSET_STEREO_PLUS_SPKR_MONO_RX 0x13
+#define CAD_HW_DEVICE_ID_HEADSET_STEREO_PLUS_SPKR_STEREO_RX 0x14
+
+#define CAD_HW_DEVICE_ID_VOICE 0x15
+
+#define CAD_HW_DEVICE_ID_I2S_RX 0x20
+#define CAD_HW_DEVICE_ID_I2S_TX 0x21
+
+/* AUXPGA */
+#define CAD_HW_DEVICE_ID_HEADSET_SPKR_STEREO_LB 0x22
+#define CAD_HW_DEVICE_ID_HEADSET_SPKR_MONO_LB 0x23
+#define CAD_HW_DEVICE_ID_SPEAKER_SPKR_STEREO_LB 0x24
+#define CAD_HW_DEVICE_ID_SPEAKER_SPKR_MONO_LB 0x25
+
+#define CAD_HW_DEVICE_ID_NULL_RX 0x2A
+
+#define CAD_HW_DEVICE_ID_MAX_NUM 0x2F
+
+#define CAD_HW_DEVICE_ID_INVALID 0xFF
+
+#define CAD_RX_DEVICE 0x00
+#define CAD_TX_DEVICE 0x01
+
+static struct q6_device_info q6_audio_devices[] = {
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR,
+ .cad_id = CAD_HW_DEVICE_ID_HANDSET_SPKR,
+ .path = ADIE_PATH_HANDSET_RX,
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ICODEC_RX,
+ .hw = Q6_HW_HANDSET,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_MONO,
+ .cad_id = CAD_HW_DEVICE_ID_HEADSET_SPKR_MONO,
+ .path = ADIE_PATH_HEADSET_MONO_RX,
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ICODEC_RX,
+ .hw = Q6_HW_HEADSET,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_STEREO,
+ .cad_id = CAD_HW_DEVICE_ID_HEADSET_SPKR_STEREO,
+ .path = ADIE_PATH_HEADSET_STEREO_RX,
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ICODEC_RX,
+ .hw = Q6_HW_HEADSET,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO,
+ .cad_id = CAD_HW_DEVICE_ID_SPKR_PHONE_MONO,
+ .path = ADIE_PATH_SPEAKER_RX,
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ICODEC_RX,
+ .hw = Q6_HW_SPEAKER,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO,
+ .cad_id = CAD_HW_DEVICE_ID_SPKR_PHONE_STEREO,
+ .path = ADIE_PATH_SPEAKER_STEREO_RX,
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ICODEC_RX,
+ .hw = Q6_HW_SPEAKER,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_MONO_HEADSET,
+ .cad_id = CAD_HW_DEVICE_ID_HEADSET_MONO_PLUS_SPKR_MONO_RX,
+ .path = ADIE_PATH_SPKR_MONO_HDPH_MONO_RX,
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ICODEC_RX,
+ .hw = Q6_HW_SPEAKER,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_STEREO_HEADSET,
+ .cad_id = CAD_HW_DEVICE_ID_HEADSET_STEREO_PLUS_SPKR_MONO_RX,
+ .path = ADIE_PATH_SPKR_MONO_HDPH_STEREO_RX,
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ICODEC_RX,
+ .hw = Q6_HW_SPEAKER,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_MONO_HEADSET,
+ .cad_id = CAD_HW_DEVICE_ID_HEADSET_MONO_PLUS_SPKR_STEREO_RX,
+ .path = ADIE_PATH_SPKR_STEREO_HDPH_MONO_RX,
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ICODEC_RX,
+ .hw = Q6_HW_SPEAKER,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_STEREO_HEADSET,
+ .cad_id = CAD_HW_DEVICE_ID_HEADSET_STEREO_PLUS_SPKR_STEREO_RX,
+ .path = ADIE_PATH_SPKR_STEREO_HDPH_STEREO_RX,
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ICODEC_RX,
+ .hw = Q6_HW_SPEAKER,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_SPKR,
+ .cad_id = CAD_HW_DEVICE_ID_TTY_HEADSET_SPKR,
+ .path = ADIE_PATH_TTY_HEADSET_RX,
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ICODEC_RX,
+ .hw = Q6_HW_TTY,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_HANDSET_MIC,
+ .cad_id = CAD_HW_DEVICE_ID_HANDSET_MIC,
+ .path = ADIE_PATH_HANDSET_TX,
+ .rate = 8000,
+ .dir = Q6_TX,
+ .codec = Q6_ICODEC_TX,
+ .hw = Q6_HW_HANDSET,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_HEADSET_MIC,
+ .cad_id = CAD_HW_DEVICE_ID_HEADSET_MIC,
+ .path = ADIE_PATH_HEADSET_MONO_TX,
+ .rate = 8000,
+ .dir = Q6_TX,
+ .codec = Q6_ICODEC_TX,
+ .hw = Q6_HW_HEADSET,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MIC,
+ .cad_id = CAD_HW_DEVICE_ID_SPKR_PHONE_MIC,
+ .path = ADIE_PATH_SPEAKER_TX,
+ .rate = 8000,
+ .dir = Q6_TX,
+ .codec = Q6_ICODEC_TX,
+ .hw = Q6_HW_SPEAKER,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_HANDSET_DUAL_MIC,
+ .cad_id = CAD_HW_DEVICE_ID_HANDSET_DUAL_MIC_ENDFIRE,
+ .path = ADIE_CODEC_HANDSET_SPKR_EF_TX,
+ .rate = 8000,
+ .dir = Q6_TX,
+ .codec = Q6_ICODEC_TX,
+ .hw = Q6_HW_HANDSET,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_HANDSET_DUAL_MIC,
+ .cad_id = CAD_HW_DEVICE_ID_HANDSET_DUAL_MIC_BROADSIDE,
+ .path = ADIE_CODEC_HANDSET_SPKR_BS_TX,
+ .rate = 8000,
+ .dir = Q6_TX,
+ .codec = Q6_ICODEC_TX,
+ .hw = Q6_HW_HANDSET,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_DUAL_MIC,
+ .cad_id = CAD_HW_DEVICE_ID_SPKR_PHONE_DUAL_MIC_ENDFIRE,
+ .path = ADIE_CODEC_HANDSET_SPKR_EF_TX,
+ .rate = 8000,
+ .dir = Q6_TX,
+ .codec = Q6_ICODEC_TX,
+ .hw = Q6_HW_SPEAKER,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_DUAL_MIC,
+ .cad_id = CAD_HW_DEVICE_ID_SPKR_PHONE_DUAL_MIC_BROADSIDE,
+ .path = ADIE_CODEC_HANDSET_SPKR_BS_TX,
+ .rate = 8000,
+ .dir = Q6_TX,
+ .codec = Q6_ICODEC_TX,
+ .hw = Q6_HW_SPEAKER,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_MIC,
+ .cad_id = CAD_HW_DEVICE_ID_TTY_HEADSET_MIC,
+ .path = ADIE_PATH_TTY_HEADSET_TX,
+ .rate = 8000,
+ .dir = Q6_TX,
+ .codec = Q6_ICODEC_TX,
+ .hw = Q6_HW_HEADSET,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_BT_SCO_SPKR,
+ .cad_id = CAD_HW_DEVICE_ID_BT_SCO_SPKR,
+ .path = 0, /* XXX */
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ECODEC_RX,
+ .hw = Q6_HW_BT_SCO,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_BT_A2DP_SPKR,
+ .cad_id = CAD_HW_DEVICE_ID_BT_A2DP_SPKR,
+ .path = 0, /* XXX */
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ECODEC_RX,
+ .hw = Q6_HW_BT_A2DP,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_BT_SCO_MIC,
+ .cad_id = CAD_HW_DEVICE_ID_BT_SCO_MIC,
+ .path = 0, /* XXX */
+ .rate = 8000,
+ .dir = Q6_TX,
+ .codec = Q6_ECODEC_TX,
+ .hw = Q6_HW_BT_SCO,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_I2S_SPKR,
+ .cad_id = CAD_HW_DEVICE_ID_I2S_RX,
+ .path = 0, /* XXX */
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_SDAC_RX,
+ .hw = Q6_HW_SPEAKER,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_I2S_MIC,
+ .cad_id = CAD_HW_DEVICE_ID_I2S_TX,
+ .path = 0, /* XXX */
+ .rate = 16000,
+ .dir = Q6_TX,
+ .codec = Q6_SDAC_TX,
+ .hw = Q6_HW_SPEAKER,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_AUXPCM_RX,
+ .cad_id = CAD_HW_DEVICE_ID_BT_SCO_SPKR,
+ .path = 0, /* XXX */
+ .rate = 8000,
+ .dir = Q6_RX,
+ .codec = Q6_ECODEC_RX,
+ .hw = Q6_HW_BT_SCO,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_AUXPCM_TX,
+ .cad_id = CAD_HW_DEVICE_ID_BT_SCO_MIC,
+ .path = 0, /* XXX */
+ .rate = 8000,
+ .dir = Q6_TX,
+ .codec = Q6_ECODEC_TX,
+ .hw = Q6_HW_BT_SCO,
+ },
+ {
+ .id = 0,
+ .cad_id = 0,
+ .path = 0,
+ .rate = 8000,
+ .dir = 0,
+ .codec = Q6_CODEC_NONE,
+ .hw = 0,
+ },
+};
+
diff --git a/arch/arm/mach-msm/qdsp6/qcelp_in.c b/arch/arm/mach-msm/qdsp6/qcelp_in.c
new file mode 100644
index 0000000..ca0ab1a
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/qcelp_in.c
@@ -0,0 +1,475 @@
+/*
+ * Copyright (C) 2009 Google, Inc.
+ * Copyright (C) 2009 HTC Corporation
+ * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Author: Brian Swetland <swetland@google.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/slab.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+
+#include <linux/msm_audio_qcp.h>
+#include <mach/msm_qdsp6_audio.h>
+#include "dal_audio_format.h"
+#include <mach/debug_mm.h>
+
+#define QCELP_FC_BUFF_CNT 10
+#define QCELP_READ_TIMEOUT 2000
+struct qcelp_fc_buff {
+ struct mutex lock;
+ int empty;
+ void *data;
+ int size;
+ int actual_size;
+};
+
+struct qcelp_fc {
+ struct task_struct *task;
+ wait_queue_head_t fc_wq;
+ struct qcelp_fc_buff fc_buff[QCELP_FC_BUFF_CNT];
+ int buff_index;
+};
+
+struct qcelp {
+ struct mutex lock;
+ struct msm_audio_qcelp_enc_config cfg;
+ struct msm_audio_stream_config str_cfg;
+ struct audio_client *audio_client;
+ struct msm_voicerec_mode voicerec_mode;
+ struct qcelp_fc *qcelp_fc;
+};
+
+
+static int q6_qcelp_flowcontrol(void *data)
+{
+ struct audio_client *ac;
+ struct audio_buffer *ab;
+ struct qcelp *qcelp = data;
+ int buff_index = 0;
+ int xfer = 0;
+ struct qcelp_fc *fc;
+
+
+ ac = qcelp->audio_client;
+ fc = qcelp->qcelp_fc;
+ if (!ac) {
+ pr_err("[%s:%s] audio_client is NULL\n", __MM_FILE__, __func__);
+ return 0;
+ }
+
+ while (!kthread_should_stop()) {
+ ab = ac->buf + ac->cpu_buf;
+ if (ab->used)
+ wait_event(ac->wait, (ab->used == 0));
+
+ pr_debug("[%s:%s] ab->data = %p, cpu_buf = %d", __MM_FILE__,
+ __func__, ab->data, ac->cpu_buf);
+ xfer = ab->actual_size;
+
+
+ mutex_lock(&(fc->fc_buff[buff_index].lock));
+ if (!fc->fc_buff[buff_index].empty) {
+ pr_err("[%s:%s] flow control buffer[%d] not read!\n",
+ __MM_FILE__, __func__, buff_index);
+ }
+
+ if (fc->fc_buff[buff_index].size < xfer) {
+ pr_err("[%s:%s] buffer %d too small\n", __MM_FILE__,
+ __func__, buff_index);
+ memcpy(fc->fc_buff[buff_index].data, ab->data,
+ fc->fc_buff[buff_index].size);
+ fc->fc_buff[buff_index].empty = 0;
+ fc->fc_buff[buff_index].actual_size =
+ fc->fc_buff[buff_index].size;
+ } else {
+ memcpy(fc->fc_buff[buff_index].data, ab->data, xfer);
+ fc->fc_buff[buff_index].empty = 0;
+ fc->fc_buff[buff_index].actual_size = xfer;
+ }
+ mutex_unlock(&(fc->fc_buff[buff_index].lock));
+ /*wake up client, if any*/
+ wake_up(&fc->fc_wq);
+
+ buff_index++;
+ if (buff_index >= QCELP_FC_BUFF_CNT)
+ buff_index = 0;
+
+ ab->used = 1;
+
+ q6audio_read(ac, ab);
+ ac->cpu_buf ^= 1;
+ }
+
+ return 0;
+}
+static long q6_qcelp_in_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct qcelp *qcelp = file->private_data;
+ int rc = 0;
+ int i = 0;
+ struct qcelp_fc *fc;
+ int size = 0;
+
+ mutex_lock(&qcelp->lock);
+ switch (cmd) {
+ case AUDIO_SET_VOLUME:
+ pr_debug("[%s:%s] SET_VOLUME\n", __MM_FILE__, __func__);
+ break;
+ case AUDIO_GET_STATS:
+ {
+ struct msm_audio_stats stats;
+ pr_debug("[%s:%s] GET_STATS\n", __MM_FILE__, __func__);
+ memset(&stats, 0, sizeof(stats));
+ if (copy_to_user((void *) arg, &stats,
+ sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ }
+ case AUDIO_START:
+ {
+ uint32_t acdb_id;
+ pr_debug("[%s:%s] AUDIO_START\n", __MM_FILE__, __func__);
+ if (arg == 0) {
+ acdb_id = 0;
+ } else {
+ if (copy_from_user(&acdb_id,
+ (void *) arg, sizeof(acdb_id))) {
+ rc = -EFAULT;
+ break;
+ }
+ }
+ if (qcelp->audio_client) {
+ pr_err("[%s:%s] active session already existing\n",
+ __MM_FILE__, __func__);
+ rc = -EBUSY;
+ break;
+ } else {
+ qcelp->audio_client = q6audio_open_qcp(
+ qcelp->str_cfg.buffer_size,
+ qcelp->cfg.min_bit_rate,
+ qcelp->cfg.max_bit_rate,
+ qcelp->voicerec_mode.rec_mode,
+ ADSP_AUDIO_FORMAT_V13K_FS,
+ acdb_id);
+
+ if (!qcelp->audio_client) {
+ pr_err("[%s:%s] qcelp open session failed\n",
+ __MM_FILE__, __func__);
+ kfree(qcelp);
+ rc = -ENOMEM;
+ break;
+ }
+ }
+
+ /*allocate flow control buffers*/
+ fc = qcelp->qcelp_fc;
+ size = qcelp->str_cfg.buffer_size;
+ for (i = 0; i < QCELP_FC_BUFF_CNT; ++i) {
+ mutex_init(&(fc->fc_buff[i].lock));
+ fc->fc_buff[i].empty = 1;
+ fc->fc_buff[i].data = kmalloc(size, GFP_KERNEL);
+ if (fc->fc_buff[i].data == NULL) {
+ pr_err("[%s:%s] No memory for FC buffers\n",
+ __MM_FILE__, __func__);
+ rc = -ENOMEM;
+ goto fc_fail;
+ }
+ fc->fc_buff[i].size = size;
+ fc->fc_buff[i].actual_size = 0;
+ }
+
+ /*create flow control thread*/
+ fc->task = kthread_run(q6_qcelp_flowcontrol,
+ qcelp, "qcelp_flowcontrol");
+ if (IS_ERR(fc->task)) {
+ rc = PTR_ERR(fc->task);
+ pr_err("[%s:%s] error creating flow control thread\n",
+ __MM_FILE__, __func__);
+ goto fc_fail;
+ }
+ break;
+fc_fail:
+ /*free flow control buffers*/
+ --i;
+ for (; i >= 0; i--) {
+ kfree(fc->fc_buff[i].data);
+ fc->fc_buff[i].data = NULL;
+ }
+ break;
+ }
+ case AUDIO_STOP:
+ pr_debug("[%s:%s] AUDIO_STOP\n", __MM_FILE__, __func__);
+ break;
+ case AUDIO_FLUSH:
+ break;
+ case AUDIO_SET_INCALL: {
+ pr_debug("[%s:%s] SET_INCALL\n", __MM_FILE__, __func__);
+ if (copy_from_user(&qcelp->voicerec_mode,
+ (void *)arg, sizeof(struct msm_voicerec_mode)))
+ rc = -EFAULT;
+
+ if (qcelp->voicerec_mode.rec_mode != AUDIO_FLAG_READ
+ && qcelp->voicerec_mode.rec_mode !=
+ AUDIO_FLAG_INCALL_MIXED) {
+ qcelp->voicerec_mode.rec_mode = AUDIO_FLAG_READ;
+ pr_err("[%s:%s] Invalid rec_mode\n", __MM_FILE__,
+ __func__);
+ rc = -EINVAL;
+ }
+ break;
+ }
+ case AUDIO_GET_STREAM_CONFIG:
+ if (copy_to_user((void *)arg, &qcelp->str_cfg,
+ sizeof(struct msm_audio_stream_config)))
+ rc = -EFAULT;
+ pr_debug("[%s:%s] GET_STREAM_CONFIG: buffsz=%d, buffcnt=%d\n",
+ __MM_FILE__, __func__, qcelp->str_cfg.buffer_size,
+ qcelp->str_cfg.buffer_count);
+ break;
+ case AUDIO_SET_STREAM_CONFIG:
+ if (copy_from_user(&qcelp->str_cfg, (void *)arg,
+ sizeof(struct msm_audio_stream_config))) {
+ rc = -EFAULT;
+ break;
+ }
+ pr_debug("[%s:%s] SET_STREAM_CONFIG: buffsz=%d, buffcnt=%d\n",
+ __MM_FILE__, __func__, qcelp->str_cfg.buffer_size,
+ qcelp->str_cfg.buffer_count);
+
+ if (qcelp->str_cfg.buffer_size < 35) {
+ pr_err("[%s:%s] Buffer size too small\n", __MM_FILE__,
+ __func__);
+ rc = -EINVAL;
+ break;
+ }
+
+ if (qcelp->str_cfg.buffer_count != 2)
+ pr_info("[%s:%s] Buffer count set to 2\n", __MM_FILE__,
+ __func__);
+ break;
+ case AUDIO_SET_QCELP_ENC_CONFIG:
+ if (copy_from_user(&qcelp->cfg, (void *) arg,
+ sizeof(struct msm_audio_qcelp_enc_config)))
+ rc = -EFAULT;
+ pr_debug("[%s:%s] SET_QCELP_ENC_CONFIG\n", __MM_FILE__,
+ __func__);
+
+ if (qcelp->cfg.min_bit_rate > 4 ||
+ qcelp->cfg.min_bit_rate < 1) {
+
+ pr_err("[%s:%s] invalid min bitrate\n", __MM_FILE__,
+ __func__);
+ rc = -EINVAL;
+ }
+ if (qcelp->cfg.max_bit_rate > 4 ||
+ qcelp->cfg.max_bit_rate < 1) {
+
+ pr_err("[%s:%s] invalid max bitrate\n", __MM_FILE__,
+ __func__);
+ rc = -EINVAL;
+ }
+
+ break;
+ case AUDIO_GET_QCELP_ENC_CONFIG:
+ if (copy_to_user((void *) arg, &qcelp->cfg,
+ sizeof(struct msm_audio_qcelp_enc_config)))
+ rc = -EFAULT;
+ pr_debug("[%s:%s] GET_QCELP_ENC_CONFIG\n", __MM_FILE__,
+ __func__);
+ break;
+
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&qcelp->lock);
+ pr_debug("[%s:%s] rc = %d\n", __MM_FILE__, __func__, rc);
+ return rc;
+}
+
+static int q6_qcelp_in_open(struct inode *inode, struct file *file)
+{
+ struct qcelp *qcelp;
+ struct qcelp_fc *fc;
+ int i;
+ pr_info("[%s:%s] open\n", __MM_FILE__, __func__);
+ qcelp = kmalloc(sizeof(struct qcelp), GFP_KERNEL);
+ if (qcelp == NULL) {
+ pr_err("[%s:%s] Could not allocate memory for qcelp driver\n",
+ __MM_FILE__, __func__);
+ return -ENOMEM;
+ }
+
+ mutex_init(&qcelp->lock);
+ file->private_data = qcelp;
+ qcelp->audio_client = NULL;
+ qcelp->str_cfg.buffer_size = 35;
+ qcelp->str_cfg.buffer_count = 2;
+ qcelp->cfg.cdma_rate = CDMA_RATE_FULL;
+ qcelp->cfg.min_bit_rate = 1;
+ qcelp->cfg.max_bit_rate = 4;
+ qcelp->voicerec_mode.rec_mode = AUDIO_FLAG_READ;
+
+ qcelp->qcelp_fc = kmalloc(sizeof(struct qcelp_fc), GFP_KERNEL);
+ if (qcelp->qcelp_fc == NULL) {
+ pr_err("[%s:%s] Could not allocate memory for qcelp_fc\n",
+ __MM_FILE__, __func__);
+ kfree(qcelp);
+ return -ENOMEM;
+ }
+ fc = qcelp->qcelp_fc;
+ fc->task = NULL;
+ fc->buff_index = 0;
+ for (i = 0; i < QCELP_FC_BUFF_CNT; ++i) {
+ fc->fc_buff[i].data = NULL;
+ fc->fc_buff[i].size = 0;
+ fc->fc_buff[i].actual_size = 0;
+ }
+ /*initialize wait queue head*/
+ init_waitqueue_head(&fc->fc_wq);
+ return 0;
+}
+
+static ssize_t q6_qcelp_in_read(struct file *file, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio_client *ac;
+ const char __user *start = buf;
+ struct qcelp *qcelp = file->private_data;
+ struct qcelp_fc *fc;
+ int xfer = 0;
+ int res = 0;
+
+ pr_debug("[%s:%s] count = %d\n", __MM_FILE__, __func__, count);
+ mutex_lock(&qcelp->lock);
+ ac = qcelp->audio_client;
+ if (!ac) {
+ res = -ENODEV;
+ goto fail;
+ }
+ fc = qcelp->qcelp_fc;
+ while (count > xfer) {
+ /*wait for buffer to full*/
+ if (fc->fc_buff[fc->buff_index].empty != 0) {
+ res = wait_event_interruptible_timeout(fc->fc_wq,
+ (fc->fc_buff[fc->buff_index].empty == 0),
+ msecs_to_jiffies(QCELP_READ_TIMEOUT));
+
+ pr_debug("[%s:%s] buff_index = %d\n", __MM_FILE__,
+ __func__, fc->buff_index);
+ if (res == 0) {
+ pr_err("[%s:%s] Timeout!\n", __MM_FILE__,
+ __func__);
+ res = -ETIMEDOUT;
+ goto fail;
+ } else if (res < 0) {
+ pr_err("[%s:%s] Returning on Interrupt\n",
+ __MM_FILE__, __func__);
+ goto fail;
+ }
+ }
+ /*lock the buffer*/
+ mutex_lock(&(fc->fc_buff[fc->buff_index].lock));
+ xfer = fc->fc_buff[fc->buff_index].actual_size;
+
+ if (xfer > count) {
+ mutex_unlock(&(fc->fc_buff[fc->buff_index].lock));
+ pr_err("[%s:%s] read failed! byte count too small\n",
+ __MM_FILE__, __func__);
+ res = -EINVAL;
+ goto fail;
+ }
+
+ if (copy_to_user(buf, fc->fc_buff[fc->buff_index].data, xfer)) {
+ mutex_unlock(&(fc->fc_buff[fc->buff_index].lock));
+ pr_err("[%s:%s] copy_to_user failed at index %d\n",
+ __MM_FILE__, __func__, fc->buff_index);
+ res = -EFAULT;
+ goto fail;
+ }
+ buf += xfer;
+ count -= xfer;
+
+ fc->fc_buff[fc->buff_index].empty = 1;
+ fc->fc_buff[fc->buff_index].actual_size = 0;
+
+ mutex_unlock(&(fc->fc_buff[fc->buff_index].lock));
+ ++(fc->buff_index);
+ if (fc->buff_index >= QCELP_FC_BUFF_CNT)
+ fc->buff_index = 0;
+ }
+ res = buf - start;
+
+fail:
+ mutex_unlock(&qcelp->lock);
+
+ return res;
+}
+
+static int q6_qcelp_in_release(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+ struct qcelp *qcelp = file->private_data;
+ int i = 0;
+ struct qcelp_fc *fc;
+
+ mutex_lock(&qcelp->lock);
+ fc = qcelp->qcelp_fc;
+ kthread_stop(fc->task);
+ fc->task = NULL;
+
+ /*free flow control buffers*/
+ for (i = 0; i < QCELP_FC_BUFF_CNT; ++i) {
+ kfree(fc->fc_buff[i].data);
+ fc->fc_buff[i].data = NULL;
+ }
+ kfree(fc);
+
+ if (qcelp->audio_client)
+ rc = q6audio_close(qcelp->audio_client);
+ mutex_unlock(&qcelp->lock);
+ kfree(qcelp);
+ pr_info("[%s:%s] release\n", __MM_FILE__, __func__);
+ return rc;
+}
+
+static const struct file_operations q6_qcelp_in_fops = {
+ .owner = THIS_MODULE,
+ .open = q6_qcelp_in_open,
+ .read = q6_qcelp_in_read,
+ .release = q6_qcelp_in_release,
+ .unlocked_ioctl = q6_qcelp_in_ioctl,
+};
+
+struct miscdevice q6_qcelp_in_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_qcelp_in",
+ .fops = &q6_qcelp_in_fops,
+};
+
+static int __init q6_qcelp_in_init(void)
+{
+ return misc_register(&q6_qcelp_in_misc);
+}
+
+device_initcall(q6_qcelp_in_init);
diff --git a/arch/arm/mach-msm/qdsp6/routing.c b/arch/arm/mach-msm/qdsp6/routing.c
new file mode 100644
index 0000000..f6533a4
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/routing.c
@@ -0,0 +1,78 @@
+/* arch/arm/mach-msm/qdsp6/routing.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Author: Brian Swetland <swetland@google.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/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <mach/debug_mm.h>
+
+extern int q6audio_set_route(const char *name);
+
+static int q6_open(struct inode *inode, struct file *file)
+{
+ pr_debug("[%s:%s]\n", __MM_FILE__, __func__);
+ return 0;
+}
+
+static ssize_t q6_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ char cmd[32];
+
+ pr_debug("[%s:%s] count = %d", __MM_FILE__, __func__, count);
+ if (count >= sizeof(cmd)) {
+ pr_err("[%s:%s] invalid count %d\n", __MM_FILE__,
+ __func__, count);
+ return -EINVAL;
+ }
+ if (copy_from_user(cmd, buf, count))
+ return -EFAULT;
+ cmd[count] = 0;
+
+ if ((count > 1) && (cmd[count-1] == '\n'))
+ cmd[count-1] = 0;
+
+ q6audio_set_route(cmd);
+
+ return count;
+}
+
+static int q6_release(struct inode *inode, struct file *file)
+{
+ pr_debug("[%s:%s]\n", __MM_FILE__, __func__);
+ return 0;
+}
+
+static struct file_operations q6_fops = {
+ .owner = THIS_MODULE,
+ .open = q6_open,
+ .write = q6_write,
+ .release = q6_release,
+};
+
+static struct miscdevice q6_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_audio_route",
+ .fops = &q6_fops,
+};
+
+
+static int __init q6_init(void) {
+ return misc_register(&q6_misc);
+}
+
+device_initcall(q6_init);