diff --git a/asoc/msm-dai-q6-v2.c b/asoc/msm-dai-q6-v2.c index ceba2e0ac11c..d39ef1e49b3f 100755 --- a/asoc/msm-dai-q6-v2.c +++ b/asoc/msm-dai-q6-v2.c @@ -481,6 +481,8 @@ static bool afe_port_logging_item[IDX_TDM_MAX]; static int afe_port_loggging_control_added; +static int afe_dyn_mclk_control_added; + static DEFINE_MUTEX(tdm_mutex); static atomic_t tdm_group_ref[IDX_GROUP_TDM_MAX]; @@ -522,6 +524,51 @@ static struct afe_clk_set tdm_clk_set = { 0, }; +static int clk_id_index; +static int clk_root_index; +static int clk_attri_index; +static int global_dyn_mclk_cfg_portid; +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, + .clk_freq_in_hz = 12288000, + .clk_attri = Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + .clk_root = Q6AFE_LPASS_MCLK_IN0, + .divider_2x = AFE_CLOCK_DEFAULT_INTEGER_DIVIDER, + .m = AFE_CLOCK_DEFAULT_M_VALUE, + .n = AFE_CLOCK_DEFAULT_N_VALUE, + .d = AFE_CLOCK_DEFAULT_D_VALUE, + .enable = 0, +}; + +static int afe_dyn_clk_root_enum[] = { + Q6AFE_LPASS_MCLK_IN0, + Q6AFE_LPASS_MCLK_IN1, +}; + +static int afe_dyn_clk_attri_enum[] = { + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_DIVIDEND, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_DIVISOR, +}; + +static int afe_dyn_clk_id_enum[] = { + Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT, Q6AFE_LPASS_CLK_ID_PRI_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT, Q6AFE_LPASS_CLK_ID_SEC_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT, Q6AFE_LPASS_CLK_ID_TER_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT, Q6AFE_LPASS_CLK_ID_QUAD_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_IBIT, Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_EBIT, + Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_OSR, + Q6AFE_LPASS_CLK_ID_PRI_PCM_IBIT, Q6AFE_LPASS_CLK_ID_PRI_PCM_EBIT, + Q6AFE_LPASS_CLK_ID_SEC_PCM_IBIT, Q6AFE_LPASS_CLK_ID_SEC_PCM_EBIT, + Q6AFE_LPASS_CLK_ID_TER_PCM_IBIT, Q6AFE_LPASS_CLK_ID_TER_PCM_EBIT, + Q6AFE_LPASS_CLK_ID_QUAD_PCM_IBIT, Q6AFE_LPASS_CLK_ID_QUAD_PCM_EBIT, + Q6AFE_LPASS_CLK_ID_MCLK_1, Q6AFE_LPASS_CLK_ID_MCLK_2, + Q6AFE_LPASS_CLK_ID_MCLK_3, Q6AFE_LPASS_CLK_ID_MCLK_4, + Q6AFE_LPASS_CLK_ID_MCLK_5, Q6AFE_LPASS_CLK_ID_AHB_HDMI_INPUT, + Q6AFE_LPASS_CLK_ID_SPDIF_CORE +}; + static int msm_dai_q6_get_tdm_clk_ref(u16 id) { switch (id) { @@ -10371,14 +10418,168 @@ static int msm_pcm_add_afe_port_logging_control(struct snd_soc_dai *dai) return rc; } +static int get_global_clk_root(int value) +{ + if ((value < 0) || (value > ARRAY_SIZE(afe_dyn_clk_root_enum) - 1)) { + pr_err("%s, %d set clk_root range failed\n", __func__, __LINE__); + return global_dyn_mclk_cfg.clk_root; + } + clk_root_index = value; + return afe_dyn_clk_root_enum[clk_root_index]; +} + +static int get_global_clk_attri(int value) +{ + if ((value < 0) || (value > ARRAY_SIZE(afe_dyn_clk_attri_enum) - 1)) { + pr_err("%s, %d set clk_attri range failed\n", __func__, __LINE__); + return global_dyn_mclk_cfg.clk_attri; + } + clk_attri_index = value; + return afe_dyn_clk_attri_enum[clk_attri_index]; +} + +static int get_global_clk_id(int value) +{ + if ((value < 0) || (value > ARRAY_SIZE(afe_dyn_clk_id_enum) - 1)) { + pr_err("%s, %d set clk_id range failed\n", __func__, __LINE__); + return global_dyn_mclk_cfg.clk_id; + } + clk_id_index = value; + return afe_dyn_clk_id_enum[clk_id_index]; +} + +static int msm_pcm_afe_dyn_mclk_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *ucontrol) +{ + ucontrol->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + /* 11 int values:clk_set_minor_version, clk_id, clk_freq_in_hz, clk_attri, + * clk_root, enable, divider_2x, m, n, d + */ + ucontrol->count = 11; + ucontrol->value.integer.min = 0; + ucontrol->value.integer.max = INT_MAX; + /* Valid range is all positive values to support above controls */ + pr_debug("%s,%d\n", __func__, __LINE__); + + return 0; +} + +static int msm_pcm_afe_dyn_mclk_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = global_dyn_mclk_cfg_portid; + ucontrol->value.integer.value[1] = clk_id_index; + ucontrol->value.integer.value[2] = global_dyn_mclk_cfg.clk_freq_in_hz; + ucontrol->value.integer.value[3] = clk_attri_index; + ucontrol->value.integer.value[4] = clk_root_index; + ucontrol->value.integer.value[5] = global_dyn_mclk_cfg.enable; + ucontrol->value.integer.value[6] = global_dyn_mclk_cfg.divider_2x; + ucontrol->value.integer.value[7] = global_dyn_mclk_cfg.m; + ucontrol->value.integer.value[8] = global_dyn_mclk_cfg.n; + ucontrol->value.integer.value[9] = global_dyn_mclk_cfg.d; + ucontrol->value.integer.value[10] = global_dyn_mclk_cfg.clk_set_minor_version; + + pr_debug("%s:%d.... portid:%d, clk_id:%d, clk_freq_in_hz:%d, clk_attri:%d, clk_root:%d, enable:%d, divider_2x:%d, m:%d, n:%d, d:%d, version:%d\n", + __func__, __LINE__, global_dyn_mclk_cfg_portid, global_dyn_mclk_cfg.clk_id, + global_dyn_mclk_cfg.clk_freq_in_hz, global_dyn_mclk_cfg.clk_attri, + global_dyn_mclk_cfg.clk_root, global_dyn_mclk_cfg.enable, + global_dyn_mclk_cfg.divider_2x, global_dyn_mclk_cfg.m, global_dyn_mclk_cfg.n, + global_dyn_mclk_cfg.d, global_dyn_mclk_cfg.clk_set_minor_version); + + return 0; +} + +static int msm_pcm_afe_dyn_mclk_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + + pr_debug("%s: enter\n", __func__); + + global_dyn_mclk_cfg_portid = ucontrol->value.integer.value[0]; + + global_dyn_mclk_cfg.clk_id = get_global_clk_id(ucontrol->value.integer.value[1]); + + if (ucontrol->value.integer.value[2] >= 0) + global_dyn_mclk_cfg.clk_freq_in_hz = ucontrol->value.integer.value[2]; + + global_dyn_mclk_cfg.clk_attri = get_global_clk_attri(ucontrol->value.integer.value[3]); + + global_dyn_mclk_cfg.clk_root = get_global_clk_root(ucontrol->value.integer.value[4]); + + if ((ucontrol->value.integer.value[5] <= 1) && (ucontrol->value.integer.value[5] >= 0)) + global_dyn_mclk_cfg.enable = ucontrol->value.integer.value[5]; + + global_dyn_mclk_cfg.divider_2x = ucontrol->value.integer.value[6]; + global_dyn_mclk_cfg.m = ucontrol->value.integer.value[7]; + global_dyn_mclk_cfg.n = ucontrol->value.integer.value[8]; + global_dyn_mclk_cfg.d = ucontrol->value.integer.value[9]; + + pr_debug("%s:%d.... portid:%d, clk_id_index:%d, clk_freq_in_hz:%d, clk_attri_index:%d, clk_root_index:%d, enable:%d, divider_2x:%d, m:%d, n:%d, d:%d, version:%d\n", + __func__, __LINE__, ucontrol->value.integer.value[0], + ucontrol->value.integer.value[1], + ucontrol->value.integer.value[2], ucontrol->value.integer.value[3], + ucontrol->value.integer.value[4], ucontrol->value.integer.value[5], + ucontrol->value.integer.value[6], ucontrol->value.integer.value[7], + ucontrol->value.integer.value[8], ucontrol->value.integer.value[9]); + + ret = afe_set_lpass_clk_cfg_ext_mclk_v2(global_dyn_mclk_cfg_portid, + &global_dyn_mclk_cfg, 0); + if (ret) + pr_err("%s: AFE port logging setting for port 0x%x failed %d\n", + __func__, global_dyn_mclk_cfg_portid, ret); + + return ret; +} + +static int msm_pcm_add_afe_dyn_mclk_control(struct snd_soc_dai *dai) +{ + int rc = 0; + struct snd_kcontrol_new *knew = NULL; + struct snd_kcontrol *kctl = NULL; + const char *afe_dyn_mclk_ctl_name = "AFE_dyn_switch_mclk_source"; + + /* Add AFE port logging controls */ + knew = kzalloc(sizeof(struct snd_kcontrol_new), GFP_KERNEL); + if (!knew) + return -ENOMEM; + + knew->iface = SNDRV_CTL_ELEM_IFACE_MIXER; + knew->info = msm_pcm_afe_dyn_mclk_ctl_info; + knew->get = msm_pcm_afe_dyn_mclk_ctl_get; + knew->put = msm_pcm_afe_dyn_mclk_ctl_put; + knew->name = afe_dyn_mclk_ctl_name; + knew->private_value = dai->id; + kctl = snd_ctl_new1(knew, knew); + if (!kctl) { + kfree(knew); + return -ENOMEM; + } + + rc = snd_ctl_add(dai->component->card->snd_card, kctl); + if (rc < 0) + pr_err("%s: err add AFE dyn mclk control, DAI = %s\n", + __func__, dai->name); + + return rc; +} + +int jitter_cleaner_afe_enable_mclk_and_get_info_cb_func(void *private_data, + uint32_t enable, uint32_t mclk_freq, + struct afe_param_id_clock_set_v2_t *dyn_mclk_cfg) +{ + pr_debug("%s,%d\n", __func__, __LINE__); + return 0; +} + static int msm_dai_q6_dai_tdm_probe(struct snd_soc_dai *dai) { int rc = 0; + int port_idx = 0; struct msm_dai_q6_tdm_dai_data *tdm_dai_data = NULL; struct snd_kcontrol *data_format_kcontrol = NULL; struct snd_kcontrol *header_type_kcontrol = NULL; struct snd_kcontrol *header_kcontrol = NULL; - int port_idx = 0; const struct snd_kcontrol_new *data_format_ctrl = NULL; const struct snd_kcontrol_new *header_type_ctrl = NULL; const struct snd_kcontrol_new *header_ctrl = NULL; @@ -10459,6 +10660,26 @@ static int msm_dai_q6_dai_tdm_probe(struct snd_soc_dai *dai) afe_port_loggging_control_added = 1; } + /* add AFE dyn mclk controls */ + if (!afe_dyn_mclk_control_added) { + 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", + __func__, dai->name); + goto rtn; + } + + afe_dyn_mclk_control_added = 1; + + rc = afe_register_ext_mclk_cb(jitter_cleaner_afe_enable_mclk_and_get_info_cb_func, + (void *)&global_dyn_mclk_cfg); + if (rc < 0) { + dev_err(dai->dev, "%s: add AFE afe_register_ext_mclk_cb failed : %s\n", + __func__, dai->name); + goto rtn; + } + } + if (tdm_dai_data->is_island_dai) rc = msm_dai_q6_add_island_mx_ctls( dai->component->card->snd_card, diff --git a/dsp/q6afe.c b/dsp/q6afe.c index 9a7d7def16c8..9334d4c2972f 100755 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -1,5 +1,6 @@ // 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. */ #include #include @@ -9792,7 +9793,6 @@ static int afe_set_lpass_clk_cfg_ext_mclk(int index, struct afe_clk_set *cfg, dyn_mclk_cfg.clk_id = cfg->clk_id; dyn_mclk_cfg.clk_attri = cfg->clk_attri; dyn_mclk_cfg.enable = cfg->enable; - pr_debug("%s: Minor version =0x%x clk id = %d\n", __func__, dyn_mclk_cfg.clk_set_minor_version, dyn_mclk_cfg.clk_id); pr_debug("%s: clk freq (Hz) = %d, clk attri = 0x%x\n", __func__, @@ -9831,6 +9831,83 @@ stop_mclk: return ret; } +int afe_set_lpass_clk_cfg_ext_mclk_v2(int index, struct afe_param_id_clock_set_v2_t *dyn_mclk_cfg, + uint32_t mclk_freq) +{ + struct param_hdr_v3 param_hdr; + int ret = 0; + + if (!dyn_mclk_cfg) { + pr_err("%s: clock cfg is NULL\n", __func__); + ret = -EINVAL; + return ret; + } + + if (index < 0 || index >= AFE_MAX_PORTS) { + pr_err("%s: index[%d] invalid!\n", __func__, index); + return -EINVAL; + } + + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = AFE_MODULE_CLOCK_SET; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_CLOCK_SET_V2; + param_hdr.param_size = sizeof(struct afe_param_id_clock_set_v2_t); + + if (afe_ext_mclk.ext_mclk_cb) { + ret = afe_ext_mclk.ext_mclk_cb(afe_ext_mclk.private_data, + dyn_mclk_cfg->enable, mclk_freq, dyn_mclk_cfg); + if (ret) { + pr_err_ratelimited("%s: get mclk cfg failed %d\n", + __func__, ret); + return ret; + } + } else { + pr_err_ratelimited("%s: mclk callback not registered\n", __func__); + return -EINVAL; + } + + dyn_mclk_cfg->clk_set_minor_version = 1; + + pr_debug("%s: Minor version =0x%x clk id = %d\n", __func__, + dyn_mclk_cfg->clk_set_minor_version, dyn_mclk_cfg->clk_id); + pr_debug("%s: clk freq (Hz) = %d, clk attri = 0x%x\n", __func__, + dyn_mclk_cfg->clk_freq_in_hz, dyn_mclk_cfg->clk_attri); + pr_debug("%s: clk root = 0x%x clk enable = 0x%x\n", __func__, + dyn_mclk_cfg->clk_root, dyn_mclk_cfg->enable); + pr_debug("%s: divider_2x =%d m = %d n = %d, d =%d\n", __func__, + dyn_mclk_cfg->divider_2x, dyn_mclk_cfg->m, dyn_mclk_cfg->n, + dyn_mclk_cfg->d); + + ret = afe_q6_interface_prepare(); + if (ret != 0) { + pr_err_ratelimited("%s: Q6 interface prepare failed %d\n", + __func__, ret); + goto stop_mclk; + } + + mutex_lock(&this_afe.afe_cmd_lock); + ret = q6afe_svc_pack_and_set_param_in_band(index, param_hdr, + (u8 *) dyn_mclk_cfg); + if (ret < 0) + pr_err_ratelimited("%s: ext MCLK clk cfg failed with ret %d\n", + __func__, ret); + + mutex_unlock(&this_afe.afe_cmd_lock); + + if (ret >= 0) + return ret; + +stop_mclk: + if (afe_ext_mclk.ext_mclk_cb && dyn_mclk_cfg->enable) { + afe_ext_mclk.ext_mclk_cb(afe_ext_mclk.private_data, + dyn_mclk_cfg->enable, mclk_freq, dyn_mclk_cfg); + } + + return ret; +} +EXPORT_SYMBOL(afe_set_lpass_clk_cfg_ext_mclk_v2); + /** * afe_set_lpass_clk_cfg - Set AFE clk config * diff --git a/include/dsp/apr_audio-v2.h b/include/dsp/apr_audio-v2.h index 4fb7b49c41fa..b70670c04d2b 100644 --- a/include/dsp/apr_audio-v2.h +++ b/include/dsp/apr_audio-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) 2022, Qualcomm Innovation Center, Inc. All rights reserved. */ @@ -12209,6 +12210,9 @@ struct afe_param_id_clip_bank_sel { /* Supported LPASS CLK root*/ #define Q6AFE_LPASS_CLK_ROOT_DEFAULT 0 +#define Q6AFE_LPASS_MCLK_IN0 1 +#define Q6AFE_LPASS_MCLK_IN1 2 + enum afe_lpass_clk_mode { Q6AFE_LPASS_MODE_BOTH_INVALID, Q6AFE_LPASS_MODE_CLK1_VALID, @@ -12362,6 +12366,8 @@ enum afe_lpass_clk_mode { /* Clock ID for AHB HDMI input */ #define Q6AFE_LPASS_CLK_ID_AHB_HDMI_INPUT 0x400 +#define Q6AFE_LPASS_CLK_ID_SPDIF_CORE 0x000 + /* Clock ID for the primary SPDIF output core. */ #define AFE_CLOCK_SET_CLOCK_ID_PRI_SPDIF_OUTPUT_CORE 0x500 /* Clock ID for the secondary SPDIF output core. */ @@ -12448,6 +12454,12 @@ struct afe_clk_set { #define AFE_PARAM_ID_CLOCK_SET_V2 0x000102E6 +#define AFE_CLOCK_SET_CLOCK_ROOT_DEFAULT 0x2 +#define AFE_CLOCK_DEFAULT_INTEGER_DIVIDER 0x0 +#define AFE_CLOCK_DEFAULT_M_VALUE 0x1 +#define AFE_CLOCK_DEFAULT_N_VALUE 0x2 +#define AFE_CLOCK_DEFAULT_D_VALUE 0x1 + #define AFE_API_VERSION_CLOCK_SET_V2 0x1 struct afe_param_id_clock_set_v2_t { diff --git a/include/dsp/q6afe-v2.h b/include/dsp/q6afe-v2.h index 48209087a8a2..016996e3bb67 100644 --- a/include/dsp/q6afe-v2.h +++ b/include/dsp/q6afe-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) 2022, Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef __Q6AFE_V2_H__ #define __Q6AFE_V2_H__ @@ -592,6 +593,8 @@ int afe_port_send_logging_cfg(u16 port_id, struct afe_param_id_port_data_log_disable_t *log_disable); int afe_get_av_dev_drift(struct afe_param_id_dev_timing_stats *timing_stats, u16 port); +int afe_set_lpass_clk_cfg_ext_mclk_v2(int index, + struct afe_param_id_clock_set_v2_t *dyn_mclk_cfg, uint32_t mclk_freq); int afe_get_sp_rx_tmax_xmax_logging_data( struct afe_sp_rx_tmax_xmax_logging_param *xt_logging, u16 port_id);