From 7581982cee85c8e652bfefe7ddf997e6f4577307 Mon Sep 17 00:00:00 2001 From: Srinivasa Reddy M N Date: Thu, 20 Oct 2022 17:59:48 +0530 Subject: [PATCH] ASoC: Add support "adm cmd register" and "adm pp" events Added support for "ADM_CMD_REGISTER_EVENT" and "ADM_PP_EVENT" events. Signed-off-by: Srinivasa Reddy M N Change-Id: I80d8dc6d9fccdac67ea616d19415aa2133a3f370 --- asoc/msm-pcm-q6-v2.c | 29 ++- asoc/msm-pcm-routing-auto.c | 339 +++++++++++++++++++++++++++ asoc/msm-pcm-routing-v2.c | 50 ++++ asoc/msm-pcm-routing-v2.h | 3 + asoc/msm-qti-pp-config.c | 211 +++++++++++++++++ asoc/msm-qti-pp-config.h | 8 + dsp/q6adm.c | 186 +++++++++++++++ include/dsp/apr_audio-v2.h | 29 +++ include/dsp/q6adm-v2.h | 7 + include/uapi/audio/linux/msm_audio.h | 14 ++ 10 files changed, 875 insertions(+), 1 deletion(-) mode change 100755 => 100644 asoc/msm-pcm-routing-auto.c mode change 100755 => 100644 asoc/msm-pcm-routing-v2.c mode change 100755 => 100644 include/dsp/apr_audio-v2.h diff --git a/asoc/msm-pcm-q6-v2.c b/asoc/msm-pcm-q6-v2.c index 3d337efc98c1..1dfa857be5ea 100644 --- a/asoc/msm-pcm-q6-v2.c +++ b/asoc/msm-pcm-q6-v2.c @@ -565,6 +565,8 @@ static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream) uint16_t format = 0; uint16_t req_format = 0; uint16_t input_file_format = 0; + int port_id = 0, copp_idx = -1; + bool found = false; if (!component) { pr_err("%s: component is NULL\n", __func__); @@ -691,6 +693,15 @@ static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream) return ret; } + found = msm_pcm_routing_get_portid_copp_idx(soc_prtd->dai_link->id, + SESSION_TYPE_RX, &port_id, &copp_idx); + if (found) { + q6adm_update_rtd_info(soc_prtd, port_id, copp_idx, + soc_prtd->dai_link->id, 1); + } else { + pr_err("%s: copp_idx not found\n", __func__); + } + /*Format block is configure with input file bits_per_sample*/ switch (input_file_format) { case SNDRV_PCM_FORMAT_S32_LE: @@ -1070,8 +1081,10 @@ static int msm_pcm_open(struct snd_pcm_substream *substream) prtd->reset_event = false; runtime->private_data = prtd; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { msm_adsp_init_mixer_ctl_pp_event_queue(soc_prtd); + msm_adsp_init_mixer_ctl_adm_pp_event_queue(soc_prtd); + } /* Vote to update the Rx thread priority to RT Thread for playback */ if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) && @@ -1189,6 +1202,8 @@ static int msm_pcm_playback_close(struct snd_pcm_substream *substream) uint32_t timeout; int dir = 0; int ret = 0; + int port_id = 0, copp_idx = -1; + bool found = false; pr_debug("%s: cmd_pending 0x%lx\n", __func__, prtd->cmd_pending); @@ -1239,9 +1254,21 @@ static int msm_pcm_playback_close(struct snd_pcm_substream *substream) prtd->audio_client); q6asm_audio_client_free(prtd->audio_client); } + + found = msm_pcm_routing_get_portid_copp_idx(soc_prtd->dai_link->id, + SESSION_TYPE_RX, &port_id, &copp_idx); + if (found) { + q6adm_update_rtd_info(soc_prtd, port_id, copp_idx, + soc_prtd->dai_link->id, 0); + q6adm_clear_callback(); + } else { + pr_err("%s: copp_idx not found\n", __func__); + } + msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->id, SNDRV_PCM_STREAM_PLAYBACK); msm_adsp_clean_mixer_ctl_pp_event_queue(soc_prtd); + msm_adsp_clean_mixer_ctl_adm_pp_event_queue(soc_prtd); kfree(prtd); runtime->private_data = NULL; mutex_unlock(&pdata->lock); diff --git a/asoc/msm-pcm-routing-auto.c b/asoc/msm-pcm-routing-auto.c old mode 100755 new mode 100644 index 4eccb8c4f398..f8e15f51fc24 --- a/asoc/msm-pcm-routing-auto.c +++ b/asoc/msm-pcm-routing-auto.c @@ -103,6 +103,10 @@ static int afe_loopback_tx_port_id = -1; static struct msm_pcm_channel_mixer ec_ref_chmix_cfg[MSM_FRONTEND_DAI_MAX]; static struct msm_ec_ref_port_cfg ec_ref_port_cfg; +static uint32_t adm_pp_reg_event_opcode[] = { + ADM_CMD_REGISTER_EVENT +}; + #define WEIGHT_0_DB 0x4000 /* all the FEs which can support channel mixer */ static struct msm_pcm_channel_mixer channel_mixer[MSM_FRONTEND_DAI_MM_SIZE]; @@ -2418,6 +2422,56 @@ int msm_pcm_routing_set_channel_mixer_runtime(int be_id, int session_id, } EXPORT_SYMBOL(msm_pcm_routing_set_channel_mixer_runtime); +/* + * msm_pcm_routing_get_portid_copp_idx: + * update the port_id and copp_idx for a given + * fe_id + * + * @fe_id: front end id + * @session_type: indicates session is of type TX or RX + * port: port_id to be updated as output + * copp_idx: copp_idx is updated as output + * @stream_type: indicates either Audio or Listen stream type + */ +bool msm_pcm_routing_get_portid_copp_idx(int fe_id, + int session_type, int *port, int *copp_idx) +{ + int idx = 0; + bool found = false; + int be_index = 0, port_id = 0; + + pr_debug("%s:fe_id[%d] sess_type [%d]\n", + __func__, fe_id, session_type); + if (!is_mm_lsm_fe_id(fe_id)) { + /* bad ID assigned in machine driver */ + pr_err("%s: bad MM ID %d\n", __func__, fe_id); + return -EINVAL; + } + + for (be_index = 0; be_index < MSM_BACKEND_DAI_MAX; be_index++) { + port_id = msm_bedais[be_index].port_id; + if (!msm_bedais[be_index].active || + !test_bit(fe_id, &msm_bedais[be_index].fe_sessions[0])) + continue; + + for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) { + unsigned long copp_id = + session_copp_map[fe_id][session_type][be_index]; + if (test_bit(idx, &copp_id)) { + pr_debug("%s: port_id: %d, copp_idx:%d\n", + __func__, port_id, idx); + *port = port_id; + *copp_idx = idx; + found = true; + break; + } + } + } + + return found; +} +EXPORT_SYMBOL(msm_pcm_routing_get_portid_copp_idx); + int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode, int dspst_id, int stream_type) { @@ -32481,6 +32535,273 @@ static void msm_routing_unload_topology(uint32_t topology_id) } +static int msm_audio_get_copp_idx_from_port_id(int port_id, int session_type, + int *copp_idx) +{ + int i = 0, idx = 0, be_idx = 0; + int ret = 0; + unsigned long copp = 0; + + pr_debug("%s: Enter, port_id=%d\n", __func__, port_id); + + ret = q6audio_validate_port(port_id); + if (ret < 0) { + pr_err("%s: port validation failed id 0x%x ret %d\n", + __func__, port_id, ret); + + ret = -EINVAL; + goto done; + } + + for (be_idx = 0; be_idx < MSM_BACKEND_DAI_MAX; be_idx++) { + if (msm_bedais[be_idx].port_id == port_id) + break; + } + if (be_idx >= MSM_BACKEND_DAI_MAX) { + pr_err("%s: Invalid be id %d\n", __func__, be_idx); + + ret = -EINVAL; + goto done; + } + + for_each_set_bit(i, &msm_bedais[be_idx].fe_sessions[0], + MSM_FRONTEND_DAI_MAX) { + if (!(is_mm_lsm_fe_id(i) && + route_check_fe_id_adm_support(i))) + continue; + + for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) { + copp = session_copp_map[i] + [session_type][be_idx]; + if (test_bit(idx, &copp)) + break; + } + if (idx >= MAX_COPPS_PER_PORT) + continue; + else + break; + } + if (i >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s: Invalid FE, exiting\n", __func__); + + ret = -EINVAL; + goto done; + } + *copp_idx = idx; + pr_debug("%s: copp_idx=%d\n", __func__, *copp_idx); + +done: + return ret; +} + +static int msm_routing_put_copp_dtmf_module_enable + (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + uint8_t *be_id = NULL, be_id_val = 0; + int copp_idx = -1, port_id = 0; + struct param_hdr_v3 param_hdr = {0}; + int rc = 0; + u32 flag = (bool)ucontrol->value.integer.value[0]; + + be_id = (uint8_t *)ucontrol->value.bytes.data; + be_id_val = *be_id; + + if((be_id_val < 0) || (be_id_val >= MSM_BACKEND_DAI_MAX)) { + pr_err("%s: received invalid be_id %lu\n", __func__, be_id_val); + return -EINVAL; + } + + port_id = msm_bedais[be_id_val].port_id; + rc = msm_audio_get_copp_idx_from_port_id(port_id, SESSION_TYPE_RX, + &copp_idx); + + if (rc) { + pr_err(" %s:failure in getting copp_idx\n", __func__); + return rc; + } + + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = AUDPROC_MODULE_ID_DTMF_DETECTION; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AUDPROC_PARAM_ID_ENABLE; + param_hdr.param_size = 4; + + rc = adm_pack_and_set_one_pp_param(port_id, copp_idx, param_hdr, + (u8 *)&flag); + if (rc) + pr_err("%s: Failed to set adm_pack_and_set_one_pp_param, err %d\n", + __func__, rc); + + return rc; + +} + +static const struct snd_kcontrol_new copp_dtmf_detect_enable_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1 COPP DTMF Detect Enable", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, NULL, + msm_routing_put_copp_dtmf_module_enable), + SOC_SINGLE_EXT("MultiMedia6 COPP DTMF Detect Enable", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, NULL, + msm_routing_put_copp_dtmf_module_enable), + SOC_SINGLE_EXT("MultiMedia21 COPP DTMF Detect Enable", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, NULL, + msm_routing_put_copp_dtmf_module_enable), +}; + +static int adsp_copp_event_handler(uint32_t opcode, + uint32_t token_adm, uint32_t *payload, void *priv) +{ + struct snd_soc_pcm_runtime *rtd = priv; + int ret = 0; + + if (!rtd) { + pr_err("%s: rtd is NULL\n", __func__); + return -EINVAL; + } + + ret = msm_adsp_copp_inform_mixer_ctl(rtd, payload); + if (ret) { + pr_err("%s: failed to inform mixer ctrl. err = %d\n", + __func__, ret); + return -EINVAL; + } + + return ret; +} + +static int msm_routing_get_copp_callback_event(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + uint32_t payload_size = 0; + struct dsp_adm_callback_list *oldest_event = NULL; + unsigned long spin_flags = 0; + struct dsp_adm_callback_prtd *kctl_prtd = NULL; + int ret = 0; + + kctl_prtd = (struct dsp_adm_callback_prtd *) + kcontrol->private_data; + + if (kctl_prtd == NULL) { + pr_err("%s: ADM PP event queue is not initialized.\n", + __func__); + ret = -EINVAL; + goto done; + } + + spin_lock_irqsave(&kctl_prtd->prtd_spin_lock, spin_flags); + pr_debug("%s: %d events in queue.\n", __func__, kctl_prtd->event_count); + if (list_empty(&kctl_prtd->event_queue)) { + pr_err("%s: ADM PP event queue is empty.\n", __func__); + ret = -EINVAL; + spin_unlock_irqrestore(&kctl_prtd->prtd_spin_lock, spin_flags); + goto done; + } + + oldest_event = list_first_entry(&kctl_prtd->event_queue, + struct dsp_adm_callback_list, list); + list_del(&oldest_event->list); + kctl_prtd->event_count--; + spin_unlock_irqrestore(&kctl_prtd->prtd_spin_lock, spin_flags); + + payload_size = oldest_event->event.payload_len; + pr_debug("%s: event fetched: type %d length %d\n", + __func__, oldest_event->event.event_type, + oldest_event->event.payload_len); + memcpy(ucontrol->value.bytes.data, &oldest_event->event, + sizeof(struct msm_adsp_event_data) + payload_size); + kfree(oldest_event); + +done: + return ret; +} + +static int msm_routing_put_copp_callback_event(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s\n", __func__); + return 0; +} + +static int msm_copp_callback_event_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = + sizeof(((struct snd_ctl_elem_value *)0)->value.bytes.data); + + return 0; +} + +static const struct snd_kcontrol_new copp_callback_event_controls[] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "ADSP COPP Callback Event", + .info = msm_copp_callback_event_info, + .get = msm_routing_get_copp_callback_event, + .put = msm_routing_put_copp_callback_event, + }, +}; + +int msm_copp_event_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = + sizeof(((struct snd_ctl_elem_value *)0)->value.bytes.data); + + return 0; +} + +static int msm_routing_get_copp_event_cmd(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s\n", __func__); + return 0; +} + +static int msm_routing_put_copp_event_cmd(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_adm_event_data *ev = NULL; + int be_id = 0, ret = 0, param_size = 0, opcode = 0; + int copp_idx = -1, port_id = 0; + + ev = (struct msm_adm_event_data *)ucontrol->value.bytes.data; + param_size = ev->payload_length - sizeof(struct module_info_data); + opcode = adm_pp_reg_event_opcode[(ev->event_type) + - ADSP_ADM_SERVICE_ID]; + be_id = ev->mod_info.be_id; + port_id = msm_bedais[be_id].port_id; + + pr_debug("%s: port_id= 0x%x, be_id=%d\n", __func__,port_id, be_id); + + ret = msm_audio_get_copp_idx_from_port_id(port_id, SESSION_TYPE_RX, + &copp_idx); + + if (ret) { + pr_debug("%s:failure in getting copp_idx\n", __func__); + return ret; + } + + q6adm_register_callback(&adsp_copp_event_handler); + q6adm_send_event_register_cmd(port_id, copp_idx, (u8 *) (ev->payload), + param_size, opcode); + return ret; +} + +static const struct snd_kcontrol_new copp_event_controls[] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "COPP Event Cmd", + .info = msm_copp_event_info, + .get = msm_routing_get_copp_event_cmd, + .put = msm_routing_put_copp_event_cmd, + }, +}; + static int msm_routing_put_device_pp_params_mixer(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -33661,6 +33982,7 @@ static void snd_soc_dapm_add_routes_aux_pcm(struct snd_soc_component *component) /* Not used but frame seems to require it */ static int msm_routing_probe(struct snd_soc_component *component) { + struct snd_kcontrol *kctl = NULL; snd_soc_dapm_new_controls(&component->dapm, msm_qdsp6_widgets, ARRAY_SIZE(msm_qdsp6_widgets)); @@ -33718,10 +34040,27 @@ static int msm_routing_probe(struct snd_soc_component *component) ARRAY_SIZE(asrc_config_controls)); snd_soc_add_component_controls(component, asrc_start_controls, ARRAY_SIZE(asrc_start_controls)); + snd_soc_add_component_controls(component, + copp_dtmf_detect_enable_mixer_controls, + ARRAY_SIZE(copp_dtmf_detect_enable_mixer_controls)); + #ifdef CONFIG_MSM_INTERNAL_MCLK snd_soc_add_component_controls(component, internal_mclk_control, ARRAY_SIZE(internal_mclk_control)); #endif + snd_soc_add_component_controls(component, copp_callback_event_controls, + ARRAY_SIZE(copp_callback_event_controls)); + snd_soc_add_component_controls(component, copp_event_controls, + ARRAY_SIZE(copp_event_controls)); + + kctl = snd_soc_card_get_kcontrol(component->dapm.card, + "ADSP COPP Callback Event"); + if (!kctl) { + pr_err("%s: failed to get kctl %s.\n", __func__, + "ADSP COPP Callback Event"); + return -EINVAL; + } + kctl->private_data = NULL; return 0; } diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c old mode 100755 new mode 100644 index 3849f6470242..6ec26e7d53b9 --- a/asoc/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -2458,6 +2458,56 @@ int msm_pcm_routing_set_channel_mixer_runtime(int be_id, int session_id, } EXPORT_SYMBOL(msm_pcm_routing_set_channel_mixer_runtime); +/* + * msm_pcm_routing_get_portid_copp_idx: + * update the port_id and copp_idx for a given + * fe_id + * + * @fe_id: front end id + * @session_type: indicates session is of type TX or RX + * port: port_id to be updated as output + * copp_idx: copp_idx is updated as output + * @stream_type: indicates either Audio or Listen stream type + */ +bool msm_pcm_routing_get_portid_copp_idx(int fe_id, + int session_type, int *port, int *copp_idx) +{ + int idx = 0; + bool found = false; + int be_index = 0, port_id = 0; + + pr_debug("%s:fe_id[%d] sess_type [%d]\n", + __func__, fe_id, session_type); + if (!is_mm_lsm_fe_id(fe_id)) { + /* bad ID assigned in machine driver */ + pr_err("%s: bad MM ID %d\n", __func__, fe_id); + return -EINVAL; + } + + for (be_index = 0; be_index < MSM_BACKEND_DAI_MAX; be_index++) { + port_id = msm_bedais[be_index].port_id; + if (!msm_bedais[be_index].active || + !test_bit(fe_id, &msm_bedais[be_index].fe_sessions[0])) + continue; + + for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) { + unsigned long copp_id = + session_copp_map[fe_id][session_type][be_index]; + if (test_bit(idx, &copp_id)) { + pr_debug("%s: port_id: %d, copp_idx:%d\n", + __func__, port_id, idx); + *port = port_id; + *copp_idx = idx; + found = true; + break; + } + } + } + + return found; +} +EXPORT_SYMBOL(msm_pcm_routing_get_portid_copp_idx); + int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode, int dspst_id, int stream_type) { diff --git a/asoc/msm-pcm-routing-v2.h b/asoc/msm-pcm-routing-v2.h index 752fe58c1ec5..d57851b7bec4 100644 --- a/asoc/msm-pcm-routing-v2.h +++ b/asoc/msm-pcm-routing-v2.h @@ -897,4 +897,7 @@ int snd_pcm_add_usr_ctls(struct snd_pcm *pcm, int stream, unsigned long private_value, struct snd_pcm_usr **info_ret); #endif + +bool msm_pcm_routing_get_portid_copp_idx(int fe_id, + int session_type, int *port_id, int *copp_idx); #endif /*_MSM_PCM_H*/ diff --git a/asoc/msm-qti-pp-config.c b/asoc/msm-qti-pp-config.c index 2e62922dc805..cdc0f38168f5 100644 --- a/asoc/msm-qti-pp-config.c +++ b/asoc/msm-qti-pp-config.c @@ -1207,6 +1207,217 @@ done: return ret; } +int msm_adsp_init_mixer_ctl_adm_pp_event_queue(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_kcontrol *kctl = NULL; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + const char *mixer_ctl_name = DSP_ADM_CALLBACK; + struct dsp_adm_callback_prtd *kctl_prtd = NULL; + + if (!rtd) { + pr_err("%s: rtd is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -EINVAL; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s", mixer_ctl_name); + kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str); + kfree(mixer_str); + if (!kctl) { + pr_err("%s: failed to get kctl.\n", __func__); + ret = -EINVAL; + goto done; + } + + if (kctl->private_data != NULL) { + pr_err("%s: kctl_prtd is not NULL at initialization.\n", + __func__); + return -EINVAL; + } + + kctl_prtd = kzalloc(sizeof(struct dsp_adm_callback_prtd), + GFP_KERNEL); + if (!kctl_prtd) { + ret = -ENOMEM; + goto done; + } + + spin_lock_init(&kctl_prtd->prtd_spin_lock); + INIT_LIST_HEAD(&kctl_prtd->event_queue); + kctl_prtd->event_count = 0; + kctl->private_data = kctl_prtd; + +done: + return ret; +} + +int msm_adsp_clean_mixer_ctl_adm_pp_event_queue(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_kcontrol *kctl = NULL; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + struct dsp_adm_callback_list *node = NULL, *n = NULL; + unsigned long spin_flags = 0; + const char *mixer_ctl_name = DSP_ADM_CALLBACK; + struct dsp_adm_callback_prtd *kctl_prtd = NULL; + + if (!rtd) { + pr_err("%s: rtd is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -EINVAL; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s", mixer_ctl_name); + kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str); + kfree(mixer_str); + if (!kctl) { + pr_err("%s: failed to get kctl.\n", __func__); + ret = -EINVAL; + goto done; + } + + kctl_prtd = (struct dsp_adm_callback_prtd *) + kctl->private_data; + if (kctl_prtd != NULL) { + spin_lock_irqsave(&kctl_prtd->prtd_spin_lock, spin_flags); + /* clean the queue */ + list_for_each_entry_safe(node, n, + &kctl_prtd->event_queue, list) { + list_del(&node->list); + kctl_prtd->event_count--; + pr_debug("%s: %d remaining events after del.\n", + __func__, kctl_prtd->event_count); + kfree(node); + } + spin_unlock_irqrestore(&kctl_prtd->prtd_spin_lock, spin_flags); + } + + kfree(kctl_prtd); + kctl->private_data = NULL; + +done: + return ret; +} + +int msm_adsp_copp_inform_mixer_ctl(struct snd_soc_pcm_runtime *rtd, + uint32_t *payload) +{ + /* adsp adm pp event notifier */ + struct snd_kcontrol *kctl = NULL; + struct snd_ctl_elem_value control = {0}; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + struct dsp_adm_callback_list *new_event = NULL; + struct dsp_adm_callback_list *oldest_event = NULL; + unsigned long spin_flags = 0; + struct dsp_adm_callback_prtd *kctl_prtd = NULL; + struct msm_adsp_event_data *event_data = NULL; + const char *mixer_ctl_name = DSP_ADM_CALLBACK; + struct snd_ctl_elem_info kctl_info = {0}; + + if (!rtd || !payload) { + pr_err("%s: %s is NULL\n", __func__, + (!rtd) ? "rtd" : "payload"); + ret = -EINVAL; + goto done; + } + + if (rtd->card->snd_card == NULL) { + pr_err("%s: snd_card is null.\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1; + mixer_str = kzalloc(ctl_len, GFP_ATOMIC); + if (!mixer_str) { + ret = -EINVAL; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s", mixer_ctl_name); + kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str); + kfree(mixer_str); + if (!kctl) { + pr_err("%s: failed to get kctl.\n", __func__); + ret = -EINVAL; + goto done; + } + + event_data = (struct msm_adsp_event_data *)payload; + kctl->info(kctl, &kctl_info); + if (sizeof(struct msm_adsp_event_data) + + event_data->payload_len > kctl_info.count) { + pr_err("%s: payload length exceeds limit of %u bytes.\n", + __func__, kctl_info.count); + ret = -EINVAL; + goto done; + } + + kctl_prtd = (struct dsp_adm_callback_prtd *) + kctl->private_data; + if (kctl_prtd == NULL) { + /* queue is not initialized */ + ret = -EINVAL; + pr_err("%s: event queue is not initialized.\n", __func__); + goto done; + } + + new_event = kzalloc(sizeof(struct dsp_adm_callback_list) + + event_data->payload_len + sizeof(uint32_t), + GFP_ATOMIC); + if (new_event == NULL) { + ret = -ENOMEM; + goto done; + } + memcpy((void *)&new_event->event, (void *)payload, + event_data->payload_len + + sizeof(struct msm_adsp_event_data)); + + + spin_lock_irqsave(&kctl_prtd->prtd_spin_lock, spin_flags); + while (kctl_prtd->event_count >= DSP_ADM_CALLBACK_QUEUE_SIZE) { + pr_info("%s: queue of size %d is full. delete oldest one.\n", + __func__, DSP_ADM_CALLBACK_QUEUE_SIZE); + oldest_event = list_first_entry(&kctl_prtd->event_queue, + struct dsp_adm_callback_list, list); + pr_info("%s: event deleted: type %d length %d\n", + __func__, oldest_event->event.event_type, + oldest_event->event.payload_len); + list_del(&oldest_event->list); + kctl_prtd->event_count--; + kfree(oldest_event); + } + + list_add_tail(&new_event->list, &kctl_prtd->event_queue); + kctl_prtd->event_count++; + spin_unlock_irqrestore(&kctl_prtd->prtd_spin_lock, spin_flags); + + control.id = kctl->id; + + snd_ctl_notify(rtd->card->snd_card, + SNDRV_CTL_EVENT_MASK_INFO, + &control.id); + +done: + return ret; +} + int msm_adsp_inform_mixer_ctl(struct snd_soc_pcm_runtime *rtd, uint32_t *payload) { diff --git a/asoc/msm-qti-pp-config.h b/asoc/msm-qti-pp-config.h index 7fddb9f49840..b91746e34d36 100644 --- a/asoc/msm-qti-pp-config.h +++ b/asoc/msm-qti-pp-config.h @@ -9,6 +9,14 @@ #include #define DSP_BIT_WIDTH_MIXER_CTL "ASM Bit Width" #ifdef CONFIG_QTI_PP + +int msm_adsp_adm_inform_mixer_ctl(struct snd_soc_pcm_runtime *rtd, + uint32_t *payload); +int msm_adsp_init_mixer_ctl_adm_pp_event_queue(struct snd_soc_pcm_runtime *rtd); +int msm_adsp_clean_mixer_ctl_adm_pp_event_queue( + struct snd_soc_pcm_runtime *rtd); +int msm_adsp_copp_inform_mixer_ctl(struct snd_soc_pcm_runtime *rtd, + uint32_t *payload); int msm_adsp_inform_mixer_ctl(struct snd_soc_pcm_runtime *rtd, uint32_t *payload); int msm_adsp_init_mixer_ctl_pp_event_queue(struct snd_soc_pcm_runtime *rtd); diff --git a/dsp/q6adm.c b/dsp/q6adm.c index eae22f7c528f..f2ae327ee554 100644 --- a/dsp/q6adm.c +++ b/dsp/q6adm.c @@ -52,6 +52,9 @@ enum adm_cal_status { ADM_STATUS_MAX, }; +typedef int (*adm_cb)(uint32_t opcode, uint32_t token, + uint32_t *pp_event_package, void *pvt); + struct adm_copp { atomic_t id[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; @@ -71,6 +74,8 @@ struct adm_copp { uint32_t adm_delay[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; unsigned long adm_status[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; atomic_t token[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; + adm_cb cb; + void *priv[AFE_MAX_PORTS][MAX_COPPS_PER_PORT][MAX_FE_ID]; }; struct source_tracking_data { @@ -163,6 +168,93 @@ static int adm_arrange_mch_map_v8( int channel_mode, int port_idx); +static uint32_t adm_pp_raise_event_opcode[] = { + ADM_PP_EVENT }; + +int q6adm_send_event_register_cmd(int port_id, int copp_idx, u8 *data, + int param_size, int opcode) +{ + struct adm_register_event *adm_reg_params = NULL; + int ret = 0, port_idx = 0, sz = 0; + + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id %#x\n", __func__, port_id); + return -EINVAL; + } + + sz = sizeof(struct apr_hdr) + param_size; + adm_reg_params = kzalloc(sz, GFP_KERNEL); + + if (!adm_reg_params) + return -ENOMEM; + + memcpy(adm_reg_params->payload, data, param_size); + + adm_reg_params->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + adm_reg_params->hdr.src_svc = APR_SVC_ADM; + adm_reg_params->hdr.src_domain = APR_DOMAIN_APPS; + adm_reg_params->hdr.src_port = port_id; + adm_reg_params->hdr.dest_svc = APR_SVC_ADM; + adm_reg_params->hdr.dest_domain = APR_DOMAIN_ADSP; + adm_reg_params->hdr.dest_port = + atomic_read(&this_adm.copp.id[port_idx][copp_idx]); + adm_reg_params->hdr.token = port_idx << 16 | copp_idx; + adm_reg_params->hdr.opcode = opcode; + adm_reg_params->hdr.pkt_size = sz; + + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], 0); + ret = apr_send_pkt(this_adm.apr, (uint32_t *)adm_reg_params); + if (ret < 0) { + pr_err("%s: Set adm register params failed port %d rc %d\n", + __func__, port_id, ret); + ret = -EINVAL; + goto fail_cmd; + } + + ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read( + &this_adm.copp.stat[port_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: set params timed out port = %d\n", + __func__, port_id); + ret = -ETIMEDOUT; + goto fail_cmd; + } + ret = 0; +fail_cmd: + kfree(adm_reg_params); + + return ret; +} +EXPORT_SYMBOL(q6adm_send_event_register_cmd); + + +static int is_adsp_adm_raise_event(uint32_t cmd) +{ + int i = 0; + for (i = 0; i < ARRAY_SIZE(adm_pp_raise_event_opcode); i++) { + if (cmd == adm_pp_raise_event_opcode[i]) + return i; + } + return -EINVAL; +} + +void q6adm_register_callback(void *cb) +{ + this_adm.copp.cb = cb; +} +EXPORT_SYMBOL(q6adm_register_callback); + +void q6adm_clear_callback(void) +{ + this_adm.copp.cb = NULL; +} +EXPORT_SYMBOL(q6adm_clear_callback); + /** * adm_validate_and_get_port_index - * validate given port id @@ -1578,6 +1670,9 @@ static int32_t adm_callback(struct apr_client_data *data, void *priv) int port_idx, copp_idx, idx, client_id; int num_modules; int ret; + int payload_size = 0, i = 0; + struct msm_adsp_event_data *pp_event_package = NULL; + struct adm_usr_info usr_data = {0}; if (data == NULL) { pr_err("%s: data parameter is null\n", __func__); @@ -1749,6 +1844,13 @@ static int32_t adm_callback(struct apr_client_data *data, void *priv) pr_err("%s: ADM get topo list error = %d\n", __func__, payload[1]); break; + case ADM_CMD_REGISTER_EVENT: + pr_debug("%s:ADM_CMD_REGISTER_EVENT\n", + __func__); + if (payload[1] != 0) + pr_err("%s: ADM_CMD_REGISTER_EVENT error = %d\n", + __func__, payload[1]); + break; default: pr_err("%s: Unknown Cmd: 0x%x\n", __func__, payload[0]); @@ -1859,6 +1961,63 @@ static int32_t adm_callback(struct apr_client_data *data, void *priv) atomic_set(&this_adm.adm_stat, 0); wake_up(&this_adm.adm_wait); break; + case ADM_PP_EVENT: + if (data->payload_size < (2 * sizeof(uint32_t))) { + pr_err("%s: payload has invalid size %d\n", + __func__, data->payload_size); + return -EINVAL; + } + + pr_debug("%s: ADM_PP_EVENT payload[0][0x%x] payload[1][0x%x]\n", + __func__, payload[0], payload[1]); + + ret = is_adsp_adm_raise_event(data->opcode); + + if (ret < 0) + return 0; + + /* + * repack payload for adm_copp_pp_event + * package is composed of event type + size + payload + */ + payload_size = data->payload_size; + pp_event_package = kzalloc(payload_size + + sizeof(struct msm_adsp_event_data) + , GFP_ATOMIC); + + if (!pp_event_package) + return -ENOMEM; + + pp_event_package->event_type = ret + + ADSP_ADM_SERVICE_ID; + usr_data.service_id = ADSP_ADM_SERVICE_ID; + usr_data.token_coppidx = data->token; + + pp_event_package->payload_len = data->payload_size + + sizeof(struct adm_usr_info); + + memcpy((void *)pp_event_package->payload, &(usr_data), + sizeof(struct adm_usr_info)); + + memcpy((void *)pp_event_package->payload + + sizeof(struct adm_usr_info), + data->payload, data->payload_size); + if (this_adm.copp.cb) { + for (i = 0; i < MAX_FE_ID; i++) { + if (this_adm.copp.priv[port_idx] + [copp_idx][i]) { + pr_debug("%s: calling adm callback for feid %d port_idx %d copp_idx %d\n", + __func__, i, port_idx, copp_idx); + this_adm.copp.cb(data->opcode, + data->token, + (void *)pp_event_package, + this_adm.copp.priv[port_idx] + [copp_idx][i]); + } + } + } + kfree(pp_event_package); + break; default: pr_err("%s: Unknown cmd:0x%x\n", __func__, data->opcode); @@ -3006,6 +3165,33 @@ static int adm_arrange_mch_ep2_map_v8( return rc; } +int q6adm_update_rtd_info(void *rtd, int port_id, + int copp_idx, int fe_id, int enable) +{ + int port_idx = 0; + + port_id = q6audio_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + + if (port_idx < 0) { + pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id); + return -EINVAL; + } + + pr_debug("%s: port_id %#x copp_idx %d fe_id %d enable %d\n", + __func__, port_id, copp_idx, fe_id, enable); + + if (enable) { + this_adm.copp.priv[port_idx][copp_idx][fe_id] = rtd; + } + else { + this_adm.copp.priv[port_idx][copp_idx][fe_id] = NULL; + } + + return 0; +} +EXPORT_SYMBOL(q6adm_update_rtd_info); + static int adm_copp_set_ec_ref_mfc_cfg_v2(int port_id, int copp_idx, int sample_rate, int bps, struct msm_pcm_channel_mixer *cfg) diff --git a/include/dsp/apr_audio-v2.h b/include/dsp/apr_audio-v2.h old mode 100755 new mode 100644 index b6bdfeaa2aa5..f1c73e5087b8 --- a/include/dsp/apr_audio-v2.h +++ b/include/dsp/apr_audio-v2.h @@ -30,6 +30,11 @@ struct param_outband { /* Instance ID definitions */ #define INSTANCE_ID_0 0x0000 +struct adm_register_event { + struct apr_hdr hdr; + __u8 payload[0]; +} __packed; + struct mem_mapping_hdr { /* * LSW of parameter data payload address. Supported values: any. @@ -135,6 +140,10 @@ struct module_instance_info { #define ADM_CMD_MATRIX_MAP_ROUTINGS_V5 0x00010325 #define ADM_CMD_STREAM_DEVICE_MAP_ROUTINGS_V5 0x0001033D + +#define ADM_CMD_REGISTER_EVENT 0x00010365 +#define ADM_PP_EVENT 0x00010366 + /* Enumeration for an audio Rx matrix ID.*/ #define ADM_MATRIX_ID_AUDIO_RX 0 @@ -671,6 +680,26 @@ struct dsp_stream_callback_prtd { spinlock_t prtd_spin_lock; }; +#define DSP_ADM_CALLBACK "ADSP COPP Callback Event" +#define DSP_ADM_CALLBACK_QUEUE_SIZE 1024 + +struct dsp_adm_callback_list { + struct list_head list; + struct msm_adsp_event_data event; +}; + +struct adm_usr_info { + u32 service_id; + u32 reserved; + u32 token_coppidx; +}; + +struct dsp_adm_callback_prtd { + uint16_t event_count; + struct list_head event_queue; + spinlock_t prtd_spin_lock; +}; + /* set customized mixing on matrix mixer */ #define ADM_CMD_SET_PSPD_MTMX_STRTR_PARAMS_V5 0x00010344 struct adm_cmd_set_pspd_mtmx_strtr_params_v5 { diff --git a/include/dsp/q6adm-v2.h b/include/dsp/q6adm-v2.h index d6c20a962c64..a010b56cb5ac 100644 --- a/include/dsp/q6adm-v2.h +++ b/include/dsp/q6adm-v2.h @@ -16,6 +16,7 @@ #include #define MAX_MODULES_IN_TOPO 16 +#define MAX_FE_ID 33 #define ADM_GET_TOPO_MODULE_LIST_LENGTH\ ((MAX_MODULES_IN_TOPO + 1) * sizeof(uint32_t)) #define ADM_GET_TOPO_MODULE_INSTANCE_LIST_LENGTH \ @@ -246,4 +247,10 @@ void adm_set_native_mode(int mode); int adm_set_ffecns_freeze_event(bool ffecns_freeze_event); int adm_apr_send_pkt(void *data, wait_queue_head_t *wait, int port_idx, int copp_idx, int opcode); +void q6adm_register_callback(void *cb); +void q6adm_clear_callback(void); +int q6adm_send_event_register_cmd(int port_id, int copp_idx, u8 *data, + int param_size, int opcode); +int q6adm_update_rtd_info(void *rtd, int port_id, + int copp_idx, int fe_id, int enable); #endif /* __Q6_ADM_V2_H__ */ diff --git a/include/uapi/audio/linux/msm_audio.h b/include/uapi/audio/linux/msm_audio.h index c072b1363b9d..dda50ebc7fe1 100644 --- a/include/uapi/audio/linux/msm_audio.h +++ b/include/uapi/audio/linux/msm_audio.h @@ -456,6 +456,7 @@ struct msm_hwacc_effects_config { #define ADSP_STREAM_ENCDEC_EVENT 1 #define ADSP_STREAM_IEC_61937_FMT_UPDATE_EVENT 2 #define ADSP_STREAM_EVENT_MAX 3 +#define ADSP_ADM_SERVICE_ID 3 struct msm_adsp_event_data { __u32 event_type; @@ -463,4 +464,17 @@ struct msm_adsp_event_data { __u8 payload[0]; }; +struct module_info_data { + __u32 module_id; + __u32 instance_id; + __u32 be_id; + __u32 fe_id; +}; + +struct msm_adm_event_data { + __u32 event_type; + __u32 payload_length; + struct module_info_data mod_info; + __u8 payload[0]; +}; #endif