/* arch/arm/mach-msm/qdsp5/audpp.c * * common code to deal with the AUDPP dsp task (audio postproc) * * Copyright (C) 2008 Google, Inc. * Copyright (c) 2009-2011, The Linux Foundation. All rights reserved. * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../qdsp5/evlog.h" #include 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); static DEFINE_MUTEX(audpp_dec_lock); static struct wake_lock audpp_wake_lock; #define CH_COUNT 5 #define AUDPP_CLNT_MAX_COUNT 6 #define AUDPP_CMD_CFG_OBJ_UPDATE 0x8000 #define AUDPP_CMD_EQ_FLAG_DIS 0x0000 #define AUDPP_CMD_EQ_FLAG_ENA -1 #define AUDPP_CMD_IIR_FLAG_DIS 0x0000 #define AUDPP_CMD_IIR_FLAG_ENA -1 #define AUDPP_CMD_STF_FLAG_ENA -1 #define AUDPP_CMD_STF_FLAG_DIS 0x0000 #define MAX_EVENT_CALLBACK_CLIENTS 1 #define AUDPP_CONCURRENCY_DEFAULT 0 /* Set default to LPA mode */ #define AUDPP_MAX_DECODER_CNT 5 #define AUDPP_CODEC_MASK 0x000000FF #define AUDPP_MODE_MASK 0x00000F00 #define AUDPP_OP_MASK 0xF0000000 struct audpp_decoder_info { unsigned int codec; pid_t pid; }; 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; /* Related to decoder allocation */ struct mutex *lock_dec; struct msm_adspdec_database *dec_database; struct audpp_decoder_info dec_info_table[AUDPP_MAX_DECODER_CNT]; unsigned dec_inuse; unsigned long concurrency; struct audpp_event_callback *cb_tbl[MAX_EVENT_CALLBACK_CLIENTS]; /* Related to decoder instances */ uint8_t op_mode; /* Specifies Turbo/Non Turbo mode */ uint8_t decoder_count; /* No. of decoders active running */ uint8_t codec_max_instances; /* Max codecs allowed currently */ uint8_t codec_cnt[MSM_MAX_DEC_CNT]; /* Nr of each codec type enabled */ wait_queue_head_t event_wait; }; struct audpp_state the_audpp_state = { .lock = &audpp_lock, .lock_dec = &audpp_dec_lock, }; static inline void prevent_suspend(void) { wake_lock(&audpp_wake_lock); } static inline void allow_suspend(void) { wake_unlock(&audpp_wake_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) { struct 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)); } void audpp_route_stream(unsigned short dec_id, unsigned short mixer_mask) { struct audpp_cmd_cfg_dev_mixer_params mixer_params_cmd; memset(&mixer_params_cmd, 0, sizeof(mixer_params_cmd)); mixer_params_cmd.cmd_id = AUDPP_CMD_CFG_DEV_MIXER; mixer_params_cmd.stream_id = dec_id; mixer_params_cmd.mixer_cmd = mixer_mask; audpp_send_queue1(&mixer_params_cmd, sizeof(mixer_params_cmd)); } EXPORT_SYMBOL(audpp_route_stream); int is_audpp_enable(void) { struct audpp_state *audpp = &the_audpp_state; return audpp->enabled; } EXPORT_SYMBOL(is_audpp_enable); int audpp_register_event_callback(struct audpp_event_callback *ecb) { struct audpp_state *audpp = &the_audpp_state; int i; for (i = 0; i < MAX_EVENT_CALLBACK_CLIENTS; ++i) { if (NULL == audpp->cb_tbl[i]) { audpp->cb_tbl[i] = ecb; return 0; } } return -1; } EXPORT_SYMBOL(audpp_register_event_callback); int audpp_unregister_event_callback(struct audpp_event_callback *ecb) { struct audpp_state *audpp = &the_audpp_state; int i; for (i = 0; i < MAX_EVENT_CALLBACK_CLIENTS; ++i) { if (ecb == audpp->cb_tbl[i]) { audpp->cb_tbl[i] = NULL; return 0; } } return -1; } EXPORT_SYMBOL(audpp_unregister_event_callback); 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); } for (n = 0; n < MAX_EVENT_CALLBACK_CLIENTS; ++n) if (audpp->cb_tbl[n] && audpp->cb_tbl[n]->fn) audpp->cb_tbl[n]->fn(audpp->cb_tbl[n]->private, 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_handle_pcmdmamiss(struct audpp_state *audpp, uint16_t bit_mask) { uint8_t b_index; for (b_index = 0; b_index < AUDPP_CLNT_MAX_COUNT; b_index++) { if (bit_mask & (0x1 << b_index)) if (audpp->func[b_index]) audpp->func[b_index] (audpp->private[b_index], AUDPP_MSG_PCMDMAMISSED, &bit_mask); } } 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]; 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]; MM_DBG("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: audpp_handle_pcmdmamiss(audpp, msg[0]); break; case AUDPP_MSG_CFG_MSG: if (msg[0] == AUDPP_MSG_ENA_ENA) { MM_INFO("ENABLE\n"); audpp->enabled = 1; audpp_broadcast(audpp, id, msg); } else if (msg[0] == AUDPP_MSG_ENA_DIS) { MM_INFO("DISABLE\n"); audpp->enabled = 0; wake_up(&audpp->event_wait); audpp_broadcast(audpp, id, msg); } else { MM_ERR("invalid config msg %d\n", msg[0]); } break; case AUDPP_MSG_ROUTING_ACK: audpp_notify_clnt(audpp, msg[0], id, msg); break; case AUDPP_MSG_FLUSH_ACK: audpp_notify_clnt(audpp, msg[0], id, msg); break; case ADSP_MESSAGE_ID: MM_DBG("Received ADSP event: module enable/disable \ (audpptask)"); break; case AUDPP_MSG_AVSYNC_MSG: audpp_notify_clnt(audpp, msg[0], id, msg); break; #ifdef CONFIG_DEBUG_FS case AUDPP_MSG_FEAT_QUERY_DM_DONE: MM_INFO(" RTC ACK --> %x %x %x %x %x %x %x %x\n", msg[0],\ msg[1], msg[2], msg[3], msg[4], \ msg[5], msg[6], msg[7]); acdb_rtc_set_err(msg[3]); break; #endif default: MM_INFO("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]; uint16_t n = 0; msg[0] = arg; audpp->func[id] (audpp->private[id], event, msg); if (audpp->enabled == 1) { for (n = 0; n < MAX_EVENT_CALLBACK_CLIENTS; ++n) if (audpp->cb_tbl[n] && audpp->cb_tbl[n]->fn) audpp->cb_tbl[n]->fn(audpp->cb_tbl[n]->private, AUDPP_MSG_CFG_MSG, 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) { MM_DBG("enable\n"); res = msm_adsp_get("AUDPPTASK", &audpp->mod, &adsp_ops, audpp); if (res < 0) { MM_ERR("audpp: cannot open AUDPPTASK\n"); audpp->open_count = 0; audpp->func[id] = NULL; audpp->private[id] = NULL; goto out; } LOG(EV_ENABLE, 2); prevent_suspend(); 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; int rc; 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) { MM_DBG("disable\n"); LOG(EV_DISABLE, 2); audpp_dsp_config(0); rc = wait_event_interruptible(audpp->event_wait, (audpp->enabled == 0)); if (audpp->enabled == 0) MM_INFO("Received CFG_MSG_DISABLE from ADSP\n"); else MM_ERR("Didn't receive CFG_MSG DISABLE \ message from ADSP\n"); msm_adsp_disable(audpp->mod); msm_adsp_put(audpp->mod); audpp->mod = NULL; allow_suspend(); } out: mutex_unlock(audpp->lock); } EXPORT_SYMBOL(audpp_disable); #define BAD_ID(id) ((id < 0) || (id >= CH_COUNT)) int audpp_restore_avsync(int id, uint16_t *avsync) { struct audpp_cmd_avsync cmd; if (BAD_ID(id)) return -1; memset(&cmd, 0, sizeof(cmd)); cmd.cmd_id = AUDPP_CMD_AVSYNC; cmd.stream_id = id; cmd.interrupt_interval = 0; /* Setting it to Zero as there won't be periodic update */ cmd.sample_counter_dlsw = avsync[3]; cmd.sample_counter_dmsw = avsync[2]; cmd.sample_counter_msw = avsync[1]; cmd.byte_counter_dlsw = avsync[6]; cmd.byte_counter_dmsw = avsync[5]; cmd.byte_counter_msw = avsync[4]; return audpp_send_queue1(&cmd, sizeof(cmd)); } EXPORT_SYMBOL(audpp_restore_avsync); int audpp_query_avsync(int id) { struct audpp_cmd_query_avsync cmd; if (BAD_ID(id)) return -EINVAL; memset(&cmd, 0, sizeof(cmd)); cmd.cmd_id = AUDPP_CMD_QUERY_AVSYNC; cmd.stream_id = id; return audpp_send_queue1(&cmd, sizeof(cmd)); } EXPORT_SYMBOL(audpp_query_avsync); int audpp_set_volume_and_pan(unsigned id, unsigned volume, int pan, enum obj_type objtype) { /* cmd, obj_cfg[7], cmd_type, volume, pan */ uint16_t cmd[7]; if (objtype) { if (id > 5) { MM_ERR("Wrong POPP decoder id: %d\n", id); return -EINVAL; } } else { if (id > 3) { MM_ERR("Wrong COPP decoder id: %d\n", id); return -EINVAL; } } memset(cmd, 0, sizeof(cmd)); cmd[0] = AUDPP_CMD_CFG_OBJECT_PARAMS; if (objtype) cmd[1] = AUDPP_CMD_POPP_STREAM; else cmd[1] = AUDPP_CMD_COPP_STREAM; cmd[2] = id; cmd[3] = AUDPP_CMD_CFG_OBJ_UPDATE; cmd[4] = AUDPP_CMD_VOLUME_PAN; cmd[5] = volume; cmd[6] = pan; return audpp_send_queue3(cmd, sizeof(cmd)); } EXPORT_SYMBOL(audpp_set_volume_and_pan); /* Implementation of COPP features */ int audpp_dsp_set_mbadrc(unsigned id, unsigned enable, struct audpp_cmd_cfg_object_params_mbadrc *mbadrc, enum obj_type objtype) { if (objtype) { if (id > 5) { MM_ERR("Wrong POPP decoder id: %d\n", id); return -EINVAL; } } else { if (id > 3) { MM_ERR("Wrong COPP decoder id: %d\n", id); return -EINVAL; } } mbadrc->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS; if (objtype) mbadrc->common.stream = AUDPP_CMD_POPP_STREAM; else mbadrc->common.stream = AUDPP_CMD_COPP_STREAM; mbadrc->common.stream_id = id; mbadrc->common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; mbadrc->common.command_type = AUDPP_CMD_MBADRC; if (enable) mbadrc->enable = AUDPP_CMD_ADRC_FLAG_ENA; else mbadrc->enable = AUDPP_CMD_ADRC_FLAG_DIS; return audpp_send_queue3(mbadrc, sizeof(struct audpp_cmd_cfg_object_params_mbadrc)); } EXPORT_SYMBOL(audpp_dsp_set_mbadrc); int audpp_dsp_set_qconcert_plus(unsigned id, unsigned enable, struct audpp_cmd_cfg_object_params_qconcert *qconcert_plus, enum obj_type objtype) { if (objtype) { if (id > 5) { MM_ERR("Wrong POPP decoder id: %d\n", id); return -EINVAL; } } else { if (id > 3) { MM_ERR("Wrong COPP decoder id: %d\n", id); return -EINVAL; } } qconcert_plus->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS; if (objtype) qconcert_plus->common.stream = AUDPP_CMD_POPP_STREAM; else qconcert_plus->common.stream = AUDPP_CMD_COPP_STREAM; qconcert_plus->common.stream_id = id; qconcert_plus->common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; qconcert_plus->common.command_type = AUDPP_CMD_QCONCERT; if (enable) qconcert_plus->enable_flag = AUDPP_CMD_ADRC_FLAG_ENA; else qconcert_plus->enable_flag = AUDPP_CMD_ADRC_FLAG_DIS; return audpp_send_queue3(qconcert_plus, sizeof(struct audpp_cmd_cfg_object_params_qconcert)); } EXPORT_SYMBOL(audpp_dsp_set_qconcert_plus); int audpp_dsp_set_rx_iir(unsigned id, unsigned enable, struct audpp_cmd_cfg_object_params_pcm *iir, enum obj_type objtype) { if (objtype) { if (id > 5) { MM_ERR("Wrong POPP decoder id: %d\n", id); return -EINVAL; } } else { if (id > 3) { MM_ERR("Wrong COPP decoder id: %d\n", id); return -EINVAL; } } iir->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS; if (objtype) iir->common.stream = AUDPP_CMD_POPP_STREAM; else iir->common.stream = AUDPP_CMD_COPP_STREAM; iir->common.stream_id = id; iir->common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; iir->common.command_type = AUDPP_CMD_IIR_TUNING_FILTER; if (enable) iir->active_flag = AUDPP_CMD_IIR_FLAG_ENA; else iir->active_flag = AUDPP_CMD_IIR_FLAG_DIS; return audpp_send_queue3(iir, sizeof(struct audpp_cmd_cfg_object_params_pcm)); } EXPORT_SYMBOL(audpp_dsp_set_rx_iir); int audpp_dsp_set_gain_rx(unsigned id, struct audpp_cmd_cfg_cal_gain *calib_gain_rx, enum obj_type objtype) { if (objtype) { return -EINVAL; } else { if (id > 3) { MM_ERR("Wrong COPP decoder id: %d\n", id); return -EINVAL; } } calib_gain_rx->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS; calib_gain_rx->common.stream = AUDPP_CMD_COPP_STREAM; calib_gain_rx->common.stream_id = id; calib_gain_rx->common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; calib_gain_rx->common.command_type = AUDPP_CMD_CALIB_GAIN_RX; return audpp_send_queue3(calib_gain_rx, sizeof(struct audpp_cmd_cfg_cal_gain)); } EXPORT_SYMBOL(audpp_dsp_set_gain_rx); int audpp_dsp_set_pbe(unsigned id, unsigned enable, struct audpp_cmd_cfg_pbe *pbe_block, enum obj_type objtype) { if (objtype) { if (id > 5) { MM_ERR("Wrong POPP decoder id: %d\n", id); return -EINVAL; } } else { if (id > 3) { MM_ERR("Wrong COPP decoder id: %d\n", id); return -EINVAL; } } pbe_block->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS; if (objtype) pbe_block->common.stream = AUDPP_CMD_POPP_STREAM; else pbe_block->common.stream = AUDPP_CMD_COPP_STREAM; pbe_block->common.stream_id = id; pbe_block->common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; pbe_block->common.command_type = AUDPP_CMD_PBE; if (enable) pbe_block->pbe_enable = AUDPP_CMD_PBE_FLAG_ENA; else pbe_block->pbe_enable = AUDPP_CMD_PBE_FLAG_DIS; return audpp_send_queue3(pbe_block, sizeof(struct audpp_cmd_cfg_pbe)); } EXPORT_SYMBOL(audpp_dsp_set_pbe); int audpp_dsp_set_spa(unsigned id, struct audpp_cmd_cfg_object_params_spectram *spa, enum obj_type objtype){ struct audpp_cmd_cfg_object_params_spectram cmd; if (objtype) { if (id > 5) { MM_ERR("Wrong POPP decoder id: %d\n", id); return -EINVAL; } } else { if (id > 3) { MM_ERR("Wrong COPP decoder id: %d\n", id); return -EINVAL; } } memset(&cmd, 0, sizeof(cmd)); if (objtype) cmd.common.stream = AUDPP_CMD_POPP_STREAM; else cmd.common.stream = AUDPP_CMD_COPP_STREAM; cmd.common.stream_id = id; cmd.common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; cmd.common.command_type = AUDPP_CMD_SPECTROGRAM; cmd.sample_interval = spa->sample_interval; cmd.num_coeff = spa->num_coeff; return audpp_send_queue3(&cmd, sizeof(cmd)); } EXPORT_SYMBOL(audpp_dsp_set_spa); int audpp_dsp_set_stf(unsigned id, unsigned enable, struct audpp_cmd_cfg_object_params_sidechain *stf, enum obj_type objtype){ if (objtype) { if (id > 5) { MM_ERR("Wrong POPP decoder id: %d\n", id); return -EINVAL; } } else { if (id > 3) { MM_ERR("Wrong COPP decoder id: %d\n", id); return -EINVAL; } } stf->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS; if (objtype) stf->common.stream = AUDPP_CMD_POPP_STREAM; else stf->common.stream = AUDPP_CMD_COPP_STREAM; stf->common.stream_id = id; stf->common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; stf->common.command_type = AUDPP_CMD_SIDECHAIN_TUNING_FILTER; if (enable) stf->active_flag = AUDPP_CMD_STF_FLAG_ENA; else stf->active_flag = AUDPP_CMD_STF_FLAG_DIS; return audpp_send_queue3(stf, sizeof(struct audpp_cmd_cfg_object_params_sidechain)); } EXPORT_SYMBOL(audpp_dsp_set_stf); /* Implementation Of COPP + POPP */ int audpp_dsp_set_eq(unsigned id, unsigned enable, struct audpp_cmd_cfg_object_params_eqalizer *eq, enum obj_type objtype) { struct audpp_cmd_cfg_object_params_eqalizer cmd; if (objtype) { if (id > 5) { MM_ERR("Wrong POPP decoder id: %d\n", id); return -EINVAL; } } else { if (id > 3) { MM_ERR("Wrong COPP decoder id: %d\n", id); return -EINVAL; } } memset(&cmd, 0, sizeof(cmd)); if (objtype) cmd.common.stream = AUDPP_CMD_POPP_STREAM; else cmd.common.stream = AUDPP_CMD_COPP_STREAM; cmd.common.stream_id = id; cmd.common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; cmd.common.command_type = AUDPP_CMD_EQUALIZER; if (enable) { cmd.eq_flag = AUDPP_CMD_EQ_FLAG_ENA; cmd.num_bands = eq->num_bands; memcpy(&cmd.eq_coeff, &eq->eq_coeff, sizeof(eq->eq_coeff)); } else cmd.eq_flag = AUDPP_CMD_EQ_FLAG_DIS; return audpp_send_queue3(&cmd, sizeof(cmd)); } EXPORT_SYMBOL(audpp_dsp_set_eq); int audpp_dsp_set_vol_pan(unsigned id, struct audpp_cmd_cfg_object_params_volume *vol_pan, enum obj_type objtype) { struct audpp_cmd_cfg_object_params_volume cmd; if (objtype) { if (id > 5) { MM_ERR("Wrong POPP decoder id: %d\n", id); return -EINVAL; } } else { if (id > AUDPP_MAX_COPP_DEVICES) { MM_ERR("Wrong COPP decoder id: %d\n", id); return -EINVAL; } } memset(&cmd, 0, sizeof(cmd)); cmd.common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS; if (objtype) cmd.common.stream = AUDPP_CMD_POPP_STREAM; else cmd.common.stream = AUDPP_CMD_COPP_STREAM; cmd.common.stream_id = id; cmd.common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; cmd.common.command_type = AUDPP_CMD_VOLUME_PAN; cmd.volume = vol_pan->volume; cmd.pan = vol_pan->pan; return audpp_send_queue3(&cmd, sizeof(cmd)); } EXPORT_SYMBOL(audpp_dsp_set_vol_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; pause_cmd[1] = id; if (pause == 1) pause_cmd[2] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_PAUSE_V; else if (pause == 0) pause_cmd[2] = 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; flush_cmd[2] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_FLUSH_V; return audpp_send_queue1(flush_cmd, sizeof(flush_cmd)); } EXPORT_SYMBOL(audpp_flush); /* dec_attrb = 7:0, 0 - No Decoder, else supported decoder * * like mp3, aac, wma etc ... * * = 15:8, bit[8] = 1 - Tunnel, bit[9] = 1 - NonTunnel * * = 31:16, reserved */ int audpp_adec_alloc(unsigned dec_attrb, const char **module_name, unsigned *queueid) { struct audpp_state *audpp = &the_audpp_state; int decid = -1, idx, lidx, mode, codec; int codecs_supported, min_codecs_supported; unsigned int *concurrency_entry; u8 max_instance, codec_type; struct dec_instance_table *dec_instance_list; dec_instance_list = (struct dec_instance_table *) (audpp->dec_database->dec_instance_list); mutex_lock(audpp->lock_dec); /* Represents in bit mask */ mode = ((dec_attrb & AUDPP_MODE_MASK) << 16); codec = (1 << (dec_attrb & AUDPP_CODEC_MASK)); codec_type = (dec_attrb & AUDPP_CODEC_MASK); /* Find whether same/different codec instances are running */ audpp->decoder_count++; audpp->codec_cnt[codec_type]++; max_instance = 0; /*if different instance of codec*/ if (audpp->codec_cnt[codec_type] < audpp->decoder_count) { max_instance = audpp->codec_max_instances; /* Get the maximum no. of instances that can be supported */ for (idx = 0; idx < MSM_MAX_DEC_CNT; idx++) { if (audpp->codec_cnt[idx]) { if ((dec_instance_list + audpp->op_mode * MSM_MAX_DEC_CNT + idx)-> max_instances_diff_dec < max_instance) { max_instance = (dec_instance_list + audpp->op_mode * MSM_MAX_DEC_CNT + idx)-> max_instances_diff_dec; } } } /* if different codec type, should not cross maximum other supported */ if (audpp->decoder_count > (max_instance + 1)) { MM_ERR("Can not support, already reached max\n"); audpp->decoder_count--; audpp->codec_cnt[codec_type]--; goto done; } audpp->codec_max_instances = max_instance; MM_DBG("different codec running\n"); } else { max_instance = (dec_instance_list + audpp->op_mode * MSM_MAX_DEC_CNT + codec_type)-> max_instances_same_dec; /* if same codec type, should not cross maximum supported */ if (audpp->decoder_count > max_instance) { MM_ERR("Can not support, already reached max\n"); audpp->decoder_count--; audpp->codec_cnt[codec_type]--; goto done; } audpp->codec_max_instances = max_instance; MM_DBG("same codec running\n"); } /* Point to Last entry of the row */ concurrency_entry = ((audpp->dec_database->dec_concurrency_table + ((audpp->concurrency + 1) * (audpp->dec_database->num_dec))) - 1); lidx = audpp->dec_database->num_dec; min_codecs_supported = sizeof(unsigned int) * 8; MM_DBG("mode = 0x%08x codec = 0x%08x\n", mode, codec); for (idx = lidx; idx > 0; idx--, concurrency_entry--) { if (!(audpp->dec_inuse & (1 << (idx - 1)))) { if (((mode & *concurrency_entry) == mode) && (codec & *concurrency_entry)) { /* Check supports minimum number codecs */ codecs_supported = audpp->dec_database->dec_info_list[idx - 1]. nr_codec_support; if (codecs_supported < min_codecs_supported) { lidx = idx - 1; min_codecs_supported = codecs_supported; } } } } if (lidx < audpp->dec_database->num_dec) { audpp->dec_inuse |= (1 << lidx); *module_name = audpp->dec_database->dec_info_list[lidx].module_name; *queueid = audpp->dec_database->dec_info_list[lidx].module_queueid; decid = audpp->dec_database->dec_info_list[lidx].module_decid; audpp->dec_info_table[lidx].codec = (dec_attrb & AUDPP_CODEC_MASK); audpp->dec_info_table[lidx].pid = current->pid; /* point to row to get supported operation */ concurrency_entry = ((audpp->dec_database->dec_concurrency_table + ((audpp->concurrency) * (audpp->dec_database->num_dec))) + lidx); decid |= ((*concurrency_entry & AUDPP_OP_MASK) >> 12); MM_INFO("decid =0x%08x module_name=%s, queueid=%d \n", decid, *module_name, *queueid); } done: mutex_unlock(audpp->lock_dec); return decid; } EXPORT_SYMBOL(audpp_adec_alloc); void audpp_adec_free(int decid) { struct audpp_state *audpp = &the_audpp_state; int idx; mutex_lock(audpp->lock_dec); for (idx = audpp->dec_database->num_dec; idx > 0; idx--) { if (audpp->dec_database->dec_info_list[idx - 1].module_decid == decid) { audpp->decoder_count--; audpp->\ codec_cnt[audpp->dec_info_table[idx - 1].codec]--; audpp->dec_inuse &= ~(1 << (idx - 1)); audpp->dec_info_table[idx - 1].codec = -1; audpp->dec_info_table[idx - 1].pid = 0; MM_INFO("free decid =%d \n", decid); break; } } mutex_unlock(audpp->lock_dec); return; } EXPORT_SYMBOL(audpp_adec_free); static ssize_t concurrency_show(struct device *dev, struct device_attribute *attr, char *buf) { struct audpp_state *audpp = &the_audpp_state; int rc; mutex_lock(audpp->lock_dec); rc = sprintf(buf, "%ld\n", audpp->concurrency); mutex_unlock(audpp->lock_dec); return rc; } static ssize_t concurrency_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct audpp_state *audpp = &the_audpp_state; unsigned long concurrency; int rc = -1; mutex_lock(audpp->lock_dec); if (audpp->dec_inuse) { MM_ERR("Can not change profile, while playback in progress\n"); goto done; } rc = strict_strtoul(buf, 10, &concurrency); if (!rc && (concurrency < audpp->dec_database->num_concurrency_support)) { audpp->concurrency = concurrency; MM_DBG("Concurrency case %ld\n", audpp->concurrency); rc = count; } else { MM_ERR("Not a valid Concurrency case\n"); rc = -EINVAL; } done: mutex_unlock(audpp->lock_dec); return rc; } static ssize_t decoder_info_show(struct device *dev, struct device_attribute *attr, char *buf); static struct device_attribute dev_attr_decoder[AUDPP_MAX_DECODER_CNT] = { __ATTR(decoder0, S_IRUGO, decoder_info_show, NULL), __ATTR(decoder1, S_IRUGO, decoder_info_show, NULL), __ATTR(decoder2, S_IRUGO, decoder_info_show, NULL), __ATTR(decoder3, S_IRUGO, decoder_info_show, NULL), __ATTR(decoder4, S_IRUGO, decoder_info_show, NULL), }; static ssize_t decoder_info_show(struct device *dev, struct device_attribute *attr, char *buf) { int cpy_sz = 0; struct audpp_state *audpp = &the_audpp_state; const ptrdiff_t off = attr - dev_attr_decoder; /* decoder number */ mutex_lock(audpp->lock_dec); cpy_sz += scnprintf(buf + cpy_sz, PAGE_SIZE - cpy_sz, "%d:", audpp->dec_info_table[off].codec); cpy_sz += scnprintf(buf + cpy_sz, PAGE_SIZE - cpy_sz, "%d\n", audpp->dec_info_table[off].pid); mutex_unlock(audpp->lock_dec); return cpy_sz; } static DEVICE_ATTR(concurrency, S_IWUSR | S_IRUGO, concurrency_show, concurrency_store); static int audpp_probe(struct platform_device *pdev) { int rc, idx; struct audpp_state *audpp = &the_audpp_state; audpp->concurrency = AUDPP_CONCURRENCY_DEFAULT; audpp->dec_database = (struct msm_adspdec_database *)pdev->dev.platform_data; MM_INFO("Number of decoder supported %d\n", audpp->dec_database->num_dec); MM_INFO("Number of concurrency supported %d\n", audpp->dec_database->num_concurrency_support); init_waitqueue_head(&audpp->event_wait); for (idx = 0; idx < audpp->dec_database->num_dec; idx++) { audpp->dec_info_table[idx].codec = -1; audpp->dec_info_table[idx].pid = 0; MM_INFO("module_name:%s\n", audpp->dec_database->dec_info_list[idx].module_name); MM_INFO("queueid:%d\n", audpp->dec_database->dec_info_list[idx].module_queueid); MM_INFO("decid:%d\n", audpp->dec_database->dec_info_list[idx].module_decid); MM_INFO("nr_codec_support:%d\n", audpp->dec_database->dec_info_list[idx]. nr_codec_support); } wake_lock_init(&audpp_wake_lock, WAKE_LOCK_SUSPEND, "audpp"); for (idx = 0; idx < audpp->dec_database->num_dec; idx++) { rc = device_create_file(&pdev->dev, &dev_attr_decoder[idx]); if (rc) goto err; } rc = device_create_file(&pdev->dev, &dev_attr_concurrency); audpp->op_mode = 0; /* Consider as non turbo mode */ if (rc) goto err; else goto done; err: while (idx--) device_remove_file(&pdev->dev, &dev_attr_decoder[idx]); done: return rc; } static struct platform_driver audpp_plat_driver = { .probe = audpp_probe, .driver = { .name = "msm_adspdec", .owner = THIS_MODULE, }, }; static int __init audpp_init(void) { return platform_driver_register(&audpp_plat_driver); } device_initcall(audpp_init);