From 6ea98f1cae39453ad76c4cd4dbb332cd2128ece2 Mon Sep 17 00:00:00 2001 From: Surendar Karka Date: Fri, 24 Jan 2020 14:35:07 +0530 Subject: [PATCH 1/3] dsp: Add support to set clock drift - Add support to adjust drift in audio interface clock source PLL. - ADSP adjusts the source clock based on clk id. Add support in AFE driver to get clk id based on the AFE port. Change-Id: I45ceb6579bb15e32541051e0ba936a2d903e4923 Signed-off-by: Surendar Karka --- dsp/q6afe.c | 191 +++++++++++++++++++++++++++++++++++++ include/dsp/apr_audio-v2.h | 29 ++++++ include/dsp/q6afe-v2.h | 3 + 3 files changed, 223 insertions(+) diff --git a/dsp/q6afe.c b/dsp/q6afe.c index 1eced32258af..ec212acb78a7 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -184,6 +184,43 @@ struct afe_ctl { uint32_t v_vali_flag; }; +struct afe_clkinfo_per_port { + u16 port_id; /* AFE port ID */ + uint32_t clk_id; /* Clock ID */ +}; + +struct afe_clkinfo_per_port clkinfo_per_port[] = { + { AFE_PORT_ID_PRIMARY_MI2S_RX, Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT}, + { AFE_PORT_ID_SECONDARY_MI2S_RX, Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT}, + { AFE_PORT_ID_TERTIARY_MI2S_RX, Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT}, + { AFE_PORT_ID_QUATERNARY_MI2S_RX, Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT}, + { AFE_PORT_ID_QUINARY_MI2S_RX, Q6AFE_LPASS_CLK_ID_QUI_MI2S_IBIT}, + { AFE_PORT_ID_SENARY_MI2S_RX, Q6AFE_LPASS_CLK_ID_SEN_MI2S_IBIT}, + { AFE_PORT_ID_PRIMARY_PCM_RX, Q6AFE_LPASS_CLK_ID_PRI_PCM_IBIT}, + { AFE_PORT_ID_SECONDARY_PCM_RX, Q6AFE_LPASS_CLK_ID_SEC_PCM_IBIT}, + { AFE_PORT_ID_TERTIARY_PCM_RX, Q6AFE_LPASS_CLK_ID_TER_PCM_IBIT}, + { AFE_PORT_ID_QUATERNARY_PCM_RX, Q6AFE_LPASS_CLK_ID_QUAD_PCM_IBIT}, + { AFE_PORT_ID_QUINARY_PCM_RX, Q6AFE_LPASS_CLK_ID_QUIN_PCM_IBIT}, + { AFE_PORT_ID_SENARY_PCM_RX, Q6AFE_LPASS_CLK_ID_SEN_PCM_IBIT}, + { AFE_PORT_ID_PRIMARY_TDM_RX, Q6AFE_LPASS_CLK_ID_PRI_TDM_IBIT}, + { AFE_PORT_ID_SECONDARY_TDM_RX, Q6AFE_LPASS_CLK_ID_SEC_TDM_IBIT}, + { AFE_PORT_ID_TERTIARY_TDM_RX, Q6AFE_LPASS_CLK_ID_TER_TDM_IBIT}, + { AFE_PORT_ID_QUATERNARY_TDM_RX, Q6AFE_LPASS_CLK_ID_QUAD_TDM_IBIT}, + { AFE_PORT_ID_QUINARY_TDM_RX, Q6AFE_LPASS_CLK_ID_QUIN_TDM_IBIT}, + { AFE_PORT_ID_PRIMARY_SPDIF_RX, + AFE_CLOCK_SET_CLOCK_ID_PRI_SPDIF_OUTPUT_CORE}, + { AFE_PORT_ID_PRIMARY_SPDIF_TX, + AFE_CLOCK_SET_CLOCK_ID_PRI_SPDIF_INPUT_CORE}, + { AFE_PORT_ID_SECONDARY_SPDIF_RX, + AFE_CLOCK_SET_CLOCK_ID_SEC_SPDIF_OUTPUT_CORE}, + { AFE_PORT_ID_SECONDARY_SPDIF_TX, + AFE_CLOCK_SET_CLOCK_ID_SEC_SPDIF_INPUT_CORE}, + { AFE_PORT_ID_PRIMARY_META_MI2S_RX, + Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT}, + { AFE_PORT_ID_SECONDARY_META_MI2S_RX, + Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT}, +}; + static atomic_t afe_ports_mad_type[SLIMBUS_PORT_LAST - SLIMBUS_0_RX]; static unsigned long afe_configured_cmd; @@ -8264,6 +8301,156 @@ int afe_set_lpass_clock(u16 port_id, struct afe_clk_cfg *cfg) } EXPORT_SYMBOL(afe_set_lpass_clock); +static int afe_get_port_idx(u16 port_id) +{ + u16 afe_port = 0; + int i = -EINVAL; + + pr_debug("%s: port id 0x%x\n", __func__, port_id); + + if ((port_id >= AFE_PORT_ID_TDM_PORT_RANGE_START) && + (port_id <= AFE_PORT_ID_TDM_PORT_RANGE_END)) + afe_port = port_id & 0xFFF0; + else if ((port_id == AFE_PORT_ID_PRIMARY_SPDIF_RX) || + (port_id == AFE_PORT_ID_PRIMARY_SPDIF_TX) || + (port_id == AFE_PORT_ID_SECONDARY_SPDIF_RX) || + (port_id == AFE_PORT_ID_SECONDARY_SPDIF_TX)) + afe_port = port_id; + else + afe_port = port_id & 0xFFFE; + + for (i = 0; i < ARRAY_SIZE(clkinfo_per_port); i++) { + if (afe_port == clkinfo_per_port[i].port_id) { + pr_debug("%s: idx 0x%x port id 0x%x\n", __func__, + i, afe_port); + return i; + } + } + + pr_debug("%s: cannot get idx for port id 0x%x\n", __func__, + afe_port); + + return -EINVAL; +} + +static int afe_get_clk_id(u16 port_id) +{ + u16 afe_port = 0; + uint32_t clk_id = -EINVAL; + int idx = 0; + + idx = afe_get_port_idx(port_id); + if (idx < 0) { + pr_err("%s: cannot get clock id for port id 0x%x\n", __func__, + afe_port); + return -EINVAL; + } + + clk_id = clkinfo_per_port[idx].clk_id; + pr_debug("%s: clk id 0x%x port id 0x%x\n", __func__, clk_id, + afe_port); + + return clk_id; +} + +/** + * afe_set_clk_id - Update clock id for AFE port + * + * @port_id: AFE port id + * @clk_id: CLock ID + * + * Returns 0 on success, appropriate error code otherwise + */ +int afe_set_clk_id(u16 port_id, uint32_t clk_id) +{ + u16 afe_port = 0; + int idx = 0; + + idx = afe_get_port_idx(port_id); + if (idx < 0) { + pr_debug("%s: cannot set clock id for port id 0x%x\n", __func__, + afe_port); + return -EINVAL; + } + + clkinfo_per_port[idx].clk_id = clk_id; + pr_debug("%s: updated clk id 0x%x port id 0x%x\n", __func__, + clkinfo_per_port[idx].clk_id, afe_port); + + return 0; +} +EXPORT_SYMBOL(afe_set_clk_id); + +/** + * afe_set_pll_clk_drift - Set audio interface PLL clock drift + * + * @port_id: AFE port id + * @set_clk_drift: clk drift to adjust PLL + * @clk_reset: reset Interface clock to original value + * + * Returns 0 on success, appropriate error code otherwise + */ +int afe_set_pll_clk_drift(u16 port_id, int32_t set_clk_drift, + uint32_t clk_reset) +{ + struct afe_set_clk_drift clk_drift; + struct param_hdr_v3 param_hdr; + uint32_t clk_id; + int index = 0, ret = 0; + + memset(¶m_hdr, 0, sizeof(param_hdr)); + memset(&clk_drift, 0, sizeof(clk_drift)); + + index = q6audio_get_port_index(port_id); + if (index < 0 || index >= AFE_MAX_PORTS) { + pr_err("%s: index[%d] invalid!\n", __func__, index); + return -EINVAL; + } + + ret = afe_q6_interface_prepare(); + if (ret != 0) { + pr_err_ratelimited("%s: Q6 interface prepare failed %d\n", + __func__, ret); + return ret; + } + + clk_id = afe_get_clk_id(port_id); + if (clk_id < 0) { + pr_err("%s: cannot get clk id for port id 0x%x\n", + __func__, port_id); + return -EINVAL; + } + + if (clk_id & 0x01) { + pr_err("%s: cannot adjust clock drift for external clock id 0x%x\n", + __func__, clk_id); + return -EINVAL; + } + + clk_drift.clk_drift = set_clk_drift; + clk_drift.clk_reset = clk_reset; + clk_drift.clk_id = clk_id; + pr_debug("%s: clk id = 0x%x clk drift = %d clk reset = %d port id 0x%x\n", + __func__, clk_drift.clk_id, clk_drift.clk_drift, + clk_drift.clk_reset, port_id); + + mutex_lock(&this_afe.afe_clk_lock); + param_hdr.module_id = AFE_MODULE_CLOCK_SET; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_CLOCK_ADJUST; + param_hdr.param_size = sizeof(struct afe_set_clk_drift); + + ret = q6afe_svc_pack_and_set_param_in_band(index, param_hdr, + (u8 *) &clk_drift); + if (ret < 0) + pr_err_ratelimited("%s: AFE PLL clk drift failed with ret %d\n", + __func__, ret); + + mutex_unlock(&this_afe.afe_clk_lock); + return ret; +} +EXPORT_SYMBOL(afe_set_pll_clk_drift); + /** * afe_set_lpass_clk_cfg - Set AFE clk config * @@ -8350,6 +8537,10 @@ int afe_set_lpass_clock_v2(u16 port_id, struct afe_clk_set *cfg) return -EINVAL; } + ret = afe_set_clk_id(port_id, cfg->clk_id); + if (ret < 0) + pr_debug("%s: afe_set_clk_id fail %d\n", __func__, ret); + ret = afe_set_lpass_clk_cfg(index, cfg); if (ret) pr_err("%s: afe_set_lpass_clk_cfg_v2 failed %d\n", diff --git a/include/dsp/apr_audio-v2.h b/include/dsp/apr_audio-v2.h index 45ff23ed2fed..82be8ebacdfc 100644 --- a/include/dsp/apr_audio-v2.h +++ b/include/dsp/apr_audio-v2.h @@ -12096,6 +12096,35 @@ struct afe_clk_cfg { #define AFE_MODULE_CLOCK_SET 0x0001028F #define AFE_PARAM_ID_CLOCK_SET 0x00010290 +struct afe_set_clk_drift { + /* + * Clock ID + * @values + * - 0x100 to 0x10E + * - 0x200 to 0x20C + * - 0x500 to 0x505 + */ + uint32_t clk_id; + + /* + * Clock drift (in PPB) to be set. + * @values + * - need to get values from DSP team + */ + int32_t clk_drift; + + /* + * Clock rest. + * @values + * - 1 -- Reset PLL with the original frequency + * - 0 -- Adjust the clock with the clk drift value + */ + uint32_t clk_reset; +} __packed; + +/* This param id is used to adjust audio interface PLL*/ +#define AFE_PARAM_ID_CLOCK_ADJUST 0x000102C6 + enum afe_lpass_digital_clk_src { Q6AFE_LPASS_DIGITAL_ROOT_INVALID, Q6AFE_LPASS_DIGITAL_ROOT_PRI_MI2S_OSR, diff --git a/include/dsp/q6afe-v2.h b/include/dsp/q6afe-v2.h index dd3d934d6f8e..dc8f91878883 100644 --- a/include/dsp/q6afe-v2.h +++ b/include/dsp/q6afe-v2.h @@ -487,6 +487,9 @@ void afe_register_wakeup_irq_callback( void (*afe_cb_wakeup_irq)(void *handle)); int afe_get_doa_tracking_mon(u16 port_id, struct doa_tracking_mon_param *doa_tracking_data); +int afe_set_pll_clk_drift(u16 port_id, int32_t set_clk_drift, + uint32_t clk_reset); +int afe_set_clk_id(u16 port_id, uint32_t clk_id); enum { AFE_LPASS_CORE_HW_BLOCK_ID_NONE, From a44c97046674f03f17cb60c17ee2d12aaf95732a Mon Sep 17 00:00:00 2001 From: Surendar Karka Date: Fri, 24 Jan 2020 14:41:46 +0530 Subject: [PATCH 2/3] asoc: add mixer control to set clock drift Add mixer controls support in routing driver to adjust the source PLL clock drift. Change-Id: I211330793f8650902130905f308dc5d4b73e09fa Signed-off-by: Surendar Karka --- asoc/msm-pcm-routing-v2.c | 47 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index 35b2a8bc3122..05443f09f38f 100644 --- a/asoc/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -30180,6 +30180,50 @@ static const struct snd_kcontrol_new aptx_dec_license_controls[] = { msm_aptx_dec_license_control_put), }; +static int msm_routing_get_pll_clk_drift(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return 0; +} + +static int msm_routing_put_pll_clk_drift(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 port_id = 0; + int32_t clk_drift = 0; + uint32_t clk_reset = 0; + int be_idx, ret = -EINVAL; + + be_idx = ucontrol->value.integer.value[0]; + clk_drift = ucontrol->value.integer.value[1]; + clk_reset = ucontrol->value.integer.value[2]; + + if (be_idx < 0 && be_idx >= MSM_BACKEND_DAI_MAX) { + pr_err("%s: Invalid be id %d\n", __func__, be_idx); + return -EINVAL; + } + + if (!msm_bedais[be_idx].active && !clk_reset) { + pr_err("%s:BE is not active %d, cannot set clock drift\n", + __func__, be_idx); + return -EINVAL; + } + + port_id = msm_bedais[be_idx].port_id; + pr_debug("%s: clk drift %d be idx %d clk reset %d port id 0x%x\n", + __func__, clk_drift, be_idx, clk_reset, port_id); + ret = afe_set_pll_clk_drift(port_id, clk_drift, clk_reset); + if (ret < 0) + pr_err("%s: failed to set pll clk drift\n", __func__); + + return ret; +} + +static const struct snd_kcontrol_new pll_clk_drift_controls[] = { + SOC_SINGLE_MULTI_EXT("PLL config data", SND_SOC_NOPM, 0, 0xFFFFFFFF, + 0, 128, msm_routing_get_pll_clk_drift, msm_routing_put_pll_clk_drift), +}; + static int msm_routing_put_port_chmap_mixer(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -30480,6 +30524,9 @@ static int msm_routing_probe(struct snd_soc_component *component) port_multi_channel_map_mixer_controls, ARRAY_SIZE(port_multi_channel_map_mixer_controls)); + snd_soc_add_component_controls(component, pll_clk_drift_controls, + ARRAY_SIZE(pll_clk_drift_controls)); + return 0; } From 98a6eda6316a8f2c5d92983717f148ea9efc8364 Mon Sep 17 00:00:00 2001 From: Surendar Karka Date: Fri, 24 Jan 2020 14:44:13 +0530 Subject: [PATCH 3/3] asoc: qcs405: add support to set clock drift AFE driver maintains the clk id for each port. AFE driver has no clue of the master port in META I2S port. Add support to set the clk id for META I2S port. Change-Id: Ic531e94df5503f5ff95364830c5177e9cd86af6c Signed-off-by: Surendar Karka --- asoc/qcs405.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/asoc/qcs405.c b/asoc/qcs405.c index 18dfb82207be..2c4c5fab0392 100644 --- a/asoc/qcs405.c +++ b/asoc/qcs405.c @@ -6279,6 +6279,7 @@ static int msm_meta_mi2s_snd_startup(struct snd_pcm_substream *substream) unsigned int fmt = SND_SOC_DAIFMT_CBS_CFS; struct snd_soc_card *card = rtd->card; struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + u16 port_id = 0; dev_dbg(rtd->card->dev, "%s: substream = %s stream = %d, dai name %s, dai ID %d\n", @@ -6312,6 +6313,13 @@ static int msm_meta_mi2s_snd_startup(struct snd_pcm_substream *substream) meta_mi2s_intf_conf[index].clk_enable[i] = true; if (i == 0) { + port_id = msm_get_port_id(rtd->dai_link->id); + ret = afe_set_clk_id(port_id, + mi2s_clk[member_port].clk_id); + if (ret < 0) + pr_err("%s: afe_set_clk_id fail %d\n", + __func__, ret); + ret = snd_soc_dai_set_fmt(cpu_dai, fmt); if (ret < 0) { pr_err("%s: set fmt cpu dai failed for META_MI2S (%d), err:%d\n",