blob: 0f34518f3212b1fd1d998067ae2b3a809e570b3a [file] [log] [blame]
/* 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);