summaryrefslogtreecommitdiff
path: root/drivers/staging/dream/qdsp5/audpp.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/dream/qdsp5/audpp.c')
-rw-r--r--drivers/staging/dream/qdsp5/audpp.c429
1 files changed, 429 insertions, 0 deletions
diff --git a/drivers/staging/dream/qdsp5/audpp.c b/drivers/staging/dream/qdsp5/audpp.c
new file mode 100644
index 000000000000..d06556eda316
--- /dev/null
+++ b/drivers/staging/dream/qdsp5/audpp.c
@@ -0,0 +1,429 @@
+
+/* arch/arm/mach-msm/qdsp5/audpp.c
+ *
+ * common code to deal with the AUDPP dsp task (audio postproc)
+ *
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+
+#include "audmgr.h"
+
+#include <mach/qdsp5/qdsp5audppcmdi.h>
+#include <mach/qdsp5/qdsp5audppmsg.h>
+
+/* for queue ids - should be relative to module number*/
+#include "adsp.h"
+
+#include "evlog.h"
+
+
+enum {
+ EV_NULL,
+ EV_ENABLE,
+ EV_DISABLE,
+ EV_EVENT,
+ EV_DATA,
+};
+
+static const char *dsp_log_strings[] = {
+ "NULL",
+ "ENABLE",
+ "DISABLE",
+ "EVENT",
+ "DATA",
+};
+
+DECLARE_LOG(dsp_log, 64, dsp_log_strings);
+
+static int __init _dsp_log_init(void)
+{
+ return ev_log_init(&dsp_log);
+}
+module_init(_dsp_log_init);
+#define LOG(id,arg) ev_log_write(&dsp_log, id, arg)
+
+static DEFINE_MUTEX(audpp_lock);
+
+#define CH_COUNT 5
+#define AUDPP_CLNT_MAX_COUNT 6
+#define AUDPP_AVSYNC_INFO_SIZE 7
+
+struct audpp_state {
+ struct msm_adsp_module *mod;
+ audpp_event_func func[AUDPP_CLNT_MAX_COUNT];
+ void *private[AUDPP_CLNT_MAX_COUNT];
+ struct mutex *lock;
+ unsigned open_count;
+ unsigned enabled;
+
+ /* which channels are actually enabled */
+ unsigned avsync_mask;
+
+ /* flags, 48 bits sample/bytes counter per channel */
+ uint16_t avsync[CH_COUNT * AUDPP_CLNT_MAX_COUNT + 1];
+};
+
+struct audpp_state the_audpp_state = {
+ .lock = &audpp_lock,
+};
+
+int audpp_send_queue1(void *cmd, unsigned len)
+{
+ return msm_adsp_write(the_audpp_state.mod,
+ QDSP_uPAudPPCmd1Queue, cmd, len);
+}
+EXPORT_SYMBOL(audpp_send_queue1);
+
+int audpp_send_queue2(void *cmd, unsigned len)
+{
+ return msm_adsp_write(the_audpp_state.mod,
+ QDSP_uPAudPPCmd2Queue, cmd, len);
+}
+EXPORT_SYMBOL(audpp_send_queue2);
+
+int audpp_send_queue3(void *cmd, unsigned len)
+{
+ return msm_adsp_write(the_audpp_state.mod,
+ QDSP_uPAudPPCmd3Queue, cmd, len);
+}
+EXPORT_SYMBOL(audpp_send_queue3);
+
+static int audpp_dsp_config(int enable)
+{
+ audpp_cmd_cfg cmd;
+
+ cmd.cmd_id = AUDPP_CMD_CFG;
+ cmd.cfg = enable ? AUDPP_CMD_CFG_ENABLE : AUDPP_CMD_CFG_SLEEP;
+
+ return audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static void audpp_broadcast(struct audpp_state *audpp, unsigned id,
+ uint16_t *msg)
+{
+ unsigned n;
+ for (n = 0; n < AUDPP_CLNT_MAX_COUNT; n++) {
+ if (audpp->func[n])
+ audpp->func[n] (audpp->private[n], id, msg);
+ }
+}
+
+static void audpp_notify_clnt(struct audpp_state *audpp, unsigned clnt_id,
+ unsigned id, uint16_t *msg)
+{
+ if (clnt_id < AUDPP_CLNT_MAX_COUNT && audpp->func[clnt_id])
+ audpp->func[clnt_id] (audpp->private[clnt_id], id, msg);
+}
+
+static void audpp_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent)(void *ptr, size_t len))
+{
+ struct audpp_state *audpp = data;
+ uint16_t msg[8];
+
+ if (id == AUDPP_MSG_AVSYNC_MSG) {
+ getevent(audpp->avsync, sizeof(audpp->avsync));
+
+ /* mask off any channels we're not watching to avoid
+ * cases where we might get one last update after
+ * disabling avsync and end up in an odd state when
+ * we next read...
+ */
+ audpp->avsync[0] &= audpp->avsync_mask;
+ return;
+ }
+
+ getevent(msg, sizeof(msg));
+
+ LOG(EV_EVENT, (id << 16) | msg[0]);
+ LOG(EV_DATA, (msg[1] << 16) | msg[2]);
+
+ switch (id) {
+ case AUDPP_MSG_STATUS_MSG:{
+ unsigned cid = msg[0];
+ pr_info("audpp: status %d %d %d\n", cid, msg[1],
+ msg[2]);
+ if ((cid < 5) && audpp->func[cid])
+ audpp->func[cid] (audpp->private[cid], id, msg);
+ break;
+ }
+ case AUDPP_MSG_HOST_PCM_INTF_MSG:
+ if (audpp->func[5])
+ audpp->func[5] (audpp->private[5], id, msg);
+ break;
+ case AUDPP_MSG_PCMDMAMISSED:
+ pr_err("audpp: DMA missed obj=%x\n", msg[0]);
+ break;
+ case AUDPP_MSG_CFG_MSG:
+ if (msg[0] == AUDPP_MSG_ENA_ENA) {
+ pr_info("audpp: ENABLE\n");
+ audpp->enabled = 1;
+ audpp_broadcast(audpp, id, msg);
+ } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+ pr_info("audpp: DISABLE\n");
+ audpp->enabled = 0;
+ audpp_broadcast(audpp, id, msg);
+ } else {
+ pr_err("audpp: invalid config msg %d\n", msg[0]);
+ }
+ break;
+ case AUDPP_MSG_ROUTING_ACK:
+ audpp_broadcast(audpp, id, msg);
+ break;
+ case AUDPP_MSG_FLUSH_ACK:
+ audpp_notify_clnt(audpp, msg[0], id, msg);
+ break;
+ default:
+ pr_info("audpp: unhandled msg id %x\n", id);
+ }
+}
+
+static struct msm_adsp_ops adsp_ops = {
+ .event = audpp_dsp_event,
+};
+
+static void audpp_fake_event(struct audpp_state *audpp, int id,
+ unsigned event, unsigned arg)
+{
+ uint16_t msg[1];
+ msg[0] = arg;
+ audpp->func[id] (audpp->private[id], event, msg);
+}
+
+int audpp_enable(int id, audpp_event_func func, void *private)
+{
+ struct audpp_state *audpp = &the_audpp_state;
+ int res = 0;
+
+ if (id < -1 || id > 4)
+ return -EINVAL;
+
+ if (id == -1)
+ id = 5;
+
+ mutex_lock(audpp->lock);
+ if (audpp->func[id]) {
+ res = -EBUSY;
+ goto out;
+ }
+
+ audpp->func[id] = func;
+ audpp->private[id] = private;
+
+ LOG(EV_ENABLE, 1);
+ if (audpp->open_count++ == 0) {
+ pr_info("audpp: enable\n");
+ res = msm_adsp_get("AUDPPTASK", &audpp->mod, &adsp_ops, audpp);
+ if (res < 0) {
+ pr_err("audpp: cannot open AUDPPTASK\n");
+ audpp->open_count = 0;
+ audpp->func[id] = NULL;
+ audpp->private[id] = NULL;
+ goto out;
+ }
+ LOG(EV_ENABLE, 2);
+ msm_adsp_enable(audpp->mod);
+ audpp_dsp_config(1);
+ } else {
+ unsigned long flags;
+ local_irq_save(flags);
+ if (audpp->enabled)
+ audpp_fake_event(audpp, id,
+ AUDPP_MSG_CFG_MSG, AUDPP_MSG_ENA_ENA);
+ local_irq_restore(flags);
+ }
+
+ res = 0;
+out:
+ mutex_unlock(audpp->lock);
+ return res;
+}
+EXPORT_SYMBOL(audpp_enable);
+
+void audpp_disable(int id, void *private)
+{
+ struct audpp_state *audpp = &the_audpp_state;
+ unsigned long flags;
+
+ if (id < -1 || id > 4)
+ return;
+
+ if (id == -1)
+ id = 5;
+
+ mutex_lock(audpp->lock);
+ LOG(EV_DISABLE, 1);
+ if (!audpp->func[id])
+ goto out;
+ if (audpp->private[id] != private)
+ goto out;
+
+ local_irq_save(flags);
+ audpp_fake_event(audpp, id, AUDPP_MSG_CFG_MSG, AUDPP_MSG_ENA_DIS);
+ audpp->func[id] = NULL;
+ audpp->private[id] = NULL;
+ local_irq_restore(flags);
+
+ if (--audpp->open_count == 0) {
+ pr_info("audpp: disable\n");
+ LOG(EV_DISABLE, 2);
+ audpp_dsp_config(0);
+ msm_adsp_disable(audpp->mod);
+ msm_adsp_put(audpp->mod);
+ audpp->mod = NULL;
+ }
+out:
+ mutex_unlock(audpp->lock);
+}
+EXPORT_SYMBOL(audpp_disable);
+
+#define BAD_ID(id) ((id < 0) || (id >= CH_COUNT))
+
+void audpp_avsync(int id, unsigned rate)
+{
+ unsigned long flags;
+ audpp_cmd_avsync cmd;
+
+ if (BAD_ID(id))
+ return;
+
+ local_irq_save(flags);
+ if (rate)
+ the_audpp_state.avsync_mask |= (1 << id);
+ else
+ the_audpp_state.avsync_mask &= (~(1 << id));
+ the_audpp_state.avsync[0] &= the_audpp_state.avsync_mask;
+ local_irq_restore(flags);
+
+ cmd.cmd_id = AUDPP_CMD_AVSYNC;
+ cmd.object_number = id;
+ cmd.interrupt_interval_lsw = rate;
+ cmd.interrupt_interval_msw = rate >> 16;
+ audpp_send_queue1(&cmd, sizeof(cmd));
+}
+EXPORT_SYMBOL(audpp_avsync);
+
+unsigned audpp_avsync_sample_count(int id)
+{
+ uint16_t *avsync = the_audpp_state.avsync;
+ unsigned val;
+ unsigned long flags;
+ unsigned mask;
+
+ if (BAD_ID(id))
+ return 0;
+
+ mask = 1 << id;
+ id = id * AUDPP_AVSYNC_INFO_SIZE + 2;
+ local_irq_save(flags);
+ if (avsync[0] & mask)
+ val = (avsync[id] << 16) | avsync[id + 1];
+ else
+ val = 0;
+ local_irq_restore(flags);
+
+ return val;
+}
+EXPORT_SYMBOL(audpp_avsync_sample_count);
+
+unsigned audpp_avsync_byte_count(int id)
+{
+ uint16_t *avsync = the_audpp_state.avsync;
+ unsigned val;
+ unsigned long flags;
+ unsigned mask;
+
+ if (BAD_ID(id))
+ return 0;
+
+ mask = 1 << id;
+ id = id * AUDPP_AVSYNC_INFO_SIZE + 5;
+ local_irq_save(flags);
+ if (avsync[0] & mask)
+ val = (avsync[id] << 16) | avsync[id + 1];
+ else
+ val = 0;
+ local_irq_restore(flags);
+
+ return val;
+}
+EXPORT_SYMBOL(audpp_avsync_byte_count);
+
+#define AUDPP_CMD_CFG_OBJ_UPDATE 0x8000
+#define AUDPP_CMD_VOLUME_PAN 0
+
+int audpp_set_volume_and_pan(unsigned id, unsigned volume, int pan)
+{
+ /* cmd, obj_cfg[7], cmd_type, volume, pan */
+ uint16_t cmd[11];
+
+ if (id > 6)
+ return -EINVAL;
+
+ memset(cmd, 0, sizeof(cmd));
+ cmd[0] = AUDPP_CMD_CFG_OBJECT_PARAMS;
+ cmd[1 + id] = AUDPP_CMD_CFG_OBJ_UPDATE;
+ cmd[8] = AUDPP_CMD_VOLUME_PAN;
+ cmd[9] = volume;
+ cmd[10] = pan;
+
+ return audpp_send_queue3(cmd, sizeof(cmd));
+}
+EXPORT_SYMBOL(audpp_set_volume_and_pan);
+
+int audpp_pause(unsigned id, int pause)
+{
+ /* pause 1 = pause 0 = resume */
+ u16 pause_cmd[AUDPP_CMD_DEC_CTRL_LEN / sizeof(unsigned short)];
+
+ if (id >= CH_COUNT)
+ return -EINVAL;
+
+ memset(pause_cmd, 0, sizeof(pause_cmd));
+
+ pause_cmd[0] = AUDPP_CMD_DEC_CTRL;
+ if (pause == 1)
+ pause_cmd[1 + id] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_PAUSE_V;
+ else if (pause == 0)
+ pause_cmd[1 + id] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_RESUME_V;
+ else
+ return -EINVAL;
+
+ return audpp_send_queue1(pause_cmd, sizeof(pause_cmd));
+}
+EXPORT_SYMBOL(audpp_pause);
+
+int audpp_flush(unsigned id)
+{
+ u16 flush_cmd[AUDPP_CMD_DEC_CTRL_LEN / sizeof(unsigned short)];
+
+ if (id >= CH_COUNT)
+ return -EINVAL;
+
+ memset(flush_cmd, 0, sizeof(flush_cmd));
+
+ flush_cmd[0] = AUDPP_CMD_DEC_CTRL;
+ flush_cmd[1 + id] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_FLUSH_V;
+
+ return audpp_send_queue1(flush_cmd, sizeof(flush_cmd));
+}
+EXPORT_SYMBOL(audpp_flush);