| /* arch/arm/mach-msm/qdsp5/audrec.c |
| * |
| * common code to deal with the AUDREC dsp task (audio recording) |
| * |
| * Copyright (c) 2009,2012 The Linux Foundation. All rights reserved. |
| * |
| * Based on the audpp layer in arch/arm/mach-msm/qdsp5/audpp.c |
| * |
| * Copyright (C) 2008 Google, Inc. |
| * Copyright (C) 2008 HTC Corporation |
| * |
| * This software is licensed under the terms of the GNU General Public |
| * License version 2, as published by the Free Software Foundation, and |
| * may be copied, distributed, and modified under those terms. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| * |
| * See the GNU General Public License for more details. |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, you can find it at http://www.fsf.org. |
| * |
| */ |
| |
| #include <linux/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 <mach/qdsp5/qdsp5audreccmdi.h> |
| #include <mach/qdsp5/qdsp5audrecmsg.h> |
| #include <mach/qdsp5/qdsp5audpreproc.h> |
| |
| #include "audmgr.h" |
| #include <mach/debug_mm.h> |
| |
| static DEFINE_MUTEX(audrec_lock); |
| |
| #define MAX_ENC_COUNT 8 /* Max encoder supported */ |
| |
| #define ENC_SESSION_FREE 0 |
| #define ENC_SESSION_ACTIVE 1 |
| |
| struct enc_session { |
| unsigned enc_type; /* Param to identify type of encoder */ |
| unsigned audrec_obj_idx; /* Param to identify REC_OBJ or Session ID */ |
| audrec_event_func event_func; /* Event Call back |
| routine for the encoder */ |
| void *private; /* private data element passed as |
| part of Event Call back routine */ |
| unsigned state; /* Current state of the encoder session , |
| free, active*/ |
| }; |
| |
| struct audrec_state { |
| struct msm_adsp_module *audrec_mod; |
| struct enc_session enc_session[MAX_ENC_COUNT]; |
| struct mutex *lock; |
| unsigned enc_count; |
| }; |
| |
| struct audrec_state the_audrec_state = { |
| .lock = &audrec_lock, |
| }; |
| |
| int audrectask_send_cmdqueue(void *cmd, unsigned len) |
| { |
| return msm_adsp_write(the_audrec_state.audrec_mod, |
| QDSP_uPAudRecCmdQueue, cmd, len); |
| } |
| EXPORT_SYMBOL(audrectask_send_cmdqueue); |
| |
| int audrectask_send_bitstreamqueue(void *cmd, unsigned len) |
| { |
| return msm_adsp_write(the_audrec_state.audrec_mod, |
| QDSP_uPAudRecBitStreamQueue, cmd, len); |
| } |
| EXPORT_SYMBOL(audrectask_send_bitstreamqueue); |
| |
| static void audrectask_dsp_event(void *data, unsigned id, size_t len, |
| void (*getevent)(void *ptr, size_t len)) |
| { |
| struct audrec_state *audrec = data; |
| int cnt; |
| uint16_t msg[5]; /* Max size of message */ |
| getevent(msg, len); |
| |
| switch (id) { |
| case AUDREC_MSG_CMD_CFG_DONE_MSG: { |
| MM_DBG("CMD CFG DONE %x\n", msg[1]); |
| if (msg[0] & AUDREC_MSG_CFG_DONE_ENC_ENA) { |
| for (cnt = 0; cnt < MAX_ENC_COUNT ; cnt++) { |
| if (audrec->enc_session[cnt].enc_type == |
| (msg[0] & AUDREC_CMD_ENC_TYPE_MASK)) { |
| audrec->enc_session[cnt].audrec_obj_idx |
| = msg[1]; |
| audrec->enc_session[cnt].event_func( |
| audrec->enc_session[cnt].private, id, |
| msg); |
| break; |
| } |
| } |
| } else { |
| for (cnt = 0; cnt < MAX_ENC_COUNT ; cnt++) { |
| if (audrec->enc_session[cnt].enc_type == |
| (msg[0] & AUDREC_CMD_ENC_TYPE_MASK)) { |
| audrec->enc_session[cnt].event_func( |
| audrec->enc_session[cnt].private, id, |
| msg); |
| audrec->enc_session[cnt].audrec_obj_idx |
| = 0xFFFFFFFF; |
| audrec->enc_session[cnt].state |
| = ENC_SESSION_FREE; |
| audrec->enc_session[cnt].enc_type |
| = 0xFFFFFFFF; |
| audrec->enc_session[cnt].event_func |
| = NULL; |
| audrec->enc_session[cnt].private |
| = NULL; |
| break; |
| } |
| } |
| } |
| break; |
| } |
| case AUDREC_MSG_CMD_AREC_MEM_CFG_DONE_MSG: { |
| MM_DBG("CMD AREC MEM CFG DONE %x\n", msg[0]); |
| for (cnt = 0; cnt < MAX_ENC_COUNT ; cnt++) { |
| if (audrec->enc_session[cnt].audrec_obj_idx == |
| msg[0]) { |
| audrec->enc_session[cnt].event_func( |
| audrec->enc_session[cnt].private, id, msg); |
| break; |
| } |
| } |
| break; |
| } |
| case AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG: { |
| MM_DBG("CMD AREC PARAM CFG DONE %x\n", msg[0]); |
| for (cnt = 0; cnt < MAX_ENC_COUNT ; cnt++) { |
| if (audrec->enc_session[cnt].audrec_obj_idx == |
| msg[0]) { |
| audrec->enc_session[cnt].event_func( |
| audrec->enc_session[cnt].private, id, msg); |
| break; |
| } |
| } |
| break; |
| } |
| case AUDREC_MSG_PACKET_READY_MSG: { |
| MM_DBG("PCK READY %x\n", msg[0]); |
| for (cnt = 0; cnt < MAX_ENC_COUNT ; cnt++) { |
| if (audrec->enc_session[cnt].audrec_obj_idx == |
| msg[0]) { |
| audrec->enc_session[cnt].event_func( |
| audrec->enc_session[cnt].private, id, msg); |
| break; |
| } |
| } |
| break; |
| } |
| case AUDREC_MSG_FATAL_ERR_MSG: { |
| MM_ERR("ERROR %x\n", msg[0]); |
| if (msg[1] & AUDREC_MSG_FATAL_ERR_TYPE_0) { |
| for (cnt = 0; cnt < MAX_ENC_COUNT ; cnt++) { |
| if (audrec->enc_session[cnt].audrec_obj_idx == |
| msg[0]) { |
| audrec->enc_session[cnt].event_func( |
| audrec->enc_session[cnt].private, id, |
| msg); |
| break; |
| } |
| } |
| } else if (msg[1] & AUDREC_MSG_FATAL_ERR_TYPE_1) { |
| cnt = audrec->enc_count-1; |
| if (audrec->enc_session[cnt].event_func) |
| audrec->enc_session[cnt].event_func( |
| audrec->enc_session[cnt].private, id, |
| msg); |
| } |
| break; |
| } |
| case ADSP_MESSAGE_ID: |
| MM_DBG("Received ADSP event: module \ |
| enable/disable(audrectask)\n"); |
| break; |
| default: |
| MM_ERR("unknown event %d\n", id); |
| } |
| } |
| |
| static struct msm_adsp_ops adsp_ops = { |
| .event = audrectask_dsp_event, |
| }; |
| |
| int audrectask_enable(unsigned enc_type, audrec_event_func func, void *private) |
| { |
| struct audrec_state *audrec = &the_audrec_state; |
| int cnt, rc = 0; |
| |
| mutex_lock(audrec->lock); |
| |
| if (audrec->enc_count++ == 0) { |
| MM_DBG("enable\n"); |
| for (cnt = 0; cnt < MAX_ENC_COUNT ; cnt++) { |
| if (audrec->enc_session[cnt].state == |
| ENC_SESSION_FREE) { |
| audrec->enc_session[cnt].state = |
| ENC_SESSION_ACTIVE; |
| audrec->enc_session[cnt].enc_type = enc_type; |
| audrec->enc_session[cnt].event_func = func; |
| audrec->enc_session[cnt].private = private; |
| break; |
| } |
| } |
| rc = msm_adsp_get("AUDRECTASK", &audrec->audrec_mod, &adsp_ops, |
| audrec); |
| if (rc < 0) { |
| MM_ERR("cannot open AUDRECTASK\n"); |
| audrec->enc_count = 0; |
| audrec->enc_session[cnt].state = ENC_SESSION_FREE; |
| audrec->enc_session[cnt].enc_type = 0xFFFFFFFF; |
| audrec->enc_session[cnt].event_func = NULL; |
| audrec->enc_session[cnt].private = NULL; |
| goto out; |
| } |
| msm_adsp_enable(audrec->audrec_mod); |
| } else { |
| for (cnt = 0; cnt < MAX_ENC_COUNT ; cnt++) { |
| if (audrec->enc_session[cnt].state == |
| ENC_SESSION_FREE) { |
| audrec->enc_session[cnt].state = |
| ENC_SESSION_ACTIVE; |
| audrec->enc_session[cnt].enc_type = enc_type; |
| audrec->enc_session[cnt].event_func = func; |
| audrec->enc_session[cnt].private = private; |
| break; |
| } |
| } |
| } |
| if (cnt == MAX_ENC_COUNT) |
| rc = -EBUSY; |
| else |
| rc = 0; |
| |
| out: |
| mutex_unlock(audrec->lock); |
| return rc; |
| } |
| EXPORT_SYMBOL(audrectask_enable); |
| |
| void audrectask_disable(unsigned enc_type, void *private) |
| { |
| struct audrec_state *audrec = &the_audrec_state; |
| |
| mutex_lock(audrec->lock); |
| |
| if (--audrec->enc_count == 0) { |
| MM_DBG("\n"); /* Macro prints the file name and function */ |
| msm_adsp_disable(audrec->audrec_mod); |
| msm_adsp_put(audrec->audrec_mod); |
| audrec->audrec_mod = NULL; |
| } |
| |
| mutex_unlock(audrec->lock); |
| } |
| EXPORT_SYMBOL(audrectask_disable); |
| |