/* * Common code to deal with the AUDPREPROC dsp task (audio preprocessing) * * Copyright (c) 2011-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. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * */ #include #include #include #include #include #include #include static DEFINE_MUTEX(audpreproc_lock); struct msm_adspenc_info { const char *module_name; unsigned module_queueids; int module_encid; /* streamid */ int enc_formats; /* supported formats */ int nr_codec_support; /* number of codec suported */ }; #define ENC_MODULE_INFO(name, queueids, encid, formats, nr_codec) \ {.module_name = name, .module_queueids = queueids, \ .module_encid = encid, .enc_formats = formats, \ .nr_codec_support = nr_codec } #ifdef CONFIG_MSM7X27A_AUDIO #define ENC0_FORMAT ((1<func[0]) audpreproc->func[0]( audpreproc->private[0], id, &msg); break; case AUDPREPROC_MSG_ERROR_MSG_ID: MM_INFO("err_index %d\n", msg[0]); if (audpreproc->func[0]) audpreproc->func[0]( audpreproc->private[0], id, &msg); break; case ADSP_MESSAGE_ID: MM_DBG("Received ADSP event: module enable(audpreproctask)\n"); if (audpreproc->func[0]) audpreproc->func[0]( audpreproc->private[0], id, &msg); break; case AUDPREPROC_MSG_FEAT_QUERY_DM_DONE: { uint16_t msg[3]; getevent(msg, sizeof(msg)); MM_INFO("RTC ACK --> %x %x %x\n", msg[0], msg[1], msg[2]); acdb_rtc_set_err(msg[2]); } break; default: MM_ERR("unknown event %d\n", id); } return; } static struct msm_adsp_ops adsp_ops = { .event = audpreproc_dsp_event, }; /* EXPORTED API's */ int audpreproc_enable(int enc_id, audpreproc_event_func func, void *private) { struct audpreproc_state *audpreproc = &the_audpreproc_state; int res = 0; uint16_t msg[2]; int n = 0; MM_DBG("audpreproc_enable %d\n", enc_id); if (enc_id < 0 || enc_id > (MAX_ENC_COUNT - 1)) return -EINVAL; mutex_lock(audpreproc->lock); if (audpreproc->func[enc_id]) { res = -EBUSY; goto out; } audpreproc->func[enc_id] = func; audpreproc->private[enc_id] = private; /* First client to enable preproc task */ if (audpreproc->open_count++ == 0) { MM_DBG("Get AUDPREPROCTASK\n"); res = msm_adsp_get("AUDPREPROCTASK", &audpreproc->mod, &adsp_ops, audpreproc); if (res < 0) { MM_ERR("Can not get AUDPREPROCTASK\n"); audpreproc->open_count = 0; audpreproc->func[enc_id] = NULL; audpreproc->private[enc_id] = NULL; goto out; } if (msm_adsp_enable(audpreproc->mod)) { audpreproc->open_count = 0; audpreproc->func[enc_id] = NULL; audpreproc->private[enc_id] = NULL; msm_adsp_put(audpreproc->mod); audpreproc->mod = NULL; res = -ENODEV; goto out; } } msg[0] = AUDPREPROC_MSG_STATUS_FLAG_ENA; /* Generate audpre enabled message for registered clients */ for (n = 0; n < MAX_EVENT_CALLBACK_CLIENTS; ++n) { if (audpreproc->cb_tbl[n] && audpreproc->cb_tbl[n]->fn) { audpreproc->cb_tbl[n]->fn( \ audpreproc->cb_tbl[n]->private,\ AUDPREPROC_MSG_CMD_CFG_DONE_MSG, (void *) &msg); } } res = 0; out: mutex_unlock(audpreproc->lock); return res; } EXPORT_SYMBOL(audpreproc_enable); void audpreproc_disable(int enc_id, void *private) { struct audpreproc_state *audpreproc = &the_audpreproc_state; uint16_t msg[2]; int n = 0; if (enc_id < 0 || enc_id > (MAX_ENC_COUNT - 1)) return; mutex_lock(audpreproc->lock); if (!audpreproc->func[enc_id]) goto out; if (audpreproc->private[enc_id] != private) goto out; audpreproc->func[enc_id] = NULL; audpreproc->private[enc_id] = NULL; /* Last client then disable preproc task */ if (--audpreproc->open_count == 0) { msm_adsp_disable(audpreproc->mod); MM_DBG("Put AUDPREPROCTASK\n"); msm_adsp_put(audpreproc->mod); audpreproc->mod = NULL; } msg[0] = AUDPREPROC_MSG_STATUS_FLAG_DIS; /* Generate audpre enabled message for registered clients */ for (n = 0; n < MAX_EVENT_CALLBACK_CLIENTS; ++n) { if (audpreproc->cb_tbl[n] && audpreproc->cb_tbl[n]->fn) { audpreproc->cb_tbl[n]->fn( \ audpreproc->cb_tbl[n]->private,\ AUDPREPROC_MSG_CMD_CFG_DONE_MSG, (void *) &msg); } } out: mutex_unlock(audpreproc->lock); return; } EXPORT_SYMBOL(audpreproc_disable); int audpreproc_update_audrec_info( struct audrec_session_info *audrec_session_info) { if (!audrec_session_info) { MM_ERR("error in audrec session info address\n"); return -EINVAL; } if (audrec_session_info->session_id < MAX_ENC_COUNT) { memcpy(&session_info, audrec_session_info, sizeof(struct audrec_session_info)); return 0; } return -EINVAL; } EXPORT_SYMBOL(audpreproc_update_audrec_info); int get_audrec_session_info(struct audrec_session_info *info) { if (!info) { MM_ERR("error in audrec session info address\n"); return -EINVAL; } if (the_audpreproc_state.open_count == 0) { MM_ERR("No aud pre session active\n"); return -EINVAL; } memcpy(info, &session_info, sizeof(struct audrec_session_info)); return 0; } EXPORT_SYMBOL(get_audrec_session_info); int audpreproc_register_event_callback(struct audpreproc_event_callback *ecb) { struct audpreproc_state *audpreproc = &the_audpreproc_state; int i; for (i = 0; i < MAX_EVENT_CALLBACK_CLIENTS; ++i) { if (NULL == audpreproc->cb_tbl[i]) { audpreproc->cb_tbl[i] = ecb; return 0; } } return -EINVAL; } EXPORT_SYMBOL(audpreproc_register_event_callback); int audpreproc_unregister_event_callback(struct audpreproc_event_callback *ecb) { struct audpreproc_state *audpreproc = &the_audpreproc_state; int i; for (i = 0; i < MAX_EVENT_CALLBACK_CLIENTS; ++i) { if (ecb == audpreproc->cb_tbl[i]) { audpreproc->cb_tbl[i] = NULL; return 0; } } return -EINVAL; } /* enc_type = supported encode format * * like pcm, aac, sbc, evrc, qcelp, amrnb etc ... * */ int audpreproc_aenc_alloc(unsigned enc_type, const char **module_name, unsigned *queue_ids) { struct audpreproc_state *audpreproc = &the_audpreproc_state; int encid = -1, idx, lidx, mode, codec; int codecs_supported, min_codecs_supported; mutex_lock(audpreproc->lock); /* Represents in bit mask */ mode = ((enc_type & AUDPREPROC_MODE_MASK) << 16); codec = (1 << (enc_type & AUDPREPROC_CODEC_MASK)); lidx = msm_enc_database.num_enc; min_codecs_supported = sizeof(unsigned int) * 8; MM_DBG("mode = 0x%08x codec = 0x%08x\n", mode, codec); for (idx = lidx-1; idx >= 0; idx--) { /* encoder free and supports the format */ if (!(audpreproc->enc_inuse & (1 << (idx))) && ((mode & msm_enc_database.enc_info_list[idx].enc_formats) == mode) && ((codec & msm_enc_database.enc_info_list[idx].enc_formats) == codec)){ /* Check supports minimum number codecs */ codecs_supported = msm_enc_database.enc_info_list[idx].nr_codec_support; if (codecs_supported < min_codecs_supported) { lidx = idx; min_codecs_supported = codecs_supported; } } } if (lidx < msm_enc_database.num_enc) { audpreproc->enc_inuse |= (1 << lidx); *module_name = msm_enc_database.enc_info_list[lidx].module_name; *queue_ids = msm_enc_database.enc_info_list[lidx].module_queueids; encid = msm_enc_database.enc_info_list[lidx].module_encid; } mutex_unlock(audpreproc->lock); return encid; } EXPORT_SYMBOL(audpreproc_aenc_alloc); void audpreproc_aenc_free(int enc_id) { struct audpreproc_state *audpreproc = &the_audpreproc_state; int idx; mutex_lock(audpreproc->lock); for (idx = 0; idx < msm_enc_database.num_enc; idx++) { if (msm_enc_database.enc_info_list[idx].module_encid == enc_id) { audpreproc->enc_inuse &= ~(1 << idx); break; } } mutex_unlock(audpreproc->lock); return; } EXPORT_SYMBOL(audpreproc_aenc_free); int audpreproc_dsp_set_agc( audpreproc_cmd_cfg_agc_params *agc_cfg, unsigned len) { return msm_adsp_write(the_audpreproc_state.mod, QDSP_uPAudPreProcCmdQueue, agc_cfg, len); } EXPORT_SYMBOL(audpreproc_dsp_set_agc); int audpreproc_dsp_set_ns( audpreproc_cmd_cfg_ns_params *ns_cfg, unsigned len) { return msm_adsp_write(the_audpreproc_state.mod, QDSP_uPAudPreProcCmdQueue, ns_cfg, len); } EXPORT_SYMBOL(audpreproc_dsp_set_ns); int audpreproc_dsp_set_iir( audpreproc_cmd_cfg_iir_tuning_filter_params *iir_cfg, unsigned len) { return msm_adsp_write(the_audpreproc_state.mod, QDSP_uPAudPreProcCmdQueue, iir_cfg, len); } EXPORT_SYMBOL(audpreproc_dsp_set_iir); int audpreproc_send_preproccmdqueue(void *cmd, unsigned len) { return msm_adsp_write(the_audpreproc_state.mod, QDSP_uPAudPreProcCmdQueue, cmd, len); } EXPORT_SYMBOL(audpreproc_send_preproccmdqueue);