/* * Common code to deal with the AUDPREPROC dsp task (audio preprocessing) * * Copyright (c) 2009-2011, 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 #include #include #include static DEFINE_MUTEX(audpreproc_lock); static struct wake_lock audpre_wake_lock; static struct pm_qos_request audpre_pm_qos_req; 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 } #define MAX_EVENT_CALLBACK_CLIENTS 1 #define ENC0_FORMAT ((1<func[cfg_done_msg.stream_id]) audpreproc->func[cfg_done_msg.stream_id]( audpreproc->private[cfg_done_msg.stream_id], id, &cfg_done_msg); break; } case AUDPREPROC_ERROR_MSG: { struct audpreproc_err_msg err_msg; getevent(&err_msg, AUDPREPROC_ERROR_MSG_LEN); MM_DBG("AUDPREPROC_ERROR_MSG: stream id %d err idx %d\n", err_msg.stream_id, err_msg.aud_preproc_err_idx); if ((err_msg.stream_id < MAX_ENC_COUNT) && audpreproc->func[err_msg.stream_id]) audpreproc->func[err_msg.stream_id]( audpreproc->private[err_msg.stream_id], id, &err_msg); break; } case AUDPREPROC_CMD_ENC_CFG_DONE_MSG: { struct audpreproc_cmd_enc_cfg_done_msg enc_cfg_msg; getevent(&enc_cfg_msg, AUDPREPROC_CMD_ENC_CFG_DONE_MSG_LEN); MM_DBG("AUDPREPROC_CMD_ENC_CFG_DONE_MSG: stream id %d enc type \ %d\n", enc_cfg_msg.stream_id, enc_cfg_msg.rec_enc_type); if ((enc_cfg_msg.stream_id < MAX_ENC_COUNT) && audpreproc->func[enc_cfg_msg.stream_id]) audpreproc->func[enc_cfg_msg.stream_id]( audpreproc->private[enc_cfg_msg.stream_id], id, &enc_cfg_msg); 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, \ id, (void *) &enc_cfg_msg); } } break; } case AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG: { struct audpreproc_cmd_enc_param_cfg_done_msg enc_param_msg; getevent(&enc_param_msg, AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG_LEN); MM_DBG("AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG: stream id %d\n", enc_param_msg.stream_id); if ((enc_param_msg.stream_id < MAX_ENC_COUNT) && audpreproc->func[enc_param_msg.stream_id]) audpreproc->func[enc_param_msg.stream_id]( audpreproc->private[enc_param_msg.stream_id], id, &enc_param_msg); break; } case AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG: { struct audpreproc_afe_cmd_audio_record_cfg_done record_cfg_done; getevent(&record_cfg_done, AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG_LEN); MM_DBG("AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG: \ stream id %d\n", record_cfg_done.stream_id); if ((record_cfg_done.stream_id < MAX_ENC_COUNT) && audpreproc->func[record_cfg_done.stream_id]) audpreproc->func[record_cfg_done.stream_id]( audpreproc->private[record_cfg_done.stream_id], id, &record_cfg_done); break; } case AUDPREPROC_CMD_ROUTING_MODE_DONE_MSG: { struct audpreproc_cmd_routing_mode_done routing_mode_done; getevent(&routing_mode_done, AUDPREPROC_CMD_ROUTING_MODE_DONE_MSG_LEN); MM_DBG("AUDPREPROC_CMD_ROUTING_MODE_DONE_MSG: \ stream id %d\n", routing_mode_done.stream_id); if ((routing_mode_done.stream_id < MAX_ENC_COUNT) && audpreproc->func[routing_mode_done.stream_id]) audpreproc->func[routing_mode_done.stream_id]( audpreproc->private[routing_mode_done.stream_id], id, &routing_mode_done); break; } #ifdef CONFIG_DEBUG_FS 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; #endif case ADSP_MESSAGE_ID: { MM_DBG("Received ADSP event:module audpreproctask\n"); 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; 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; } prevent_suspend(); if (msm_adsp_enable(audpreproc->mod)) { MM_ERR("Can not enable AUDPREPROCTASK\n"); audpreproc->open_count = 0; audpreproc->func[enc_id] = NULL; audpreproc->private[enc_id] = NULL; msm_adsp_put(audpreproc->mod); audpreproc->mod = NULL; res = -ENODEV; allow_suspend(); goto out; } } res = 0; out: mutex_unlock(audpreproc->lock); return res; } EXPORT_SYMBOL(audpreproc_enable); 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->session_id], audrec_session_info, sizeof(struct audrec_session_info)); return 0; } return -EINVAL; } EXPORT_SYMBOL(audpreproc_update_audrec_info); void audpreproc_disable(int enc_id, void *private) { struct audpreproc_state *audpreproc = &the_audpreproc_state; 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; allow_suspend(); } out: mutex_unlock(audpreproc->lock); return; } EXPORT_SYMBOL(audpreproc_disable); 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 -1; } 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 -1; } EXPORT_SYMBOL(audpreproc_unregister_event_callback); /* 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; static int wakelock_init; 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; } if (!wakelock_init) { wake_lock_init(&audpre_wake_lock, WAKE_LOCK_SUSPEND, "audpre"); pm_qos_add_request(&audpre_pm_qos_req, PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); wakelock_init = 1; } 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_send_preproccmdqueue(void *cmd, unsigned len) { return msm_adsp_write(the_audpreproc_state.mod, QDSP_uPAudPreProcCmdQueue, cmd, len); } EXPORT_SYMBOL(audpreproc_send_preproccmdqueue); int audpreproc_send_audreccmdqueue(void *cmd, unsigned len) { return msm_adsp_write(the_audpreproc_state.mod, QDSP_uPAudPreProcAudRecCmdQueue, cmd, len); } EXPORT_SYMBOL(audpreproc_send_audreccmdqueue); int audpreproc_send_audrec2cmdqueue(void *cmd, unsigned len) { return msm_adsp_write(the_audpreproc_state.mod, QDSP_uPAudRec2CmdQueue, cmd, len); } EXPORT_SYMBOL(audpreproc_send_audrec2cmdqueue); int audpreproc_dsp_set_agc(struct audpreproc_cmd_cfg_agc_params *agc, unsigned len) { return msm_adsp_write(the_audpreproc_state.mod, QDSP_uPAudPreProcCmdQueue, agc, len); } EXPORT_SYMBOL(audpreproc_dsp_set_agc); int audpreproc_dsp_set_agc2(struct audpreproc_cmd_cfg_agc_params_2 *agc2, unsigned len) { return msm_adsp_write(the_audpreproc_state.mod, QDSP_uPAudPreProcCmdQueue, agc2, len); } EXPORT_SYMBOL(audpreproc_dsp_set_agc2); int audpreproc_dsp_set_ns(struct audpreproc_cmd_cfg_ns_params *ns, unsigned len) { return msm_adsp_write(the_audpreproc_state.mod, QDSP_uPAudPreProcCmdQueue, ns, len); } EXPORT_SYMBOL(audpreproc_dsp_set_ns); int audpreproc_dsp_set_iir( struct audpreproc_cmd_cfg_iir_tuning_filter_params *iir, unsigned len) { return msm_adsp_write(the_audpreproc_state.mod, QDSP_uPAudPreProcCmdQueue, iir, len); } EXPORT_SYMBOL(audpreproc_dsp_set_iir); int audpreproc_dsp_set_gain_tx( struct audpreproc_cmd_cfg_cal_gain *calib_gain_tx, unsigned len) { return msm_adsp_write(the_audpreproc_state.mod, QDSP_uPAudPreProcCmdQueue, calib_gain_tx, len); } EXPORT_SYMBOL(audpreproc_dsp_set_gain_tx); void get_audrec_session_info(int id, struct audrec_session_info *info) { if (id >= MAX_ENC_COUNT) { MM_ERR("invalid session id = %d\n", id); return; } memcpy(info, &session_info[id], sizeof(struct audrec_session_info)); } EXPORT_SYMBOL(get_audrec_session_info); int audpreproc_dsp_set_lvnv( struct audpreproc_cmd_cfg_lvnv_param *preproc_lvnv, unsigned len) { return msm_adsp_write(the_audpreproc_state.mod, QDSP_uPAudPreProcCmdQueue, preproc_lvnv, len); } EXPORT_SYMBOL(audpreproc_dsp_set_lvnv);