From bbf1413556d52b5e9cde530ab4ec6f7a6c0a82a8 Mon Sep 17 00:00:00 2001 From: Soumya Managoli Date: Wed, 21 Jun 2023 17:17:03 +0530 Subject: [PATCH 01/12] ASoC: msm-lsm-client: Integer overflow check Add integer overflow check for lsm_params_get_info size. Change-Id: I1130b5294f5de65eb8c595030c4db221337b1c8a Signed-off-by: Soumya Managoli --- asoc/msm-lsm-client.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/asoc/msm-lsm-client.c b/asoc/msm-lsm-client.c index aa38e8c71490..622880b0e9de 100644 --- a/asoc/msm-lsm-client.c +++ b/asoc/msm-lsm-client.c @@ -2653,6 +2653,15 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, err = -EFAULT; goto done; } + + if (temp_p_info.param_size > 0 && + ((INT_MAX - sizeof(temp_p_info)) < + temp_p_info.param_size)) { + pr_err("%s: Integer overflow\n", __func__); + err = -EINVAL; + goto done; + } + size = sizeof(temp_p_info) + temp_p_info.param_size; p_info = kzalloc(size, GFP_KERNEL); From e3971c3e31692d75c8d9ceff33a4342e485d07c1 Mon Sep 17 00:00:00 2001 From: Soumya Managoli Date: Fri, 23 Jun 2023 12:57:51 +0530 Subject: [PATCH 02/12] ASoC: dsp: q6core: Avoid use after free Add check for AVCS_CMD_RSP_LOAD_MODULE response payload to avoid its access after free. Change-Id: I3023e6676a27fe33d2cc0f44a49813f0ed0ebe3b Signed-off-by: Soumya Managoli --- dsp/q6core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dsp/q6core.c b/dsp/q6core.c index 6684b2a7d0df..df72815babc9 100644 --- a/dsp/q6core.c +++ b/dsp/q6core.c @@ -475,6 +475,8 @@ static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv) case AVCS_CMD_RSP_LOAD_MODULES: pr_debug("%s: Received AVCS_CMD_RSP_LOAD_MODULES\n", __func__); + if (!rsp_payload) + return -EINVAL; if (data->payload_size != ((sizeof(struct avcs_load_unload_modules_sec_payload) * rsp_payload->num_modules) + sizeof(uint32_t))) { pr_err("%s: payload size greater than expected size %d\n", @@ -1099,6 +1101,7 @@ int32_t q6core_avcs_load_unload_modules(struct avcs_load_unload_modules_payload done: kfree(mod); kfree(rsp_payload); + rsp_payload = NULL; mutex_unlock(&(q6core_lcl.cmd_lock)); return ret; } From a69e001b34dab9ac3731014666d463aa5917cfc5 Mon Sep 17 00:00:00 2001 From: Soumya Managoli Date: Mon, 24 Jul 2023 14:40:11 +0530 Subject: [PATCH 03/12] ASoC: Resolve use after free in listen sound client Updated get_param_payload buffer ptr to NULL after free to avoid use after free issue. Change-Id: I86da8c12a0bdccce690f67b037198b67640e339b Signed-off-by: Soumya Managoli --- asoc/msm-lsm-client.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/asoc/msm-lsm-client.c b/asoc/msm-lsm-client.c index aa38e8c71490..d65d6d652d06 100644 --- a/asoc/msm-lsm-client.c +++ b/asoc/msm-lsm-client.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2013-2020, The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. */ #include #include @@ -2325,6 +2325,7 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream, case SNDRV_LSM_GET_MODULE_PARAMS_32: { struct lsm_params_get_info_32 p_info_32, *param_info_rsp = NULL; struct lsm_params_get_info *p_info = NULL; + prtd->lsm_client->get_param_payload = NULL; memset(&p_info_32, 0 , sizeof(p_info_32)); if (!prtd->lsm_client->use_topology) { @@ -2375,6 +2376,7 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream, __func__, err); kfree(p_info); kfree(prtd->lsm_client->get_param_payload); + prtd->lsm_client->get_param_payload = NULL; goto done; } @@ -2385,6 +2387,7 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream, err = -ENOMEM; kfree(p_info); kfree(prtd->lsm_client->get_param_payload); + prtd->lsm_client->get_param_payload = NULL; goto done; } @@ -2409,6 +2412,7 @@ free: kfree(p_info); kfree(param_info_rsp); kfree(prtd->lsm_client->get_param_payload); + prtd->lsm_client->get_param_payload = NULL; break; } case SNDRV_LSM_REG_SND_MODEL_V2: @@ -2635,6 +2639,7 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, case SNDRV_LSM_GET_MODULE_PARAMS: { struct lsm_params_get_info temp_p_info, *p_info = NULL; + prtd->lsm_client->get_param_payload = NULL; memset(&temp_p_info, 0, sizeof(temp_p_info)); if (!prtd->lsm_client->use_topology) { @@ -2706,6 +2711,7 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, free: kfree(p_info); kfree(prtd->lsm_client->get_param_payload); + prtd->lsm_client->get_param_payload = NULL; break; } case SNDRV_LSM_EVENT_STATUS: From 01db7bcebc72d7d8dfbf52a3a9d2121c61ed20b5 Mon Sep 17 00:00:00 2001 From: Soumya Managoli Date: Mon, 24 Jul 2023 16:14:09 +0530 Subject: [PATCH 04/12] dsp: q6lsm: Add check for payload buffer Check get_param_payload buffer ptr before accessing. Change-Id: I5470983188dffeec14965a5cdec30747b98735e7 Signed-off-by: Soumya Managoli --- dsp/q6lsm.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dsp/q6lsm.c b/dsp/q6lsm.c index 4f1776dd221d..40a006b6e6f8 100644 --- a/dsp/q6lsm.c +++ b/dsp/q6lsm.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2013-2021, Linux Foundation. All rights reserved. + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. */ #include #include @@ -241,6 +242,11 @@ static int q6lsm_callback(struct apr_client_data *data, void *priv) goto done; } + if (!client->get_param_payload) { + pr_err("%s: invalid get_param_payload buffer ptr\n", __func__); + ret = -EINVAL; + goto done; + } memcpy((u8 *)client->get_param_payload, (u8 *)payload + payload_min_size_expected, param_size); done: From 48f5d8b079143c99aa40b923414be0ce9742bd00 Mon Sep 17 00:00:00 2001 From: Soumya Managoli Date: Thu, 3 Aug 2023 14:57:16 +0530 Subject: [PATCH 05/12] dsp: q6afe: Add check for fbsp state Added check for fbsp state in get_calib_data functions to avoid OOB read issues. Change-Id: I6e818892a6b76497aa41241db3849802f394160a Signed-off-by: Soumya Managoli --- dsp/q6afe.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/dsp/q6afe.c b/dsp/q6afe.c index ed856382ece3..3ab1499c8e32 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -10855,6 +10855,7 @@ static int afe_spv4_get_calib_data( struct param_hdr_v3 param_hdr; int port = SLIMBUS_4_TX; int ret = -EINVAL; + uint32_t th_vi_ca_state; if (!calib_resp) { pr_err("%s: Invalid params\n", __func__); @@ -10876,6 +10877,12 @@ static int afe_spv4_get_calib_data( __func__, port, param_hdr.param_id, ret); goto get_params_fail; } + th_vi_ca_state = this_afe.spv4_calib_data.res_cfg.th_vi_ca_state; + if (th_vi_ca_state < FBSP_INCORRECT_OP_MODE || + th_vi_ca_state > MAX_FBSP_STATE) { + pr_err("%s: invalid fbsp state %d\n", __func__, th_vi_ca_state); + goto get_params_fail; + } memcpy(&calib_resp->res_cfg, &this_afe.spv4_calib_data.res_cfg, sizeof(this_afe.calib_data.res_cfg)); pr_info("%s: state %s resistance %d %d\n", __func__, @@ -10894,6 +10901,7 @@ int afe_spk_prot_get_calib_data(struct afe_spkr_prot_get_vi_calib *calib_resp) struct param_hdr_v3 param_hdr; int port = SLIMBUS_4_TX; int ret = -EINVAL; + uint32_t th_vi_ca_state; if (!calib_resp) { pr_err("%s: Invalid params\n", __func__); @@ -10915,6 +10923,12 @@ int afe_spk_prot_get_calib_data(struct afe_spkr_prot_get_vi_calib *calib_resp) __func__, port, param_hdr.param_id, ret); goto get_params_fail; } + th_vi_ca_state = this_afe.calib_data.res_cfg.th_vi_ca_state; + if (th_vi_ca_state < FBSP_INCORRECT_OP_MODE || + th_vi_ca_state > MAX_FBSP_STATE) { + pr_err("%s: invalid fbsp state %d\n", __func__, th_vi_ca_state); + goto get_params_fail; + } memcpy(&calib_resp->res_cfg, &this_afe.calib_data.res_cfg, sizeof(this_afe.calib_data.res_cfg)); pr_info("%s: state %s resistance %d %d\n", __func__, From 79f709cdec3f032739b95467c3c70debb37ac13e Mon Sep 17 00:00:00 2001 From: Soumya Managoli Date: Thu, 20 Jul 2023 14:40:44 +0530 Subject: [PATCH 06/12] dsp: q6lsm: Address use after free for mmap handle The global declared mmap_handle can be left dangling for case when the handle is freed by the calling function. Fix is to address this. Also add a check to make sure the mmap_handle is accessed legally. Change-Id: I367f8a41339aa0025b545b125ee820220efedeee Signed-off-by: Soumya Managoli --- dsp/q6lsm.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/dsp/q6lsm.c b/dsp/q6lsm.c index 40a006b6e6f8..e1aad637d8be 100644 --- a/dsp/q6lsm.c +++ b/dsp/q6lsm.c @@ -484,6 +484,10 @@ static int q6lsm_apr_send_pkt(struct lsm_client *client, void *handle, } pr_debug("%s: enter wait %d\n", __func__, wait); + if (mmap_handle_p) { + pr_debug("%s: Invalid mmap_handle\n", __func__); + return -EINVAL; + } if (wait) mutex_lock(&lsm_common.apr_lock); if (mmap_p) { @@ -536,6 +540,7 @@ static int q6lsm_apr_send_pkt(struct lsm_client *client, void *handle, if (mmap_p && *mmap_p == 0) ret = -ENOMEM; + mmap_handle_p = NULL; pr_debug("%s: leave ret %d\n", __func__, ret); return ret; } @@ -2140,7 +2145,8 @@ static int q6lsm_mmapcallback(struct apr_client_data *data, void *priv) case LSM_SESSION_CMDRSP_SHARED_MEM_MAP_REGIONS: if (atomic_read(&client->cmd_state) == CMD_STATE_WAIT_RESP) { spin_lock_irqsave(&mmap_lock, flags); - *mmap_handle_p = command; + if (mmap_handle_p) + *mmap_handle_p = command; /* spin_unlock_irqrestore implies barrier */ spin_unlock_irqrestore(&mmap_lock, flags); atomic_set(&client->cmd_state, CMD_STATE_CLEARED); From 56fd62a423b410b6c5f57303cc6517ac79f71997 Mon Sep 17 00:00:00 2001 From: Soumya Managoli Date: Wed, 2 Aug 2023 13:24:08 +0530 Subject: [PATCH 07/12] dsp: Address information leak in dsp drivers Due to uninitialised variables used in q6voice and q6usm drivers, there is a possibility of stack information leak whenever lesser size data is copied. Fix is to address this. Change-Id: I6888b468ce50114be3139111ccf4696827540cfd Signed-off-by: Soumya Managoli --- dsp/q6usm.c | 2 ++ dsp/q6voice.c | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/dsp/q6usm.c b/dsp/q6usm.c index cd024fce8139..b9bdce20e02a 100644 --- a/dsp/q6usm.c +++ b/dsp/q6usm.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. */ #include #include @@ -898,6 +899,7 @@ int q6usm_enc_cfg_blk(struct us_client *usc, struct us_encdec_cfg *us_cfg) sizeof(struct usm_cfg_common)); /* Transparent data copy */ + memset(enc_cfg->enc_blk.transp_data, 0, sizeof (enc_cfg->enc_blk.transp_data)); memcpy(enc_cfg->enc_blk.transp_data, us_cfg->params, us_cfg->params_size); pr_debug("%s: cfg_size[%d], params_size[%d]\n", diff --git a/dsp/q6voice.c b/dsp/q6voice.c index 0f2fe8ccc1b4..f0f2a05b636d 100644 --- a/dsp/q6voice.c +++ b/dsp/q6voice.c @@ -1164,6 +1164,10 @@ static int voice_create_mvm_cvs_session(struct voice_data *v) common.mvs_info.media_type; cvs_full_ctl_cmd.cvs_session.network_id = common.mvs_info.network_type; + + memset(cvs_full_ctl_cmd.cvs_session.name, 0, + sizeof(cvs_full_ctl_cmd.cvs_session.name)); + strlcpy(cvs_full_ctl_cmd.cvs_session.name, "default q6 voice", strlen("default q6 voice")+1); @@ -3079,6 +3083,9 @@ static int voice_send_cvp_create_cmd(struct voice_data *v) VSS_IVOCPROC_PORT_ID_NONE; } + memset(cvp_session_cmd.cvp_session.name, 0, + sizeof(cvp_session_cmd.cvp_session.name)); + pr_debug("tx_topology: %d tx_port_id=%d, rx_port_id=%d, mode: 0x%x\n", cvp_session_cmd.cvp_session.tx_topology_id, cvp_session_cmd.cvp_session.tx_port_id, From eb692cb343f0ea7b210053947d126e89f434f57a Mon Sep 17 00:00:00 2001 From: Soumya Managoli Date: Mon, 31 Jul 2023 14:26:42 +0530 Subject: [PATCH 08/12] ASoC: msm-pcm-host-voice: Check validity of session idx Added check for voice session index. Change-Id: Ifff36add5d62f2fdc3395de1447075d297f2c2df Signed-off-by: Soumya Managoli --- asoc/msm-pcm-host-voice-v2.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/asoc/msm-pcm-host-voice-v2.c b/asoc/msm-pcm-host-voice-v2.c index ad5ab90bee1a..42c3a59a99ef 100644 --- a/asoc/msm-pcm-host-voice-v2.c +++ b/asoc/msm-pcm-host-voice-v2.c @@ -745,6 +745,13 @@ void hpcm_notify_evt_processing(uint8_t *data, char *session, return; } + if (prtd->mixer_conf.sess_indx < VOICE_INDEX || + prtd->mixer_conf.sess_indx >= MAX_SESSION) { + pr_err("%s:: Invalid session idx %d\n", + __func__, prtd->mixer_conf.sess_indx); + return; + } + if (notify_evt->tap_point == VSS_IVPCM_TAP_POINT_TX_DEFAULT) { tp = &prtd->session[prtd->mixer_conf.sess_indx].tx_tap_point; tmd = &prtd->mixer_conf.tx; From 9c91c3d179d7456581ffb459bb1a766e611ae624 Mon Sep 17 00:00:00 2001 From: Soumya Managoli Date: Fri, 4 Aug 2023 14:49:34 +0530 Subject: [PATCH 09/12] dsp: q6asm: Add check for ADSP payload size There is no check for the ADSP returned payload size for ASM_SESSION_CMD_GET_MTMX_STRTR_PARAMS_V2 cmd response. This can lead to buffer overread. Fix is to address this. Change-Id: I0bd6ee7f19823addc5dde1dfbb32b8a9b102a725 Signed-off-by: Soumya Managoli --- dsp/q6asm.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/dsp/q6asm.c b/dsp/q6asm.c index ea4db1956da2..5ba45f3c775d 100644 --- a/dsp/q6asm.c +++ b/dsp/q6asm.c @@ -2551,7 +2551,16 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) __func__, data->payload_size); break; case ASM_SESSION_CMDRSP_GET_MTMX_STRTR_PARAMS_V2: - q6asm_process_mtmx_get_param_rsp(ac, (void *) payload); + payload_size = sizeof(struct asm_mtmx_strtr_get_params_cmdrsp); + if (data->payload_size < payload_size) { + pr_err("%s: insufficient payload size = %d\n", + __func__, data->payload_size); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); + return -EINVAL; + } + q6asm_process_mtmx_get_param_rsp(ac, + (struct asm_mtmx_strtr_get_params_cmdrsp *) payload); break; case ASM_STREAM_PP_EVENT: case ASM_STREAM_CMD_ENCDEC_EVENTS: From 69a16a0e0fd012be6086e282117cb42cf02ba030 Mon Sep 17 00:00:00 2001 From: Jing Wang Date: Fri, 11 Aug 2023 16:50:03 +0800 Subject: [PATCH 10/12] asoc: Add jitter cleaner external clock switch Add jitter cleaner external clock switch. Change-Id: Iacdaef52ca0d00bb4423cb8a719ef5e955711619 Signed-off-by: Jing Wang --- asoc/msm-dai-q6-v2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/asoc/msm-dai-q6-v2.c b/asoc/msm-dai-q6-v2.c index 822273ff26d5..bde03c63f5a7 100644 --- a/asoc/msm-dai-q6-v2.c +++ b/asoc/msm-dai-q6-v2.c @@ -533,6 +533,7 @@ static int clk_id_index; static int clk_root_index; static int clk_attri_index; static int global_dyn_mclk_cfg_portid; +static bool jitter_cleaner_enable = false; // jitter cleaner ext clock enable/disable struct afe_param_id_clock_set_v2_t global_dyn_mclk_cfg = { .clk_set_minor_version = Q6AFE_LPASS_CLK_CONFIG_API_VERSION, .clk_id = Q6AFE_LPASS_CLK_ID_TER_PCM_IBIT, @@ -10755,7 +10756,7 @@ static int msm_dai_q6_dai_tdm_probe(struct snd_soc_dai *dai) } /* add AFE dyn mclk controls */ - if (!afe_dyn_mclk_control_added) { + if ((!afe_dyn_mclk_control_added) && (jitter_cleaner_enable)) { rc = msm_pcm_add_afe_dyn_mclk_control(dai); if (rc < 0) { dev_err(dai->dev, "%s: add AFE dyn mclk control failed DAI: %s\n", From 782c8273ed046cbaa8e7447b628ce80f35cf96dc Mon Sep 17 00:00:00 2001 From: Vijay Kumar Maddula Date: Fri, 28 Jul 2023 22:11:57 +0530 Subject: [PATCH 11/12] asoc: lahaina: add 16 channel support on tdm interface Added support for 16 channel playback and capture support on tdm interface Change-Id: Idbfe55f5ee29c3f01a379e24e85288321dbeee3b --- asoc/lahaina.c | 729 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 651 insertions(+), 78 deletions(-) diff --git a/asoc/lahaina.c b/asoc/lahaina.c index 191f4b9960ee..82b18769c339 100644 --- a/asoc/lahaina.c +++ b/asoc/lahaina.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -76,6 +76,7 @@ #define WCD_MBHC_HS_V_MAX 1600 #define TDM_CHANNEL_MAX 8 +#define TDM_SLOT_OFFSET_MAX 32 #define MI2S_NUM_CHANNELS 2 @@ -107,7 +108,7 @@ enum { TDM_PORT_MAX, }; -#define TDM_MAX_SLOTS 8 +#define TDM_MAX_SLOTS 16 #define TDM_SLOT_WIDTH_BITS 32 #define TDM_SLOT_WIDTH_BYTES TDM_SLOT_WIDTH_BITS/8 @@ -667,6 +668,59 @@ static struct dev_config afe_loopback_tx_cfg[] = { [AFE_LOOPBACK_TX_IDX] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, }; +/* TDM default slot config */ +struct tdm_slot_cfg { + u32 width; + u32 num; +}; + +static struct tdm_slot_cfg tdm_slot[TDM_INTERFACE_MAX] = { + /* PRI TDM */ + {32, 8}, + /* SEC TDM */ + {32, 8}, + /* TERT TDM */ + {32, 8}, + /* QUAT TDM */ + {32, 8}, + /* QUIN TDM */ + {32, 8}, + /* SEN TDM*/ + {32, 8} +}; + +static unsigned int tdm_rx_slot_offset + [TDM_INTERFACE_MAX][TDM_SLOT_OFFSET_MAX] = { + /* PRI TDM */ + {0, 4, 8, 12, 16, 20, 24, 0xFFFF}, + /* SEC TDM */ + {0, 4, 8, 12, 16, 20, 24, 0xFFFF}, + /* TERT TDM */ + {0, 4, 8, 12, 16, 20, 24, 0xFFFF}, + /* QUAT TDM */ + {0, 4, 8, 12, 16, 20, 24, 0xFFFF}, + /* QUIN TDM */ + {0, 4, 8, 12, 16, 20, 24, 0xFFFF}, + /* SEN TDM */ + {0, 4, 8, 12, 16, 20, 24, 0xFFFF} +}; + +static unsigned int tdm_tx_slot_offset + [TDM_INTERFACE_MAX][TDM_SLOT_OFFSET_MAX] = { + /* PRI TDM */ + {0, 4, 8, 12, 16, 20, 24, 0xFFFF}, + /* SEC TDM */ + {0, 4, 8, 12, 16, 20, 24, 0xFFFF}, + /* TERT TDM */ + {0, 4, 8, 12, 16, 20, 24, 0xFFFF}, + /* QUAT TDM */ + {0, 4, 8, 12, 16, 20, 24, 0xFFFF}, + /* QUIN TDM */ + {0, 4, 8, 12, 16, 20, 24, 0xFFFF}, + /* SEN TDM */ + {0, 4, 8, 12, 16, 20, 24, 0xFFFF} +}; + static int msm_vi_feed_tx_ch = 2; static const char *const vi_feed_ch_text[] = {"One", "Two"}; static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE", @@ -682,12 +736,23 @@ static char const *usb_sample_rate_text[] = {"KHZ_8", "KHZ_11P025", static const char *const usb_ch_text[] = {"One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight"}; -static char const *tdm_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_32", - "KHZ_48", "KHZ_176P4", - "KHZ_352P8"}; +static char const *tdm_sample_rate_text[] = {"KHZ_8", "KHZ_11P025", + "KHZ_16", "KHZ_22P05", + "KHZ_32", "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96", "KHZ_176P4", + "KHZ_192", "KHZ_352P8", "KHZ_384"}; +static const char *const tdm_slot_num_text[] = {"One", "Two", "Four", + "Eight", "Sixteen", "ThirtyTwo"}; +static const char *const tdm_slot_width_text[] = {"16", "24", "32"}; static char const *tdm_bit_format_text[] = {"S16_LE", "S24_LE", "S32_LE"}; -static char const *tdm_ch_text[] = {"One", "Two", "Three", "Four", - "Five", "Six", "Seven", "Eight"}; +static char const *tdm_ch_text[] = { + "One", "Two", "Three", "Four", "Five", "Six", "Seven", + "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", + "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", + "Nineteen", "Twenty", "TwentyOne", "TwentyTwo", "TwentyThree", + "TwentyFour", "TwentyFive", "TwentySix", "TwentySeven", + "TwentyEight", "TwentyNine", "Thirty", "ThirtyOne", "ThirtyTwo" +}; static const char *const auxpcm_rate_text[] = {"KHZ_8", "KHZ_16"}; static char const *mi2s_rate_text[] = {"KHZ_8", "KHZ_11P025", "KHZ_16", "KHZ_22P05", "KHZ_32", "KHZ_44P1", @@ -739,6 +804,8 @@ static SOC_ENUM_SINGLE_EXT_DECL(vi_feed_tx_chs, vi_feed_ch_text); static SOC_ENUM_SINGLE_EXT_DECL(proxy_rx_chs, ch_text); static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_sample_rate, tdm_sample_rate_text); static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_sample_rate, tdm_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_slot_num, tdm_slot_num_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_slot_width, tdm_slot_width_text); static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_format, tdm_bit_format_text); static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_format, tdm_bit_format_text); static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_chs, tdm_ch_text); @@ -1650,34 +1717,43 @@ static int proxy_rx_ch_put(struct snd_kcontrol *kcontrol, return 1; } +static int tdm_get_mode(struct snd_kcontrol *kcontrol) +{ + int mode; + + if (strnstr(kcontrol->id.name, "PRI", + sizeof(kcontrol->id.name))) { + mode = TDM_PRI; + } else if (strnstr(kcontrol->id.name, "SEC", + sizeof(kcontrol->id.name))) { + mode = TDM_SEC; + } else if (strnstr(kcontrol->id.name, "TERT", + sizeof(kcontrol->id.name))) { + mode = TDM_TERT; + } else if (strnstr(kcontrol->id.name, "QUAT", + sizeof(kcontrol->id.name))) { + mode = TDM_QUAT; + } else if (strnstr(kcontrol->id.name, "QUIN", + sizeof(kcontrol->id.name))) { + mode = TDM_QUIN; + } else if (strnstr(kcontrol->id.name, "SEN", + sizeof(kcontrol->id.name))) { + mode = TDM_SEN; + } else { + pr_err("%s: unsupported mode in: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + return mode; +} + static int tdm_get_port_idx(struct snd_kcontrol *kcontrol, struct tdm_port *port) { if (port) { - if (strnstr(kcontrol->id.name, "PRI", - sizeof(kcontrol->id.name))) { - port->mode = TDM_PRI; - } else if (strnstr(kcontrol->id.name, "SEC", - sizeof(kcontrol->id.name))) { - port->mode = TDM_SEC; - } else if (strnstr(kcontrol->id.name, "TERT", - sizeof(kcontrol->id.name))) { - port->mode = TDM_TERT; - } else if (strnstr(kcontrol->id.name, "QUAT", - sizeof(kcontrol->id.name))) { - port->mode = TDM_QUAT; - } else if (strnstr(kcontrol->id.name, "QUIN", - sizeof(kcontrol->id.name))) { - port->mode = TDM_QUIN; - } else if (strnstr(kcontrol->id.name, "SEN", - sizeof(kcontrol->id.name))) { - port->mode = TDM_SEN; - } else { - pr_err("%s: unsupported mode in: %s\n", - __func__, kcontrol->id.name); - return -EINVAL; - } - + port->mode = tdm_get_mode(kcontrol); + if (port->mode < 0) + return port->mode; if (strnstr(kcontrol->id.name, "RX_0", sizeof(kcontrol->id.name)) || strnstr(kcontrol->id.name, "TX_0", @@ -2072,6 +2148,263 @@ static int tdm_tx_ch_put(struct snd_kcontrol *kcontrol, return ret; } +static int tdm_get_slot_num_val(int slot_num) +{ + int slot_num_val; + + switch (slot_num) { + case 1: + slot_num_val = 0; + break; + case 2: + slot_num_val = 1; + break; + case 4: + slot_num_val = 2; + break; + case 8: + slot_num_val = 3; + break; + case 16: + slot_num_val = 4; + break; + case 32: + slot_num_val = 5; + break; + default: + slot_num_val = 5; + break; + } + return slot_num_val; +} + +static int tdm_get_slot_num(int value) +{ + int slot_num; + + switch (value) { + case 0: + slot_num = 1; + break; + case 1: + slot_num = 2; + break; + case 2: + slot_num = 4; + break; + case 3: + slot_num = 8; + break; + case 4: + slot_num = 16; + break; + case 5: + slot_num = 32; + break; + default: + slot_num = 8; + break; + } + return slot_num; +} + +static int tdm_slot_num_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int mode = tdm_get_mode(kcontrol); + + if (mode < 0) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + return mode; + } + ucontrol->value.enumerated.item[0] = + tdm_get_slot_num_val(tdm_slot[mode].num); + pr_debug("%s: mode = %d, tdm_slot_num = %d, item = %d\n", __func__, + mode, tdm_slot[mode].num, + ucontrol->value.enumerated.item[0]); + return 0; +} + +static int tdm_slot_num_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int mode = tdm_get_mode(kcontrol); + + if (mode < 0) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + return mode; + } + tdm_slot[mode].num = + tdm_get_slot_num(ucontrol->value.enumerated.item[0]); + pr_debug("%s: mode = %d, tdm_slot_num = %d, item = %d\n", __func__, + mode, tdm_slot[mode].num, + ucontrol->value.enumerated.item[0]); + return 0; +} + +static int tdm_get_slot_width_val(int slot_width) +{ + int slot_width_val; + + switch (slot_width) { + case 16: + slot_width_val = 0; + break; + case 24: + slot_width_val = 1; + break; + case 32: + slot_width_val = 2; + break; + default: + slot_width_val = 2; + break; + } + return slot_width_val; +} + +static int tdm_slot_width_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int mode = tdm_get_mode(kcontrol); + + if (mode < 0) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + return mode; + } + ucontrol->value.enumerated.item[0] = + tdm_get_slot_width_val(tdm_slot[mode].width); + pr_debug("%s: mode = %d, tdm_slot_width = %d, item = %d\n", __func__, + mode, tdm_slot[mode].width, + ucontrol->value.enumerated.item[0]); + return 0; +} + +static int tdm_get_slot_width(int value) +{ + int slot_width; + + switch (value) { + case 0: + slot_width = 16; + break; + case 1: + slot_width = 24; + break; + case 2: + slot_width = 32; + break; + default: + slot_width = 32; + break; + } + return slot_width; +} + +static int tdm_slot_width_put(struct snd_kcontrol *kcontrol, +struct snd_ctl_elem_value *ucontrol) +{ + int mode = tdm_get_mode(kcontrol); + + if (mode < 0) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + return mode; + } + tdm_slot[mode].width = + tdm_get_slot_width(ucontrol->value.enumerated.item[0]); + pr_debug("%s: mode = %d, tdm_slot_width = %d, item = %d\n", __func__, + mode, tdm_slot[mode].width, + ucontrol->value.enumerated.item[0]); + return 0; +} + +static int tdm_rx_slot_mapping_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int *slot_offset; + int i; + int mode = tdm_get_mode(kcontrol); + + if (mode < 0) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + return mode; + } + slot_offset = tdm_rx_slot_offset[mode]; + for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) { + ucontrol->value.integer.value[i] = slot_offset[i]; + pr_debug("%s: offset %d, value %d\n", + __func__, i, slot_offset[i]); + } + return 0; +} + +static int tdm_rx_slot_mapping_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int *slot_offset; + int i; + int mode = tdm_get_mode(kcontrol); + + if (mode < 0) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + return mode; + } + slot_offset = tdm_rx_slot_offset[mode]; + for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) { + slot_offset[i] = ucontrol->value.integer.value[i]; + pr_debug("%s: offset %d, value %d\n", + __func__, i, slot_offset[i]); + } + return 0; +} +static int tdm_tx_slot_mapping_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int *slot_offset; + int i; + int mode = tdm_get_mode(kcontrol); + + if (mode < 0) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + return mode; + } + slot_offset = tdm_tx_slot_offset[mode]; + for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) { + ucontrol->value.integer.value[i] = slot_offset[i]; + pr_debug("%s: offset %d, value %d\n", + __func__, i, slot_offset[i]); + } + return 0; +} + +static int tdm_tx_slot_mapping_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int *slot_offset; + int i; + int mode = tdm_get_mode(kcontrol); + + if (mode < 0) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + return mode; + } + slot_offset = tdm_tx_slot_offset[mode]; + for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) { + slot_offset[i] = ucontrol->value.integer.value[i]; + pr_debug("%s: offset %d, value %d\n", + __func__, i, slot_offset[i]); + } + return 0; +} + static int tdm_slot_map_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -2899,6 +3232,112 @@ err: return ret; } +static int lahaina_tdm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + if (cpu_dai->id == AFE_PORT_ID_PRIMARY_TDM_RX) { + channels->min = channels->max = + tdm_rx_cfg[TDM_PRI][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_PRI][TDM_0].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_PRI][TDM_0].sample_rate; + } else if (cpu_dai->id == AFE_PORT_ID_PRIMARY_TDM_TX) { + channels->min = channels->max = + tdm_tx_cfg[TDM_PRI][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_PRI][TDM_0].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_PRI][TDM_0].sample_rate; + } else if (cpu_dai->id == AFE_PORT_ID_SECONDARY_TDM_RX) { + channels->min = channels->max = + tdm_rx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_SEC][TDM_0].sample_rate; + } else if (cpu_dai->id == AFE_PORT_ID_SECONDARY_TDM_TX) { + channels->min = channels->max = + tdm_tx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_SEC][TDM_0].sample_rate; + } else if (cpu_dai->id == AFE_PORT_ID_TERTIARY_TDM_RX) { + channels->min = channels->max = + tdm_rx_cfg[TDM_TERT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_TERT][TDM_0].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_TERT][TDM_0].sample_rate; + } else if (cpu_dai->id == AFE_PORT_ID_TERTIARY_TDM_TX) { + channels->min = channels->max = + tdm_tx_cfg[TDM_TERT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_TERT][TDM_0].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_TERT][TDM_0].sample_rate; + } else if (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX) { + channels->min = channels->max = + tdm_rx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_QUAT][TDM_0].sample_rate; + } else if (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_TX) { + channels->min = channels->max = + tdm_tx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_QUAT][TDM_0].sample_rate; + } else if (cpu_dai->id == AFE_PORT_ID_QUINARY_TDM_RX) { + channels->min = channels->max = + tdm_rx_cfg[TDM_QUIN][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUIN][TDM_0].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_QUIN][TDM_0].sample_rate; + } else if (cpu_dai->id == AFE_PORT_ID_QUINARY_TDM_TX) { + channels->min = channels->max = + tdm_tx_cfg[TDM_QUIN][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUIN][TDM_0].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_QUIN][TDM_0].sample_rate; + } else if (cpu_dai->id == AFE_PORT_ID_SENARY_TDM_RX) { + channels->min = channels->max = + tdm_rx_cfg[TDM_SEN][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEN][TDM_0].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_SEN][TDM_0].sample_rate; + } else if (cpu_dai->id == AFE_PORT_ID_SENARY_TDM_TX) { + channels->min = channels->max = + tdm_tx_cfg[TDM_SEN][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_SEN][TDM_0].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_SEN][TDM_0].sample_rate; + } else { + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + + pr_debug("%s: dai id = 0x%x channels = %d rate = %d format = 0x%x\n", + __func__, cpu_dai->id, channels->max, rate->max, + params_format(params)); + + return 0; +} + static int cdc_dma_get_port_idx(struct snd_kcontrol *kcontrol) { int idx = 0; @@ -4175,6 +4614,30 @@ static const struct snd_kcontrol_new msm_common_snd_controls[] = { SOC_ENUM_EXT("SEN_TDM_TX_0 Channels", tdm_tx_chs, tdm_tx_ch_get, tdm_tx_ch_put), + SOC_ENUM_EXT("PRI_TDM SlotNumber", tdm_slot_num, + tdm_slot_num_get, tdm_slot_num_put), + SOC_ENUM_EXT("SEC_TDM SlotNumber", tdm_slot_num, + tdm_slot_num_get, tdm_slot_num_put), + SOC_ENUM_EXT("TERT_TDM SlotNumber", tdm_slot_num, + tdm_slot_num_get, tdm_slot_num_put), + SOC_ENUM_EXT("QUAT_TDM SlotNumber", tdm_slot_num, + tdm_slot_num_get, tdm_slot_num_put), + SOC_ENUM_EXT("QUIN_TDM SlotNumber", tdm_slot_num, + tdm_slot_num_get, tdm_slot_num_put), + SOC_ENUM_EXT("SEN_TDM SlotNumber", tdm_slot_num, + tdm_slot_num_get, tdm_slot_num_put), + SOC_ENUM_EXT("PRI_TDM SlotWidth", tdm_slot_width, + tdm_slot_width_get, tdm_slot_width_put), + SOC_ENUM_EXT("SEC_TDM SlotWidth", tdm_slot_width, + tdm_slot_width_get, tdm_slot_width_put), + SOC_ENUM_EXT("TERT_TDM SlotWidth", tdm_slot_width, + tdm_slot_width_get, tdm_slot_width_put), + SOC_ENUM_EXT("QUAT_TDM SlotWidth", tdm_slot_width, + tdm_slot_width_get, tdm_slot_width_put), + SOC_ENUM_EXT("QUIN_TDM SlotWidth", tdm_slot_width, + tdm_slot_width_get, tdm_slot_width_put), + SOC_ENUM_EXT("SEN_TDM SlotWidth", tdm_slot_width, + tdm_slot_width_get, tdm_slot_width_put), SOC_ENUM_EXT("PRIM_MI2S_RX Channels", prim_mi2s_rx_chs, msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), SOC_ENUM_EXT("SEC_MI2S_RX Channels", sec_mi2s_rx_chs, @@ -4228,6 +4691,42 @@ static const struct snd_kcontrol_new msm_common_snd_controls[] = { msm_vi_feed_tx_ch_get, msm_vi_feed_tx_ch_put), SOC_SINGLE_MULTI_EXT("TDM Slot Map", SND_SOC_NOPM, 0, 255, 0, TDM_MAX_SLOTS + MAX_PATH, NULL, tdm_slot_map_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_TX SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_RX SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_TX SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_RX SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_TX SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_RX SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUIN_TDM_TX SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUIN_TDM_RX SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("SEN_TDM_TX SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("SEN_TDM_RX SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), }; static const struct snd_kcontrol_new msm_snd_controls[] = { @@ -4806,55 +5305,113 @@ static int lahaina_tdm_snd_hw_params(struct snd_pcm_substream *substream, int channels, slots; unsigned int slot_mask, rate, clk_freq; unsigned int *slot_offset; - struct tdm_dev_config *config; - unsigned int path_dir = 0, interface = 0, channel_interface = 0; - struct msm_asoc_mach_data *pdata = NULL; + int offset_channels = 0; + int i; pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id); - pdata = snd_soc_card_get_drvdata(rtd->card); - slots = pdata->tdm_max_slots; - if (cpu_dai->id < AFE_PORT_ID_TDM_PORT_RANGE_START) { + /* currently only supporting TDM_RX_0 and TDM_TX_0 */ + switch (cpu_dai->id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + channels = tdm_rx_cfg[TDM_PRI][TDM_0].channels; + slots = tdm_slot[TDM_PRI].num; + slot_width = tdm_slot[TDM_PRI].width; + slot_offset = tdm_rx_slot_offset[TDM_PRI]; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX: + channels = tdm_rx_cfg[TDM_SEC][TDM_0].channels; + slots = tdm_slot[TDM_SEC].num; + slot_width = tdm_slot[TDM_SEC].width; + slot_offset = tdm_rx_slot_offset[TDM_SEC]; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX: + channels = tdm_rx_cfg[TDM_TERT][TDM_0].channels; + slots = tdm_slot[TDM_TERT].num; + slot_width = tdm_slot[TDM_TERT].width; + slot_offset = tdm_rx_slot_offset[TDM_TERT]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX: + channels = tdm_rx_cfg[TDM_QUAT][TDM_0].channels; + slots = tdm_slot[TDM_QUAT].num; + slot_width = tdm_slot[TDM_QUAT].width; + slot_offset = tdm_rx_slot_offset[TDM_QUAT]; + break; + case AFE_PORT_ID_QUINARY_TDM_RX: + channels = tdm_rx_cfg[TDM_QUIN][TDM_0].channels; + slots = tdm_slot[TDM_QUIN].num; + slot_width = tdm_slot[TDM_QUIN].width; + slot_offset = tdm_rx_slot_offset[TDM_QUIN]; + break; + case AFE_PORT_ID_SENARY_TDM_RX: + channels = tdm_rx_cfg[TDM_SEN][TDM_0].channels; + slots = tdm_slot[TDM_SEN].num; + slot_width = tdm_slot[TDM_SEN].width; + slot_offset = tdm_rx_slot_offset[TDM_SEN]; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX: + channels = tdm_tx_cfg[TDM_PRI][TDM_0].channels; + slots = tdm_slot[TDM_PRI].num; + slot_width = tdm_slot[TDM_PRI].width; + slot_offset = tdm_tx_slot_offset[TDM_PRI]; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX: + channels = tdm_tx_cfg[TDM_SEC][TDM_0].channels; + slots = tdm_slot[TDM_SEC].num; + slot_width = tdm_slot[TDM_SEC].width; + slot_offset = tdm_tx_slot_offset[TDM_SEC]; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX: + channels = tdm_tx_cfg[TDM_TERT][TDM_0].channels; + slots = tdm_slot[TDM_TERT].num; + slot_width = tdm_slot[TDM_TERT].width; + slot_offset = tdm_tx_slot_offset[TDM_TERT]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX: + channels = tdm_tx_cfg[TDM_QUAT][TDM_0].channels; + slots = tdm_slot[TDM_QUAT].num; + slot_width = tdm_slot[TDM_QUAT].width; + slot_offset = tdm_tx_slot_offset[TDM_QUAT]; + break; + case AFE_PORT_ID_QUINARY_TDM_TX: + channels = tdm_tx_cfg[TDM_QUIN][TDM_0].channels; + slots = tdm_slot[TDM_QUIN].num; + slot_width = tdm_slot[TDM_QUIN].width; + slot_offset = tdm_tx_slot_offset[TDM_QUIN]; + break; + case AFE_PORT_ID_SENARY_TDM_TX: + channels = tdm_tx_cfg[TDM_SEN][TDM_0].channels; + slots = tdm_slot[TDM_SEN].num; + slot_width = tdm_slot[TDM_SEN].width; + slot_offset = tdm_tx_slot_offset[TDM_SEN]; + break; + default: pr_err("%s: dai id 0x%x not supported\n", __func__, cpu_dai->id); return -EINVAL; } - /* RX or TX */ - path_dir = cpu_dai->id % MAX_PATH; - - /* PRI, SEC, TERT, QUAT, QUIN, ... */ - interface = (cpu_dai->id - AFE_PORT_ID_TDM_PORT_RANGE_START) - / (MAX_PATH * TDM_PORT_MAX); - - /* 0, 1, 2, .. 7 */ - channel_interface = - ((cpu_dai->id - AFE_PORT_ID_TDM_PORT_RANGE_START) / MAX_PATH) - % TDM_PORT_MAX; - - pr_debug("%s: path dir: %u, interface %u, channel interface %u\n", - __func__, path_dir, interface, channel_interface); - - config = ((struct tdm_dev_config *) tdm_cfg[interface]) + - (path_dir * TDM_PORT_MAX) + channel_interface; - if (!config) { - pr_err("%s: tdm config is NULL\n", __func__); - return -EINVAL; + for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) { + if (slot_offset[i] != AFE_SLOT_MAPPING_OFFSET_INVALID) + offset_channels++; + else + break; } - slot_offset = config->tdm_slot_offset; - if (!slot_offset) { - pr_err("%s: slot offset is NULL\n", __func__); + + if (offset_channels == 0) { + pr_err("%s: invalid offset_channels %d\n", + __func__, offset_channels); return -EINVAL; } - if (path_dir) - channels = tdm_tx_cfg[interface][channel_interface].channels; - else - channels = tdm_rx_cfg[interface][channel_interface].channels; + if (channels > offset_channels) { + pr_err("%s: channels %d exceed offset_channels %d\n", + __func__, channels, offset_channels); + return -EINVAL; + } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { /*2 slot config - bits 0 and 1 set for the first two slots */ - slot_mask = 0x0000FFFF >> (16 - slots); + slot_mask = 0xFFFFFFFF >> (32-channels); pr_debug("%s: tdm rx slot_width %d slots %d slot_mask %x\n", __func__, slot_width, slots, slot_mask); @@ -4878,7 +5435,7 @@ static int lahaina_tdm_snd_hw_params(struct snd_pcm_substream *substream, } } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { /*2 slot config - bits 0 and 1 set for the first two slots */ - slot_mask = 0x0000FFFF >> (16 - slots); + slot_mask = 0xFFFFFFFF >> (32-channels); pr_debug("%s: tdm tx slot_width %d slots %d slot_mask %x\n", __func__, slot_width, slots, slot_mask); @@ -4909,6 +5466,12 @@ static int lahaina_tdm_snd_hw_params(struct snd_pcm_substream *substream, rate = params_rate(params); clk_freq = rate * slot_width * slots; + if (clk_freq > 24576000) { + ret = -EINVAL; + pr_err("%s: clk frequency > 24.576MHz %d\n", + __func__, clk_freq); + goto end; + } ret = snd_soc_dai_set_sysclk(cpu_dai, 0, clk_freq, SND_SOC_CLOCK_OUT); if (ret < 0) pr_err("%s: failed to set tdm clk, err:%d\n", @@ -6540,13 +7103,17 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .ignore_suspend = 1, SND_SOC_DAILINK_REG(usb_audio_tx), }, +}; + + +static struct snd_soc_dai_link msm_tdm_be_dai_links[] = { { .name = LPASS_BE_PRI_TDM_RX_0, .stream_name = "Primary TDM0 Playback", .no_pcm = 1, .dpcm_playback = 1, .id = MSM_BACKEND_DAI_PRI_TDM_RX_0, - .be_hw_params_fixup = msm_be_hw_params_fixup, + .be_hw_params_fixup = lahaina_tdm_be_hw_params_fixup, .ops = &lahaina_tdm_be_ops, .ignore_suspend = 1, .ignore_pmdown_time = 1, @@ -6558,7 +7125,7 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .no_pcm = 1, .dpcm_capture = 1, .id = MSM_BACKEND_DAI_PRI_TDM_TX_0, - .be_hw_params_fixup = msm_be_hw_params_fixup, + .be_hw_params_fixup = lahaina_tdm_be_hw_params_fixup, .ops = &lahaina_tdm_be_ops, .ignore_suspend = 1, SND_SOC_DAILINK_REG(pri_tdm_tx_0), @@ -6569,7 +7136,7 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .no_pcm = 1, .dpcm_playback = 1, .id = MSM_BACKEND_DAI_SEC_TDM_RX_0, - .be_hw_params_fixup = msm_be_hw_params_fixup, + .be_hw_params_fixup = lahaina_tdm_be_hw_params_fixup, .ops = &lahaina_tdm_be_ops, .ignore_suspend = 1, .ignore_pmdown_time = 1, @@ -6581,7 +7148,7 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .no_pcm = 1, .dpcm_capture = 1, .id = MSM_BACKEND_DAI_SEC_TDM_TX_0, - .be_hw_params_fixup = msm_be_hw_params_fixup, + .be_hw_params_fixup = lahaina_tdm_be_hw_params_fixup, .ops = &lahaina_tdm_be_ops, .ignore_suspend = 1, SND_SOC_DAILINK_REG(sec_tdm_tx_0), @@ -6592,7 +7159,7 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .no_pcm = 1, .dpcm_playback = 1, .id = MSM_BACKEND_DAI_TERT_TDM_RX_0, - .be_hw_params_fixup = msm_be_hw_params_fixup, + .be_hw_params_fixup = lahaina_tdm_be_hw_params_fixup, .ops = &lahaina_tdm_be_ops, .ignore_suspend = 1, .ignore_pmdown_time = 1, @@ -6604,7 +7171,7 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .no_pcm = 1, .dpcm_capture = 1, .id = MSM_BACKEND_DAI_TERT_TDM_TX_0, - .be_hw_params_fixup = msm_be_hw_params_fixup, + .be_hw_params_fixup = lahaina_tdm_be_hw_params_fixup, .ops = &lahaina_tdm_be_ops, .ignore_suspend = 1, SND_SOC_DAILINK_REG(tert_tdm_tx_0), @@ -6615,7 +7182,7 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .no_pcm = 1, .dpcm_playback = 1, .id = MSM_BACKEND_DAI_QUAT_TDM_RX_0, - .be_hw_params_fixup = msm_be_hw_params_fixup, + .be_hw_params_fixup = lahaina_tdm_be_hw_params_fixup, .ops = &lahaina_tdm_be_ops, .ignore_suspend = 1, .ignore_pmdown_time = 1, @@ -6627,7 +7194,7 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .no_pcm = 1, .dpcm_capture = 1, .id = MSM_BACKEND_DAI_QUAT_TDM_TX_0, - .be_hw_params_fixup = msm_be_hw_params_fixup, + .be_hw_params_fixup = lahaina_tdm_be_hw_params_fixup, .ops = &lahaina_tdm_be_ops, .ignore_suspend = 1, SND_SOC_DAILINK_REG(quat_tdm_tx_0), @@ -6638,7 +7205,7 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .no_pcm = 1, .dpcm_playback = 1, .id = MSM_BACKEND_DAI_QUIN_TDM_RX_0, - .be_hw_params_fixup = msm_be_hw_params_fixup, + .be_hw_params_fixup = lahaina_tdm_be_hw_params_fixup, .ops = &lahaina_tdm_be_ops, .ignore_suspend = 1, .ignore_pmdown_time = 1, @@ -6650,7 +7217,7 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .no_pcm = 1, .dpcm_capture = 1, .id = MSM_BACKEND_DAI_QUIN_TDM_TX_0, - .be_hw_params_fixup = msm_be_hw_params_fixup, + .be_hw_params_fixup = lahaina_tdm_be_hw_params_fixup, .ops = &lahaina_tdm_be_ops, .ignore_suspend = 1, SND_SOC_DAILINK_REG(quin_tdm_tx_0), @@ -6661,7 +7228,7 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .no_pcm = 1, .dpcm_playback = 1, .id = MSM_BACKEND_DAI_SEN_TDM_RX_0, - .be_hw_params_fixup = msm_be_hw_params_fixup, + .be_hw_params_fixup = lahaina_tdm_be_hw_params_fixup, .ops = &lahaina_tdm_be_ops, .ignore_suspend = 1, .ignore_pmdown_time = 1, @@ -6673,7 +7240,7 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .no_pcm = 1, .dpcm_capture = 1, .id = MSM_BACKEND_DAI_SEN_TDM_TX_0, - .be_hw_params_fixup = msm_be_hw_params_fixup, + .be_hw_params_fixup = lahaina_tdm_be_hw_params_fixup, .ops = &lahaina_tdm_be_ops, .ignore_suspend = 1, SND_SOC_DAILINK_REG(sen_tdm_tx_0), @@ -7286,6 +7853,7 @@ static struct snd_soc_dai_link msm_lahaina_dai_links[ ARRAY_SIZE(msm_bolero_fe_dai_links) + ARRAY_SIZE(msm_common_misc_fe_dai_links) + ARRAY_SIZE(msm_common_be_dai_links) + + ARRAY_SIZE(msm_tdm_be_dai_links) + ARRAY_SIZE(msm_mi2s_be_dai_links) + #ifndef CONFIG_AUXPCM_DISABLE ARRAY_SIZE(msm_auxpcm_be_dai_links) + @@ -7656,6 +8224,11 @@ static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) sizeof(msm_common_be_dai_links)); total_links += ARRAY_SIZE(msm_common_be_dai_links); + memcpy(msm_lahaina_dai_links + total_links, + msm_tdm_be_dai_links, + sizeof(msm_tdm_be_dai_links)); + total_links += ARRAY_SIZE(msm_tdm_be_dai_links); + memcpy(msm_lahaina_dai_links + total_links, msm_rx_tx_cdc_dma_be_dai_links, sizeof(msm_rx_tx_cdc_dma_be_dai_links)); From a1937b42425cd22bd4b2a607f90f08930f9f3070 Mon Sep 17 00:00:00 2001 From: Hari Krishna Chowdary Yennana Date: Mon, 14 Aug 2023 15:12:43 +0530 Subject: [PATCH 12/12] dsp: add support to get Source Track data from FNN module - Add support for both audio record and voip call usecases to get STT params from dsp. Change-Id: Ifde484be0109ca25fb7cb9572abc506c1a70e4a4 --- dsp/q6adm.c | 95 +++++++++++++++++++++ dsp/q6voice.c | 167 +++++++++++++++++++++++++++++++++++++ include/dsp/apr_audio-v2.h | 36 +++++++- include/dsp/q6adm-v2.h | 3 + include/dsp/q6voice.h | 15 ++++ 5 files changed, 315 insertions(+), 1 deletion(-) diff --git a/dsp/q6adm.c b/dsp/q6adm.c index 712ba088baeb..32a5b29d3a1c 100644 --- a/dsp/q6adm.c +++ b/dsp/q6adm.c @@ -5983,6 +5983,101 @@ done: } EXPORT_SYMBOL(adm_get_source_tracking); +/** + * adm_get_fnn_source_tracking - + * Retrieve sound track info + * + * @port_id: Port ID number + * @copp_idx: copp index assigned + * @FnnSourceTrackingData: pointer for source track data to be updated with + * + * Returns 0 on success or error on failure + */ +int adm_get_fnn_source_tracking(int port_id, int copp_idx, + struct fluence_nn_source_tracking_param *FnnSourceTrackingData) +{ + int ret = 0, i; + char *params_value; + uint32_t max_param_size = 0; + struct adm_param_fluence_nn_source_tracking_t *fnn_sourcetrack_params = NULL; + struct param_hdr_v3 param_hdr; + + pr_debug("%s: Enter, port_id %d, copp_idx %d\n", + __func__, port_id, copp_idx); + + max_param_size = sizeof(struct adm_param_fluence_nn_source_tracking_t) + + sizeof(union param_hdrs); + params_value = kzalloc(max_param_size, GFP_KERNEL); + if (!params_value) + return -ENOMEM; + + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = MODULE_ID_FLUENCE_NN; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AUDPROC_PARAM_ID_FLUENCE_NN_SOURCE_TRACKING; + param_hdr.param_size = max_param_size; + ret = adm_get_pp_params(port_id, copp_idx, + ADM_CLIENT_ID_SOURCE_TRACKING, NULL, ¶m_hdr, + params_value); + if (ret) { + pr_err("%s: get parameters failed ret:%d\n", __func__, ret); + ret = -EINVAL; + goto done; + } + + if (this_adm.sourceTrackingData.apr_cmd_status != 0) { + pr_err("%s - get params returned error [%s]\n", + __func__, adsp_err_get_err_str( + this_adm.sourceTrackingData.apr_cmd_status)); + ret = adsp_err_get_lnx_err_code( + this_adm.sourceTrackingData.apr_cmd_status); + goto done; + } + + fnn_sourcetrack_params = (struct adm_param_fluence_nn_source_tracking_t *) params_value; + if ((!FnnSourceTrackingData) || (!fnn_sourcetrack_params)) { + pr_err("%s: Caught NULL pointer \n", __func__); + ret = -EINVAL; + goto done; + } + + FnnSourceTrackingData->speech_probablity_q20 = + fnn_sourcetrack_params->speech_probablity_q20; + pr_debug("%s: speech_probablity_q20 = %d\n", + __func__, FnnSourceTrackingData->speech_probablity_q20); + + for (i = 0; i < MAX_TOP_SPEAKERS; i++) { + FnnSourceTrackingData->speakers[i] = + fnn_sourcetrack_params->speakers[i]; + pr_debug("%s: speakers[%d] = %d\n", + __func__, i, FnnSourceTrackingData->speakers[i]); + } + + for (i = 0; i < MAX_POLAR_ACTIVITY_INDICATORS; i++) { + FnnSourceTrackingData->polarActivity[i] = + fnn_sourcetrack_params->polarActivity[i]; + pr_debug("%s: polarActivity[%d] = %d\n", + __func__, i, FnnSourceTrackingData->polarActivity[i]); + } + + FnnSourceTrackingData->session_time_lsw = + fnn_sourcetrack_params->session_time_lsw; + pr_debug("%s: session_time_lsw = %x\n", + __func__, FnnSourceTrackingData->session_time_lsw); + + FnnSourceTrackingData->session_time_msw = + fnn_sourcetrack_params->session_time_msw; + pr_debug("%s: session_time_msw = %x\n", + __func__, FnnSourceTrackingData->session_time_msw); + +done: + pr_debug("%s: Exit, ret = %d\n", __func__, ret); + + kfree(params_value); + return ret; +} +EXPORT_SYMBOL(adm_get_fnn_source_tracking); + /** * adm_get_doa_tracking_mon - * Retrieve doa tracking monitor info diff --git a/dsp/q6voice.c b/dsp/q6voice.c index f0f2a05b636d..f5115d225655 100644 --- a/dsp/q6voice.c +++ b/dsp/q6voice.c @@ -147,6 +147,8 @@ static int voice_send_get_sound_focus_cmd(struct voice_data *v, struct sound_focus_param *soundFocusData); static int voice_send_get_source_tracking_cmd(struct voice_data *v, struct source_tracking_param *sourceTrackingData); +static int voice_send_get_fnn_source_track_cmd(struct voice_data *v, + struct fluence_nn_source_tracking_param *FnnSourceTrackingData); static int voice_pack_and_set_cvp_param(struct voice_data *v, struct param_hdr_v3 param_hdr, u8 *param_data); @@ -8431,6 +8433,19 @@ static int32_t qdsp_cvp_callback(struct apr_client_data *data, void *priv) v->async_err = ptr[1]; wake_up(&v->cvp_wait); break; + case VSS_IFNN_ST_CMD_GET_ACTIVITY: + if (!ptr[1]) { + /* Read data from shared memory */ + common.is_source_tracking_resp_success = true; + } else { + common.is_source_tracking_resp_success = false; + pr_err("%s: Error received for source tracking params\n", + __func__); + } + v->cvp_state = CMD_STATUS_SUCCESS; + v->async_err = ptr[1]; + wake_up(&v->cvp_wait); + break; default: pr_debug("%s: not match cmd = 0x%x\n", __func__, ptr[0]); @@ -10028,6 +10043,158 @@ int voc_get_source_tracking(struct source_tracking_param *sourceTrackingData) } EXPORT_SYMBOL(voc_get_source_tracking); +static int voice_send_get_fnn_source_track_cmd(struct voice_data *v, + struct fluence_nn_source_tracking_param *FnnSourceTrackingData) +{ + struct apr_hdr cvp_get_fnn_st_param_cmd; + int ret = 0; + void *apr_cvp; + u16 cvp_handle; + int i; + + if (!v) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + + apr_cvp = common.apr_q6_cvp; + if (!apr_cvp) { + pr_err("%s: apr_cvp is NULL.\n", __func__); + return -EINVAL; + } + + if (!FnnSourceTrackingData) { + pr_err("%s: Caught NULL pointer \n", __func__); + ret = -EINVAL; + goto done; + } + + cvp_handle = voice_get_cvp_handle(v); + + /* send APR command to retrieve Sound Track Params from fluence NN Module */ + cvp_get_fnn_st_param_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvp_get_fnn_st_param_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_get_fnn_st_param_cmd) - APR_HDR_SIZE); + cvp_get_fnn_st_param_cmd.src_port = voice_get_idx_for_session(v->session_id); + cvp_get_fnn_st_param_cmd.dest_port = cvp_handle; + cvp_get_fnn_st_param_cmd.token = 0; + cvp_get_fnn_st_param_cmd.opcode = VSS_IFNN_ST_CMD_GET_ACTIVITY; + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvp, + (uint32_t *) &cvp_get_fnn_st_param_cmd); + if (ret < 0) { + pr_err("%s: Error in sending APR command\n", __func__); + + ret = -EINVAL; + goto done; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + ret = -ETIME; + goto done; + } + + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + + if (!common.is_fnn_source_tracking_resp_success) { + pr_err("%s: Error response received from CVD\n", __func__); + + ret = -EINVAL; + goto done; + } + + FnnSourceTrackingData->speech_probablity_q20 = + common.FnnSourceTrackingResponse.speech_probablity_q20; + pr_debug("%s: speech_probablity = %d\n", + __func__, FnnSourceTrackingData->speech_probablity_q20); + + for (i = 0; i < MAX_TOP_SPEAKERS; i++) { + FnnSourceTrackingData->speakers[i] = + common.FnnSourceTrackingResponse.speakers[i]; + pr_debug("%s: speakers[%d] = %d\n", + __func__, i, FnnSourceTrackingData->speakers[i]); + } + + for (i = 0; i < MAX_POLAR_ACTIVITY_INDICATORS; i++) { + FnnSourceTrackingData->polarActivity[i] = + common.FnnSourceTrackingResponse.polarActivity[i]; + pr_debug("%s: polarActivity[%d] = %d\n", + __func__, i, FnnSourceTrackingData->polarActivity[i]); + } + + FnnSourceTrackingData->session_time_lsw = + common.FnnSourceTrackingResponse.session_time_lsw; + pr_debug("%s: session_time_lsw = %d\n", + __func__, FnnSourceTrackingData->session_time_lsw); + + FnnSourceTrackingData->session_time_msw = + common.FnnSourceTrackingResponse.session_time_msw; + pr_debug("%s: session_time_msw = %d\n", + __func__, FnnSourceTrackingData->session_time_msw); + + common.is_fnn_source_tracking_resp_success = false; + +done: + pr_debug("%s: Exit, ret=%d\n", __func__, ret); + + return ret; +} + +/** + * voc_get_fnn_source_tracking - retrieves source track data. + * + * @sourceTrackingData: pointer to be updated with source track data. + * + * Returns 0 on success or error on failure + */ +int voc_get_fnn_source_tracking(struct fluence_nn_source_tracking_param *FnnSourceTrackingData) +{ + struct voice_data *v = NULL; + int ret = -EINVAL; + struct voice_session_itr itr; + + mutex_lock(&common.common_lock); + + voice_itr_init(&itr, ALL_SESSION_VSID); + while (voice_itr_get_next_session(&itr, &v)) { + if (v == NULL) { + pr_err("%s: invalid session\n", __func__); + + break; + } + + mutex_lock(&v->lock); + if (is_voc_state_active(v->voc_state) && + (v->lch_mode != VOICE_LCH_START) && + !v->disable_topology) + ret = voice_send_get_fnn_source_track_cmd(v, + FnnSourceTrackingData); + mutex_unlock(&v->lock); + + } + + mutex_unlock(&common.common_lock); + pr_debug("%s: Exit, ret=%d\n", __func__, ret); + + return ret; +} +EXPORT_SYMBOL(voc_get_fnn_source_tracking); + static int voice_set_cvp_param(struct voice_data *v, struct vss_icommon_mem_mapping_hdr *mem_hdr, u32 *param_data, u32 param_size) diff --git a/include/dsp/apr_audio-v2.h b/include/dsp/apr_audio-v2.h index ed5963474da6..ee0bfd5f2f23 100644 --- a/include/dsp/apr_audio-v2.h +++ b/include/dsp/apr_audio-v2.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved. - * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved. */ @@ -13519,10 +13519,29 @@ struct adm_set_compressed_device_latency { #define VOICEPROC_MODULE_ID_FLUENCE_PRO_VC_TX 0x00010F35 #define VOICEPROC_PARAM_ID_FLUENCE_SOUNDFOCUS 0x00010E37 #define VOICEPROC_PARAM_ID_FLUENCE_SOURCETRACKING 0x00010E38 +#define AUDPROC_PARAM_ID_FLUENCE_NN_SOURCE_TRACKING 0x00010B83 +#define MODULE_ID_FLUENCE_NN 0x00010B0F #define MAX_SECTORS 8 #define MAX_NOISE_SOURCE_INDICATORS 3 #define MAX_POLAR_ACTIVITY_INDICATORS 360 #define MAX_DOA_TRACKING_ANGLES 2 +#define MAX_TOP_SPEAKERS 5 +#define MAX_FOCUS_DIRECTION 2 + +struct fluence_nn_sound_focus_param { + int16_t mode; + int16_t focus_direction[MAX_FOCUS_DIRECTION]; + int16_t focus_width; +} __packed; + +struct fluence_nn_source_tracking_param { + int32_t speech_probablity_q20; + int16_t speakers[MAX_TOP_SPEAKERS]; + int16_t reserved; + uint8_t polarActivity[MAX_POLAR_ACTIVITY_INDICATORS]; + uint32_t session_time_lsw; + uint32_t session_time_msw; +} __packed; struct sound_focus_param { uint16_t start_angle[MAX_SECTORS]; @@ -13543,6 +13562,21 @@ struct doa_tracking_mon_param { uint8_t polar_activity[MAX_POLAR_ACTIVITY_INDICATORS]; } __packed; +struct adm_param_fluence_nn_sound_focus_t { + int16_t mode; + int16_t focus_direction[2]; + int16_t focus_width; +} __packed; + +struct adm_param_fluence_nn_source_tracking_t { + int32_t speech_probablity_q20; + int16_t speakers[5]; + int16_t reserved; + uint8_t polarActivity[360]; + uint32_t session_time_lsw; + uint32_t session_time_msw; +} __packed; + struct adm_param_fluence_soundfocus_t { uint16_t start_angles[MAX_SECTORS]; uint8_t enables[MAX_SECTORS]; diff --git a/include/dsp/q6adm-v2.h b/include/dsp/q6adm-v2.h index a010b56cb5ac..d96318ee1ccc 100644 --- a/include/dsp/q6adm-v2.h +++ b/include/dsp/q6adm-v2.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef __Q6_ADM_V2_H__ #define __Q6_ADM_V2_H__ @@ -229,6 +230,8 @@ int adm_get_sound_focus(int port_id, int copp_idx, struct sound_focus_param *soundFocusData); int adm_get_source_tracking(int port_id, int copp_idx, struct source_tracking_param *sourceTrackingData); +int adm_get_fnn_source_tracking(int port_id, int copp_idx, + struct fluence_nn_source_tracking_param *FnnSourceTrackingData); int adm_get_doa_tracking_mon(int port_id, int copp_idx, struct doa_tracking_mon_param *doa_tracking_data); int adm_set_custom_chmix_cfg(int port_id, int copp_idx, diff --git a/include/dsp/q6voice.h b/include/dsp/q6voice.h index 9ada57a5ad3e..0994cc0069bb 100644 --- a/include/dsp/q6voice.h +++ b/include/dsp/q6voice.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef __QDSP6VOICE_H__ #define __QDSP6VOICE_H__ @@ -1839,6 +1840,7 @@ struct share_memory_info { #define VSS_ISOUNDFOCUS_CMD_GET_SECTORS 0x00013134 #define VSS_ISOUNDFOCUS_RSP_GET_SECTORS 0x00013135 #define VSS_ISOURCETRACK_CMD_GET_ACTIVITY 0x00013136 +#define VSS_IFNN_ST_CMD_GET_ACTIVITY 0x00013137 struct vss_isoundfocus_cmd_set_sectors_t { uint16_t start_angles[8]; @@ -1880,6 +1882,16 @@ struct vss_isourcetrack_activity_data_t { uint8_t sound_strength[360]; } __packed; +/* Payload structure for the VSS_IFNN_ST_CMD_GET_ACTIVITY command */ +struct vss_ifnn_st_cmd_get_activity_t { + int32_t speech_probablity_q20; + int16_t speakers[MAX_TOP_SPEAKERS]; + int16_t reserved; + uint8_t polarActivity[MAX_POLAR_ACTIVITY_INDICATORS]; + uint32_t session_time_lsw; + uint32_t session_time_msw; +} __packed; + struct shared_mem_info { uint32_t mem_handle; struct mem_map_table sh_mem_block; @@ -1995,9 +2007,11 @@ struct common_data { bool is_per_vocoder_cal_enabled; bool is_sound_focus_resp_success; bool is_source_tracking_resp_success; + bool is_fnn_source_tracking_resp_success; struct vss_isoundfocus_rsp_get_sectors_t soundFocusResponse; struct shared_mem_info source_tracking_sh_mem; struct vss_isourcetrack_activity_data_t sourceTrackingResponse; + struct vss_ifnn_st_cmd_get_activity_t FnnSourceTrackingResponse; bool sidetone_enable; bool mic_break_enable; struct audio_uevent_data *uevent_data; @@ -2144,6 +2158,7 @@ int voice_set_topology_specific_info(struct voice_data *v, int voc_set_sound_focus(struct sound_focus_param sound_focus_param); int voc_get_sound_focus(struct sound_focus_param *soundFocusData); int voc_get_source_tracking(struct source_tracking_param *sourceTrackingData); +int voc_get_fnn_source_tracking(struct fluence_nn_source_tracking_param *FnnSourceTrackingData); int voc_set_afe_sidetone(uint32_t session_id, bool sidetone_enable); bool voc_get_afe_sidetone(void); #endif