diff --git a/techpack/audio/asoc/Kbuild b/techpack/audio/asoc/Kbuild old mode 100755 new mode 100644 index 3f1f48f20c7b..42bb0ef77105 --- a/techpack/audio/asoc/Kbuild +++ b/techpack/audio/asoc/Kbuild @@ -139,6 +139,8 @@ COMMON_INC := -I$(AUDIO_ROOT)/$(COMMON_DIR) ############ ASoC Drivers ############ +MACHINE_OBJS += msm-common.o + # for SM8150 sound card driver ifdef CONFIG_SND_SOC_SM8150 MACHINE_OBJS += sm8150.o diff --git a/techpack/audio/asoc/codecs/audio-ext-clk-up.c b/techpack/audio/asoc/codecs/audio-ext-clk-up.c index 8ce627ac86b3..45f430d5e065 100644 --- a/techpack/audio/asoc/codecs/audio-ext-clk-up.c +++ b/techpack/audio/asoc/codecs/audio-ext-clk-up.c @@ -57,6 +57,8 @@ struct audio_ext_clk_priv { const char *clk_name; uint32_t lpass_core_hwvote_client_handle; uint32_t lpass_audio_hwvote_client_handle; + bool supports_ext_mclk; + uint32_t ext_mclk_freq; }; static struct audio_ext_clk audio_clk_array[]; @@ -70,46 +72,75 @@ static int audio_ext_clk_prepare(struct clk_hw *hw) { struct audio_ext_clk_priv *clk_priv = to_audio_clk(hw); struct pinctrl_info *pnctrl_info = &clk_priv->audio_clk.pnctrl_info; - int ret; + int ret = 0; static DEFINE_RATELIMIT_STATE(rtl, 1 * HZ, 1); if ((clk_priv->clk_src >= AUDIO_EXT_CLK_LPASS) && - (clk_priv->clk_src < AUDIO_EXT_CLK_LPASS_MAX)) { + (clk_priv->clk_src < AUDIO_EXT_CLK_LPASS_MAX)) { clk_priv->clk_cfg.enable = 1; - ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk_priv->clk_cfg); - if (ret < 0) { - if (__ratelimit(&rtl)) - pr_err_ratelimited("%s afe_set_digital_codec_core_clock failed\n", - __func__); - return ret; + + /* Case 1. Clock driver supports ext clk source && platform enables ext clk support + * - Use ext clk API + * Case 2. Clock driver supports ext clk source && platform has NOT enabled ext clk support + * - We try using ext clk API, it fails; we fall back to internal clk API + * Case 3. Clock driver doesn't support ext clk source + * - Use internal clk API + * + * NOTE: Platform, the machine driver, confirms ext clk support + * by registering for the 'ext_mclk_cb' callback with + * Q6AFE driver. + */ + if (clk_priv->supports_ext_mclk) { + ret = afe_set_lpass_clk_cfg_ext_mclk(IDX_RSVD_3, + &clk_priv->clk_cfg, + clk_priv->ext_mclk_freq); + if (ret == 0) { + goto prepare_pinctrl; + } else if (ret == -EOPNOTSUPP) { + pr_err_ratelimited("%s: ext mclk prepare failed; falling back to internal clk\n", + __func__); + } else { + goto err; + } } + ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, + &clk_priv->clk_cfg); + if (ret < 0) + goto err; } +prepare_pinctrl: if (pnctrl_info->pinctrl) { ret = pinctrl_select_state(pnctrl_info->pinctrl, - pnctrl_info->active); + pnctrl_info->active); if (ret) { pr_err("%s: active state select failed with %d\n", __func__, ret); return -EIO; } } - if (pnctrl_info->base) iowrite32(1, pnctrl_info->base); return 0; + +err: + if (__ratelimit(&rtl)) + pr_err_ratelimited("%s: afe_set_lpass_clk_cfg_ext_mclk failed with %d\n", + __func__, ret); + return ret; + } static void audio_ext_clk_unprepare(struct clk_hw *hw) { struct audio_ext_clk_priv *clk_priv = to_audio_clk(hw); struct pinctrl_info *pnctrl_info = &clk_priv->audio_clk.pnctrl_info; - int ret; + int ret = 0; static DEFINE_RATELIMIT_STATE(rtl, 1 * HZ, 1); if (pnctrl_info->pinctrl) { ret = pinctrl_select_state(pnctrl_info->pinctrl, - pnctrl_info->sleep); + pnctrl_info->sleep); if (ret) { pr_err("%s: active state select failed with %d\n", __func__, ret); @@ -117,19 +148,34 @@ static void audio_ext_clk_unprepare(struct clk_hw *hw) } } - if ((clk_priv->clk_src >= AUDIO_EXT_CLK_LPASS) && - (clk_priv->clk_src < AUDIO_EXT_CLK_LPASS_MAX)) { - clk_priv->clk_cfg.enable = 0; - ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk_priv->clk_cfg); - if (ret < 0) { - if (__ratelimit(&rtl)) - pr_err_ratelimited("%s: afe_set_lpass_clk_cfg failed, ret = %d\n", - __func__, ret); - } - } - if (pnctrl_info->base) iowrite32(0, pnctrl_info->base); + + if ((clk_priv->clk_src >= AUDIO_EXT_CLK_LPASS) && + (clk_priv->clk_src < AUDIO_EXT_CLK_LPASS_MAX)) { + clk_priv->clk_cfg.enable = 0; + if (clk_priv->supports_ext_mclk) { + ret = afe_set_lpass_clk_cfg_ext_mclk(IDX_RSVD_3, + &clk_priv->clk_cfg, + clk_priv->ext_mclk_freq); + if (ret == 0) { + goto exit; + } else if (ret == -EOPNOTSUPP) { + pr_err_ratelimited("%s: ext mclk unprepare failed; falling back to internal clk\n", + __func__); + } else { + goto exit; + } + } + ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk_priv->clk_cfg); + if (ret < 0) + goto exit; + } + +exit: + if (ret && __ratelimit(&rtl)) + pr_err_ratelimited("%s: afe_set_lpass_clk_cfg failed, ret = %d\n", + __func__, ret); } static u8 audio_ext_clk_get_parent(struct clk_hw *hw) @@ -518,6 +564,7 @@ static int audio_ref_clk_probe(struct platform_device *pdev) int ret; struct audio_ext_clk_priv *clk_priv; u32 clk_freq = 0, clk_id = 0, clk_src = 0, use_pinctrl = 0; + u32 ext_mclk_src_freq = 0; clk_priv = devm_kzalloc(&pdev->dev, sizeof(*clk_priv), GFP_KERNEL); if (!clk_priv) @@ -561,9 +608,27 @@ static int audio_ref_clk_probe(struct platform_device *pdev) if (!ret) clk_priv->clk_cfg.clk_id = clk_id; + if (of_property_read_bool(pdev->dev.of_node, + "qcom,supports-ext-mclk")) + clk_priv->supports_ext_mclk = true; + + ret = of_property_read_u32(pdev->dev.of_node, + "qcom,ext-mclk-src-freq", + &ext_mclk_src_freq); + if (!ret) { + clk_priv->ext_mclk_freq = ext_mclk_src_freq; + } else if (clk_priv->supports_ext_mclk) { + dev_err(&pdev->dev, "%s: qcom,ext-mclk-src-freq not defined\n", + __func__); + return ret; + } + dev_dbg(&pdev->dev, "%s: ext-clk freq: %d, lpass clk_id: %d, clk_src: %d\n", - __func__, clk_priv->clk_cfg.clk_freq_in_hz, - clk_priv->clk_cfg.clk_id, clk_priv->clk_src); + __func__, clk_priv->clk_cfg.clk_freq_in_hz, + clk_priv->clk_cfg.clk_id, clk_priv->clk_src); + dev_dbg(&pdev->dev, "%s: supports-ext-mclk: %d, ext-mclk-input-freq: %d\n", + __func__, clk_priv->supports_ext_mclk, clk_priv->ext_mclk_freq); + platform_set_drvdata(pdev, clk_priv); ret = of_property_read_string(pdev->dev.of_node, "pmic-clock-names", diff --git a/techpack/audio/asoc/codecs/bolero/rx-macro.c b/techpack/audio/asoc/codecs/bolero/rx-macro.c index 1d1153b510ff..50fb7dc9341d 100644 --- a/techpack/audio/asoc/codecs/bolero/rx-macro.c +++ b/techpack/audio/asoc/codecs/bolero/rx-macro.c @@ -465,6 +465,7 @@ struct rx_macro_priv { u16 default_clk_id; int8_t rx0_gain_val; int8_t rx1_gain_val; + u32 mclk_freq; }; static struct snd_soc_dai_driver rx_macro_dai[]; @@ -1411,11 +1412,12 @@ static int rx_macro_mclk_event(struct snd_soc_dapm_widget *w, int ret = 0; struct device *rx_dev = NULL; struct rx_macro_priv *rx_priv = NULL; - int mclk_freq = MCLK_FREQ; + int mclk_freq = 0; if (!rx_macro_get_data(component, &rx_dev, &rx_priv, __func__)) return -EINVAL; + mclk_freq = rx_priv->mclk_freq; dev_dbg(rx_dev, "%s: event = %d\n", __func__, event); switch (event) { case SND_SOC_DAPM_PRE_PMU: @@ -2689,7 +2691,8 @@ static void rx_macro_hphdelay_lutbypass(struct snd_soc_component *component, } if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_OFF(event)) { - snd_soc_component_update_bits(component, + if (!rx_priv->is_ear_mode_on) + snd_soc_component_update_bits(component, BOLERO_CDC_RX_RX0_RX_PATH_CFG1, 0x02, 0x00); snd_soc_component_update_bits(component, hph_lut_bypass_reg, @@ -4166,13 +4169,14 @@ static int rx_macro_probe(struct platform_device *pdev) { struct macro_ops ops = {0}; struct rx_macro_priv *rx_priv = NULL; - u32 rx_base_addr = 0, muxsel = 0; + u32 rx_base_addr = 0, muxsel = 0, mclk_freq = 0; char __iomem *rx_io_base = NULL, *muxsel_io = NULL; int ret = 0; u8 bcl_pmic_params[3]; u32 default_clk_id = 0; u32 is_used_rx_swr_gpio = 1; const char *is_used_rx_swr_gpio_dt = "qcom,is-used-swr-gpio"; + const char *cdc_mclk_clk_rate = "qcom,cdc-mclk-clk-rate"; if (!bolero_is_va_macro_registered(&pdev->dev)) { dev_err(&pdev->dev, @@ -4207,6 +4211,15 @@ static int rx_macro_probe(struct platform_device *pdev) __func__, "qcom,default-clk-id"); default_clk_id = RX_CORE_CLK; } + ret = of_property_read_u32(pdev->dev.of_node, cdc_mclk_clk_rate, + &mclk_freq); + if (ret) { + rx_priv->mclk_freq = MCLK_FREQ; + } else { + rx_priv->mclk_freq = mclk_freq; + } + dev_dbg(rx_priv->dev, + "%s: mclk_freq = %u\n", __func__, rx_priv->mclk_freq); if (of_find_property(pdev->dev.of_node, is_used_rx_swr_gpio_dt, NULL)) { ret = of_property_read_u32(pdev->dev.of_node, diff --git a/techpack/audio/asoc/codecs/bolero/tx-macro.c b/techpack/audio/asoc/codecs/bolero/tx-macro.c index 6b0d1d82be81..208d480c0075 100644 --- a/techpack/audio/asoc/codecs/bolero/tx-macro.c +++ b/techpack/audio/asoc/codecs/bolero/tx-macro.c @@ -186,6 +186,7 @@ struct tx_macro_priv { bool register_event_listener; u16 current_clk_id; int disable_afe_wakeup_event_listener; + u32 mclk_freq; }; static bool tx_macro_get_data(struct snd_soc_component *component, @@ -2985,13 +2986,12 @@ static int tx_macro_validate_dmic_sample_rate(u32 dmic_sample_rate, struct tx_macro_priv *tx_priv) { u32 div_factor = TX_MACRO_CLK_DIV_2; - u32 mclk_rate = TX_MACRO_MCLK_FREQ; if (dmic_sample_rate == TX_MACRO_DMIC_SAMPLE_RATE_UNDEFINED || - mclk_rate % dmic_sample_rate != 0) + tx_priv->mclk_freq % dmic_sample_rate != 0) goto undefined_rate; - div_factor = mclk_rate / dmic_sample_rate; + div_factor = tx_priv->mclk_freq / dmic_sample_rate; switch (div_factor) { case 2: @@ -3019,13 +3019,13 @@ static int tx_macro_validate_dmic_sample_rate(u32 dmic_sample_rate, /* Valid dmic DIV factors */ dev_dbg(tx_priv->dev, "%s: DMIC_DIV = %u, mclk_rate = %u\n", - __func__, div_factor, mclk_rate); + __func__, div_factor, tx_priv->mclk_freq); return dmic_sample_rate; undefined_rate: dev_dbg(tx_priv->dev, "%s: Invalid rate %d, for mclk %d\n", - __func__, dmic_sample_rate, mclk_rate); + __func__, dmic_sample_rate, tx_priv->mclk_freq); dmic_sample_rate = TX_MACRO_DMIC_SAMPLE_RATE_UNDEFINED; return dmic_sample_rate; @@ -3362,10 +3362,11 @@ static int tx_macro_probe(struct platform_device *pdev) { struct macro_ops ops = {0}; struct tx_macro_priv *tx_priv = NULL; - u32 tx_base_addr = 0, sample_rate = 0; + u32 tx_base_addr = 0, sample_rate = 0, mclk_freq = 0; char __iomem *tx_io_base = NULL; int ret = 0; const char *dmic_sample_rate = "qcom,tx-dmic-sample-rate"; + const char *cdc_mclk_clk_rate = "qcom,cdc-mclk-clk-rate"; u32 is_used_tx_swr_gpio = 1; const char *is_used_tx_swr_gpio_dt = "qcom,is-used-swr-gpio"; u32 disable_afe_wakeup_event_listener = 0; @@ -3425,6 +3426,17 @@ static int tx_macro_probe(struct platform_device *pdev) return -ENOMEM; } tx_priv->tx_io_base = tx_io_base; + + ret = of_property_read_u32(pdev->dev.of_node, cdc_mclk_clk_rate, + &mclk_freq); + if (ret) { + tx_priv->mclk_freq = TX_MACRO_MCLK_FREQ; + } else { + tx_priv->mclk_freq = mclk_freq; + } + dev_dbg(tx_priv->dev, + "%s: mclk_freq = %u\n", __func__, tx_priv->mclk_freq); + ret = of_property_read_u32(pdev->dev.of_node, dmic_sample_rate, &sample_rate); if (ret) { diff --git a/techpack/audio/asoc/codecs/bolero/va-macro.c b/techpack/audio/asoc/codecs/bolero/va-macro.c index 6cd9e9402b64..363c7a41ce2c 100644 --- a/techpack/audio/asoc/codecs/bolero/va-macro.c +++ b/techpack/audio/asoc/codecs/bolero/va-macro.c @@ -186,6 +186,7 @@ struct va_macro_priv { u16 current_clk_id; int pcm_rate[VA_MACRO_NUM_DECIMATORS]; bool dev_up; + u32 mclk_freq; }; static bool va_macro_get_data(struct snd_soc_component *component, @@ -1567,6 +1568,12 @@ static int va_macro_hw_params(struct snd_pcm_substream *substream, dai->name, dai->id, params_rate(params), params_channels(params)); + if (va_priv->mclk_freq != VA_MACRO_MCLK_FREQ) { + dev_err(va_dev, "%s: unsupported VA mclk: %u\n", + __func__, va_priv->mclk_freq); + return -EINVAL; + } + sample_rate = params_rate(params); if (sample_rate > 16000) va_priv->clk_div_switch = true; @@ -2636,13 +2643,12 @@ static int va_macro_validate_dmic_sample_rate(u32 dmic_sample_rate, struct va_macro_priv *va_priv) { u32 div_factor; - u32 mclk_rate = VA_MACRO_MCLK_FREQ; if (dmic_sample_rate == VA_MACRO_DMIC_SAMPLE_RATE_UNDEFINED || - mclk_rate % dmic_sample_rate != 0) + va_priv->mclk_freq % dmic_sample_rate != 0) goto undefined_rate; - div_factor = mclk_rate / dmic_sample_rate; + div_factor = va_priv->mclk_freq / dmic_sample_rate; switch (div_factor) { case 2: @@ -2670,13 +2676,13 @@ static int va_macro_validate_dmic_sample_rate(u32 dmic_sample_rate, /* Valid dmic DIV factors */ dev_dbg(va_priv->dev, "%s: DMIC_DIV = %u, mclk_rate = %u\n", - __func__, div_factor, mclk_rate); + __func__, div_factor, va_priv->mclk_freq); return dmic_sample_rate; undefined_rate: dev_dbg(va_priv->dev, "%s: Invalid rate %d, for mclk %d\n", - __func__, dmic_sample_rate, mclk_rate); + __func__, dmic_sample_rate, va_priv->mclk_freq); dmic_sample_rate = VA_MACRO_DMIC_SAMPLE_RATE_UNDEFINED; return dmic_sample_rate; @@ -3061,7 +3067,7 @@ static int va_macro_probe(struct platform_device *pdev) { struct macro_ops ops; struct va_macro_priv *va_priv; - u32 va_base_addr, sample_rate = 0; + u32 va_base_addr, sample_rate = 0, mclk_freq = 0; char __iomem *va_io_base; bool va_without_decimation = false; const char *micb_supply_str = "va-vdd-micb-supply"; @@ -3074,6 +3080,7 @@ static int va_macro_probe(struct platform_device *pdev) struct clk *lpass_audio_hw_vote = NULL; u32 is_used_va_swr_gpio = 0; const char *is_used_va_swr_gpio_dt = "qcom,is-used-swr-gpio"; + const char *cdc_mclk_clk_rate = "qcom,cdc-mclk-clk-rate"; va_priv = devm_kzalloc(&pdev->dev, sizeof(struct va_macro_priv), GFP_KERNEL); @@ -3092,6 +3099,15 @@ static int va_macro_probe(struct platform_device *pdev) "qcom,va-without-decimation"); va_priv->va_without_decimation = va_without_decimation; + ret = of_property_read_u32(pdev->dev.of_node, cdc_mclk_clk_rate, + &mclk_freq); + if (ret) { + va_priv->mclk_freq = VA_MACRO_MCLK_FREQ; + } else { + va_priv->mclk_freq = mclk_freq; + } + dev_dbg(va_priv->dev, + "%s: mclk_freq = %u\n", __func__, va_priv->mclk_freq); ret = of_property_read_u32(pdev->dev.of_node, dmic_sample_rate, &sample_rate); if (ret) { diff --git a/techpack/audio/asoc/codecs/msm_hdmi_codec_rx.c b/techpack/audio/asoc/codecs/msm_hdmi_codec_rx.c index b0208ededa3b..be46409219f2 100644 --- a/techpack/audio/asoc/codecs/msm_hdmi_codec_rx.c +++ b/techpack/audio/asoc/codecs/msm_hdmi_codec_rx.c @@ -1,5 +1,6 @@ // 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. */ #include #include @@ -32,6 +33,7 @@ enum { DP_CONTROLLER0 = 0, + HDMI_CONTROLLER, DP_CONTROLLER_MAX, }; @@ -96,7 +98,7 @@ static int msm_ext_disp_edid_ctl_info(struct snd_kcontrol *kcontrol, codec_data->ctl[dai_id], codec_data->stream[dai_id]); mutex_lock(&codec_data->dp_ops_lock); - if (dai_id == HDMI_MS_DAI) + if (dai_id == HDMI_DAI) type = EXT_DISPLAY_TYPE_HDMI; else type = EXT_DISPLAY_TYPE_DP; @@ -148,7 +150,7 @@ static int msm_ext_disp_edid_get(struct snd_kcontrol *kcontrol, codec_data->ctl[dai_id], codec_data->stream[dai_id]); mutex_lock(&codec_data->dp_ops_lock); - if (dai_id == HDMI_MS_DAI) + if (dai_id == HDMI_DAI) type = EXT_DISPLAY_TYPE_HDMI; else type = EXT_DISPLAY_TYPE_DP; @@ -214,7 +216,7 @@ static int msm_ext_disp_audio_type_get(struct snd_kcontrol *kcontrol, codec_data->ctl[dai_id], codec_data->stream[dai_id]); mutex_lock(&codec_data->dp_ops_lock); - if (dai_id == HDMI_MS_DAI) + if (dai_id == HDMI_DAI) type = EXT_DISPLAY_TYPE_HDMI; else type = EXT_DISPLAY_TYPE_DP; @@ -306,7 +308,7 @@ static int msm_ext_disp_audio_ack_set(struct snd_kcontrol *kcontrol, codec_data->ctl[dai_id], codec_data->stream[dai_id]); mutex_lock(&codec_data->dp_ops_lock); - if (dai_id == HDMI_MS_DAI) + if (dai_id == HDMI_DAI) type = EXT_DISPLAY_TYPE_HDMI; else type = EXT_DISPLAY_TYPE_DP; @@ -366,7 +368,7 @@ static int msm_ext_disp_audio_device_get(struct snd_kcontrol *kcontrol, int dai_id = ((struct soc_multi_mixer_control *) kcontrol->private_value)->shift; - if (dai_id < 0 || dai_id > DP_DAI2) { + if (dai_id < 0 || dai_id >= DP_DAI_MAX) { dev_err(component->dev, "%s: invalid dai id: %d\n", __func__, dai_id); rc = -EINVAL; @@ -398,7 +400,7 @@ static int msm_ext_disp_audio_device_set(struct snd_kcontrol *kcontrol, int dai_id = ((struct soc_multi_mixer_control *) kcontrol->private_value)->shift; - if (dai_id < 0 || dai_id > DP_DAI2) { + if (dai_id < 0 || dai_id >= DP_DAI_MAX) { dev_err(component->dev, "%s: invalid dai id: %d\n", __func__, dai_id); rc = -EINVAL; @@ -498,8 +500,8 @@ static const struct snd_kcontrol_new msm_ext_disp_codec_rx_controls[] = { SND_SOC_NOPM, DP_DAI2, DP_STREAM_MAX - 1, 0, 2, msm_ext_disp_audio_device_get, msm_ext_disp_audio_device_set), - SOC_SINGLE_MULTI_EXT("External HDMI Device", - SND_SOC_NOPM, HDMI_MS_DAI, DP_STREAM_MAX - 1, 0, 2, + SOC_SINGLE_MULTI_EXT("External HDMI Audio Device", + SND_SOC_NOPM, HDMI_DAI, DP_STREAM_MAX - 1, 0, 2, msm_ext_disp_audio_device_get, msm_ext_disp_audio_device_set), @@ -526,7 +528,7 @@ static int msm_ext_disp_audio_codec_rx_dai_startup( codec_data->ctl[dai->id], codec_data->stream[dai->id]); mutex_lock(&codec_data->dp_ops_lock); - if (dai->id == HDMI_MS_DAI) + if (dai->id == HDMI_DAI) type = EXT_DISPLAY_TYPE_HDMI; else type = EXT_DISPLAY_TYPE_DP; @@ -587,7 +589,7 @@ static int msm_ext_disp_audio_codec_rx_dai_hw_params( codec_data->ctl[dai->id], codec_data->stream[dai->id]); mutex_lock(&codec_data->dp_ops_lock); - if (dai->id == HDMI_MS_DAI) + if (dai->id == HDMI_DAI) type = EXT_DISPLAY_TYPE_HDMI; else type = EXT_DISPLAY_TYPE_DP; @@ -661,6 +663,10 @@ static int msm_ext_disp_audio_codec_rx_dai_hw_params( audio_setup_params.down_mix = down_mix; mutex_lock(&codec_data->dp_ops_lock); + if (dai->id == HDMI_DAI) + type = EXT_DISPLAY_TYPE_HDMI; + else + type = EXT_DISPLAY_TYPE_DP; SWITCH_DP_CODEC(codec_info, codec_data, dai->id, type); rc = msm_ext_disp_select_audio_codec(codec_data->ext_disp_core_pdev, &codec_info); @@ -701,7 +707,7 @@ static void msm_ext_disp_audio_codec_rx_dai_shutdown( codec_data->ctl[dai->id], codec_data->stream[dai->id]); mutex_lock(&codec_data->dp_ops_lock); - if (dai->id == HDMI_MS_DAI) + if (dai->id == HDMI_DAI) type = EXT_DISPLAY_TYPE_HDMI; else type = EXT_DISPLAY_TYPE_DP; diff --git a/techpack/audio/asoc/codecs/wcd937x/wcd937x.c b/techpack/audio/asoc/codecs/wcd937x/wcd937x.c index 09b6246412bf..a3cfa15b555c 100644 --- a/techpack/audio/asoc/codecs/wcd937x/wcd937x.c +++ b/techpack/audio/asoc/codecs/wcd937x/wcd937x.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2018-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 @@ -60,6 +60,8 @@ enum { HPH_COMP_DELAY, HPH_PA_DELAY, AMIC2_BCS_ENABLE, + WCD_HPHL_EN, + WCD_EAR_EN, }; static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1); @@ -876,6 +878,7 @@ static int wcd937x_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, set_bit(HPH_PA_DELAY, &wcd937x->status_mask); snd_soc_component_update_bits(component, WCD937X_DIGITAL_PDM_WD_CTL0, 0x17, 0x13); + set_bit(WCD_HPHL_EN, &wcd937x->status_mask); break; case SND_SOC_DAPM_POST_PMU: /* @@ -906,12 +909,14 @@ static int wcd937x_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, WCD937X_IRQ_HPHL_PDM_WD_INT); break; case SND_SOC_DAPM_PRE_PMD: - wcd_disable_irq(&wcd937x->irq_info, + if (!test_bit(WCD_EAR_EN, &wcd937x->status_mask)) { + wcd_disable_irq(&wcd937x->irq_info, WCD937X_IRQ_HPHL_PDM_WD_INT); - if (wcd937x->update_wcd_event) - wcd937x->update_wcd_event(wcd937x->handle, + if (wcd937x->update_wcd_event) + wcd937x->update_wcd_event(wcd937x->handle, SLV_BOLERO_EVT_RX_MUTE, (WCD_RX1 << 0x10 | 0x1)); + } blocking_notifier_call_chain(&wcd937x->mbhc->notifier, WCD_EVENT_PRE_HPHL_PA_OFF, &wcd937x->mbhc->wcd_mbhc); @@ -942,6 +947,7 @@ static int wcd937x_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, WCD_CLSH_EVENT_POST_PA, WCD_CLSH_STATE_HPHL, hph_mode); + clear_bit(WCD_HPHL_EN, &wcd937x->status_mask); break; }; return ret; @@ -1030,10 +1036,12 @@ static int wcd937x_codec_enable_ear_pa(struct snd_soc_dapm_widget *w, snd_soc_component_update_bits(component, WCD937X_DIGITAL_PDM_WD_CTL2, 0x05, 0x05); - else + else { snd_soc_component_update_bits(component, WCD937X_DIGITAL_PDM_WD_CTL0, 0x17, 0x13); + set_bit(WCD_EAR_EN, &wcd937x->status_mask); + } if (!wcd937x->comp1_enable) snd_soc_component_update_bits(component, WCD937X_ANA_EAR_COMPANDER_CTL, 0x80, 0x80); @@ -1056,16 +1064,24 @@ static int wcd937x_codec_enable_ear_pa(struct snd_soc_dapm_widget *w, WCD937X_IRQ_HPHL_PDM_WD_INT); break; case SND_SOC_DAPM_PRE_PMD: - if (wcd937x->ear_rx_path & EAR_RX_PATH_AUX) + if (wcd937x->ear_rx_path & EAR_RX_PATH_AUX) { wcd_disable_irq(&wcd937x->irq_info, WCD937X_IRQ_AUX_PDM_WD_INT); - else - wcd_disable_irq(&wcd937x->irq_info, + if (wcd937x->update_wcd_event) + wcd937x->update_wcd_event(wcd937x->handle, + SLV_BOLERO_EVT_RX_MUTE, + (WCD_RX1 << 0x10 | 0x1)); + } + else { + if(!test_bit(WCD_HPHL_EN, &wcd937x->status_mask)) { + wcd_disable_irq(&wcd937x->irq_info, WCD937X_IRQ_HPHL_PDM_WD_INT); - if (wcd937x->update_wcd_event) - wcd937x->update_wcd_event(wcd937x->handle, + if (wcd937x->update_wcd_event) + wcd937x->update_wcd_event(wcd937x->handle, SLV_BOLERO_EVT_RX_MUTE, (WCD_RX1 << 0x10 | 0x1)); + } + } break; case SND_SOC_DAPM_POST_PMD: if (!wcd937x->comp1_enable) @@ -1082,10 +1098,12 @@ static int wcd937x_codec_enable_ear_pa(struct snd_soc_dapm_widget *w, snd_soc_component_update_bits(component, WCD937X_DIGITAL_PDM_WD_CTL2, 0x05, 0x00); - else + else { snd_soc_component_update_bits(component, WCD937X_DIGITAL_PDM_WD_CTL0, 0x17, 0x00); + clear_bit(WCD_EAR_EN, &wcd937x->status_mask); + } usleep_range(10000, 10010); /* disable EAR CnP FSM */ snd_soc_component_update_bits(component, @@ -1151,13 +1169,16 @@ static int wcd937x_enable_rx1(struct snd_soc_dapm_widget *w, wcd937x_rx_connect_port(component, COMP_L, true); break; case SND_SOC_DAPM_POST_PMD: - wcd937x_rx_connect_port(component, HPH_L, false); - if (wcd937x->comp1_enable) - wcd937x_rx_connect_port(component, COMP_L, false); - wcd937x_rx_clk_disable(component); - snd_soc_component_update_bits(component, + if (!test_bit(WCD_HPHL_EN, &wcd937x->status_mask) && + !test_bit(WCD_EAR_EN, &wcd937x->status_mask)) { + wcd937x_rx_connect_port(component, HPH_L, false); + if (wcd937x->comp1_enable) + wcd937x_rx_connect_port(component, COMP_L, false); + wcd937x_rx_clk_disable(component); + snd_soc_component_update_bits(component, WCD937X_DIGITAL_CDC_DIG_CLK_CTL, 0x01, 0x00); + } break; }; return 0; diff --git a/techpack/audio/asoc/lahaina.c b/techpack/audio/asoc/lahaina.c index 82b18769c339..d21d8f582ac5 100644 --- a/techpack/audio/asoc/lahaina.c +++ b/techpack/audio/asoc/lahaina.c @@ -41,6 +41,7 @@ #include "codecs/bolero/wsa-macro.h" #include "lahaina-port-config.h" #include "msm_dailink.h" +#include "msm-common.h" #define DRV_NAME "lahaina-asoc-snd" #define __CHIPSET__ "LAHAINA " @@ -132,16 +133,6 @@ enum { AUX_PCM_MAX, }; -enum { - PRIM_MI2S = 0, - SEC_MI2S, - TERT_MI2S, - QUAT_MI2S, - QUIN_MI2S, - SEN_MI2S, - MI2S_MAX, -}; - enum { WSA_CDC_DMA_RX_0 = 0, WSA_CDC_DMA_RX_1, @@ -182,28 +173,42 @@ enum { AFE_LOOPBACK_TX_IDX_MAX, }; -struct msm_asoc_mach_data { - struct snd_info_entry *codec_root; - int usbc_en2_gpio; /* used by gpio driver API */ - int lito_v2_enabled; - struct device_node *dmic01_gpio_p; /* used by pinctrl API */ - struct device_node *dmic23_gpio_p; /* used by pinctrl API */ - struct device_node *dmic45_gpio_p; /* used by pinctrl API */ - struct device_node *mi2s_gpio_p[MI2S_MAX]; /* used by pinctrl API */ - atomic_t mi2s_gpio_ref_count[MI2S_MAX]; /* used by pinctrl API */ - struct device_node *us_euro_gpio_p; /* used by pinctrl API */ - struct pinctrl *usbc_en2_gpio_p; /* used by pinctrl API */ - struct device_node *hph_en1_gpio_p; /* used by pinctrl API */ - struct device_node *hph_en0_gpio_p; /* used by pinctrl API */ - bool is_afe_config_done; - struct device_node *fsa_handle; - struct clk *lpass_audio_hw_vote; - int core_audio_vote_count; - u32 wsa_max_devs; - u32 tdm_max_slots; /* Max TDM slots used */ - int wcd_disabled; - int (*get_wsa_dev_num)(struct snd_soc_component*); - struct afe_cps_hw_intf_cfg cps_config; +#define MCLK_CFG_CELLS 5 +struct ext_mclk_freq_cfg { + u32 clk_freq; + u32 div2x; + u32 m; + u32 n; + u32 d; +}; + +/* Coupled with "qcom,ext-mclk-srcs" DTSI property */ +#define MCLK_SRCS_CELLS 3 +struct ext_mclk_src_cfg { + u32 clk_id; + u32 clk_root; + u32 gpio_idx; +}; + +struct ext_mclk_cfg_info { + u32 mclk_freq; + const char *prop; + struct ext_mclk_freq_cfg *mclk_cfg; + u32 num_mclk_cfg; +}; + +struct ext_mclk_gpio_info { + struct device_node *gpio_p; /* used by pinctrl API */ + const char *ext_mclk_muxsel_str; + uint32_t ext_mclk_muxsel_val; + uint32_t ref_cnt; + struct mutex lock; +}; + +struct ext_mclk_src_info { + u32 clk_id; + u32 clk_root; + struct ext_mclk_gpio_info *gpio_info; }; struct tdm_port { @@ -227,6 +232,19 @@ struct dev_config { u32 channels; }; +static bool ext_mclk_enable; +static uint32_t num_ext_mclk_gpios; +static struct ext_mclk_gpio_info *ext_mclk_gpio_info; +static struct ext_mclk_src_cfg *ext_mclk_src_info; +static struct ext_mclk_cfg_info ext_mclk_freq_info[MCLK_FREQ_MAX] = { + [MCLK_FREQ_11P2896_MHZ] = {11289600, "ext-mclk-cfg-11p2896", NULL, 0}, + [MCLK_FREQ_12P288_MHZ] = {12288000, "ext-mclk-cfg-12p288", NULL, 0}, + [MCLK_FREQ_16P384_MHZ] = {16384000, "ext-mclk-cfg-16p384", NULL, 0}, + [MCLK_FREQ_19P200_MHZ] = {19200000, "ext-mclk-cfg-19p200", NULL, 0}, + [MCLK_FREQ_22P5792_MHZ] = {22579200, "ext-mclk-cfg-22p5792", NULL, 0}, + [MCLK_FREQ_24P576_MHZ] = {24576000, "ext-mclk-cfg-24p576", NULL, 0}, +}; + /* Default configuration of slimbus channels */ static struct dev_config slim_rx_cfg[] = { [SLIM_RX_7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, @@ -1027,6 +1045,213 @@ static cpumask_t audio_cpu_map = CPU_MASK_NONE; static struct dev_pm_qos_request *msm_audio_req; static unsigned int qos_client_active_cnt; +static int lahaina_audio_vote(struct snd_soc_card *card, bool enable) +{ + struct msm_asoc_mach_data *pdata = NULL; + int ret = 0; + + if (!card) { + pr_err("%s: sound card is NULL\n", __func__); + return -EINVAL; + } + + pdata = snd_soc_card_get_drvdata(card); + if (!pdata || !pdata->lpass_audio_hw_vote) { + pr_err("%s: lpass audio hw voting not supported\n", __func__); + return -EINVAL; + } + + /* Locking and reference counting is handled by the underlying clock + * framework. + */ + if (enable) { + ret = clk_prepare_enable(pdata->lpass_audio_hw_vote); + if (ret < 0) { + dev_err(card->dev, "%s: audio vote error: %d\n", + __func__, ret); + return ret; + } + } else { + clk_disable_unprepare(pdata->lpass_audio_hw_vote); + } + + return ret; +} + +static int lahaina_populate_ext_mclk_cfg(struct ext_mclk_cfg_info *freq_cfg, + uint32_t mclk_freq, + struct afe_param_id_clock_set_v2_t *dyn_mclk_cfg) +{ + struct ext_mclk_freq_cfg *mclk_cfg = NULL; + uint32_t mclk_cfg_entries = 0; + enum afe_mclk_freq freq = MCLK_FREQ_MIN; + int i = 0; + + if (!freq_cfg || !dyn_mclk_cfg) + return -EINVAL; + + for (freq = MCLK_FREQ_MIN; freq < MCLK_FREQ_MAX; freq++) { + if (freq_cfg[freq].mclk_freq == mclk_freq) + break; + } + + if (freq == MCLK_FREQ_MAX) { + pr_err("%s: unsupported mclk freq: %u\n", __func__, mclk_freq); + return -EINVAL; + } + + if (!freq_cfg[freq].mclk_cfg || + !freq_cfg[freq].num_mclk_cfg) { + pr_err("%s: cfg table unavailable for mclk freq: %u\n", + __func__, mclk_freq); + return -EINVAL; + } + + mclk_cfg = freq_cfg[freq].mclk_cfg; + mclk_cfg_entries = freq_cfg[freq].num_mclk_cfg; + + for (i = 0; i < mclk_cfg_entries; i++) { + if (mclk_cfg[i].clk_freq == dyn_mclk_cfg->clk_freq_in_hz) { + dyn_mclk_cfg->divider_2x = mclk_cfg[i].div2x; + dyn_mclk_cfg->m = mclk_cfg[i].m; + dyn_mclk_cfg->n = mclk_cfg[i].n; + dyn_mclk_cfg->d = mclk_cfg[i].d; + break; + } + } + + if (i == mclk_cfg_entries) { + pr_err("%s: requested output mclk freq %u is not supported\n", + __func__, dyn_mclk_cfg->clk_freq_in_hz); + return -EINVAL; + } + + return 0; +} + +static int lahaina_handle_ext_mclk_gpio(struct snd_soc_card *card, + struct ext_mclk_gpio_info *gpio_info, + uint32_t enable) +{ + int ret = 0; + + if (!gpio_info) + return -EINVAL; + + mutex_lock(&gpio_info->lock); + if (enable) { + if (++gpio_info->ref_cnt == 1) { + ret = msm_cdc_pinctrl_select_active_state(gpio_info->gpio_p); + if (ret) { + dev_err(card->dev, "%s: couldn't activate mclk pinctrl\n", + __func__); + goto unlock; + } + + if (gpio_info->ext_mclk_muxsel_str != NULL) { + ret = lahaina_audio_vote(card, true); + if (ret) { + dev_err(card->dev, "%s: HW voting failed, ret: %d\n", + __func__, ret); + goto unlock; + } + + ret = afe_set_lpass_ext_mclk_mux_cfg(gpio_info->ext_mclk_muxsel_str, + gpio_info->ext_mclk_muxsel_val); + lahaina_audio_vote(card, false); + } + } + } else { + if (--gpio_info->ref_cnt == 0) { + ret = msm_cdc_pinctrl_select_sleep_state(gpio_info->gpio_p); + if (ret) { + dev_err(card->dev, "%s: couldn't deactivate mclk pinctrl\n", + __func__); + } + } + } + +unlock: + mutex_unlock(&gpio_info->lock); + if (ret) + enable ? --gpio_info->ref_cnt : ++gpio_info->ref_cnt; + + return ret; +} + +static int lahaina_enable_and_get_mclk_cfg(void *private_data, uint32_t enable, + uint32_t mclk_freq, + struct afe_param_id_clock_set_v2_t *dyn_mclk_cfg) +{ + struct snd_soc_card *card = (struct snd_soc_card *)private_data; + struct msm_asoc_mach_data *pdata = NULL; + struct ext_mclk_src_info *ext_mclk_src = NULL; + int i = 0; + int ret = 0; + + if (!card || !dyn_mclk_cfg) + return -EINVAL; + + if (!ext_mclk_enable) { + dev_err(card->dev, "%s: ext mclk support not enabled on the platform\n", + __func__); + return -EOPNOTSUPP; + } + + pdata = snd_soc_card_get_drvdata(card); + if (!pdata || !pdata->ext_mclk_srcs || !pdata->num_ext_mclk_srcs) + return -EINVAL; + + for (i = 0; i < pdata->num_ext_mclk_srcs; i++) { + if (pdata->ext_mclk_srcs[i].clk_id == dyn_mclk_cfg->clk_id) + break; + } + + if (i == pdata->num_ext_mclk_srcs) { + dev_err(card->dev, "%s: unsupported clk id for ext mclk support: %u\n", + __func__, dyn_mclk_cfg->clk_id); + return -EINVAL; + } + + ext_mclk_src = &pdata->ext_mclk_srcs[i]; + if (!ext_mclk_src) { + dev_err(card->dev, "%s: ext mclk src/clk cfg unavailable for mclk: %u\n", + __func__, pdata->ext_mclk_srcs[i].clk_id); + return -EINVAL; + } + + /* Populate clk root */ + dyn_mclk_cfg->clk_root = (uint16_t) ext_mclk_src->clk_root; + + /* Populate div2x, M, N, D values */ + ret = lahaina_populate_ext_mclk_cfg(ext_mclk_freq_info, mclk_freq, + dyn_mclk_cfg); + if (ret) { + dev_err(card->dev, "%s: unable to populate ext mclk cfg, ret: %d\n", __func__, ret); + goto reset; + } + + /* Enable/disable ext mclk GPIO */ + ret = lahaina_handle_ext_mclk_gpio(card, ext_mclk_src->gpio_info, enable); + if (ret) { + dev_err(card->dev, "%s: unable to enable/disable ext mclk gpio, ret: %d\n", + __func__, ret); + goto reset; + } + + return 0; + +reset: + /* Reset clk cfg */ + dyn_mclk_cfg->divider_2x = 0; + dyn_mclk_cfg->m = 0; + dyn_mclk_cfg->n = 0; + dyn_mclk_cfg->d = 0; + dyn_mclk_cfg->clk_root = 0; + + return ret; +} + static void msm_audio_add_qos_request(void) { int i; @@ -1814,20 +2039,41 @@ static int tdm_get_sample_rate(int value) sample_rate = SAMPLING_RATE_8KHZ; break; case 1: - sample_rate = SAMPLING_RATE_16KHZ; + sample_rate = SAMPLING_RATE_11P025KHZ; break; case 2: - sample_rate = SAMPLING_RATE_32KHZ; + sample_rate = SAMPLING_RATE_16KHZ; break; case 3: - sample_rate = SAMPLING_RATE_48KHZ; + sample_rate = SAMPLING_RATE_22P05KHZ; break; case 4: - sample_rate = SAMPLING_RATE_176P4KHZ; + sample_rate = SAMPLING_RATE_32KHZ; break; case 5: + sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 6: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 7: + sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 8: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 9: + sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 10: + sample_rate = SAMPLING_RATE_192KHZ; + break; + case 11: sample_rate = SAMPLING_RATE_352P8KHZ; break; + case 12: + sample_rate = SAMPLING_RATE_384KHZ; + break; default: sample_rate = SAMPLING_RATE_48KHZ; break; @@ -1843,23 +2089,44 @@ static int tdm_get_sample_rate_val(int sample_rate) case SAMPLING_RATE_8KHZ: sample_rate_val = 0; break; - case SAMPLING_RATE_16KHZ: + case SAMPLING_RATE_11P025KHZ: sample_rate_val = 1; break; - case SAMPLING_RATE_32KHZ: + case SAMPLING_RATE_16KHZ: sample_rate_val = 2; break; - case SAMPLING_RATE_48KHZ: + case SAMPLING_RATE_22P05KHZ: sample_rate_val = 3; break; - case SAMPLING_RATE_176P4KHZ: + case SAMPLING_RATE_32KHZ: sample_rate_val = 4; break; - case SAMPLING_RATE_352P8KHZ: + case SAMPLING_RATE_44P1KHZ: sample_rate_val = 5; break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 10; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 11; + break; + case SAMPLING_RATE_384KHZ: + sample_rate_val = 12; + break; default: - sample_rate_val = 3; + sample_rate_val = 6; break; } return sample_rate_val; @@ -4742,6 +5009,25 @@ static const struct snd_kcontrol_new msm_snd_controls[] = { aux_pcm_tx_sample_rate_put), }; +static int msm_ext_mclk_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = ext_mclk_enable; + return 0; +} + +static int msm_ext_mclk_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ext_mclk_enable = (bool) ucontrol->value.integer.value[0]; + return 0; +} + +static const struct snd_kcontrol_new msm_ext_mclk_controls[] = { + SOC_SINGLE_BOOL_EXT("EXT MCLK Enable", 0, + msm_ext_mclk_get, msm_ext_mclk_put), +}; + static int msm_ext_disp_get_idx_from_beid(int32_t be_id) { int idx; @@ -8806,6 +9092,411 @@ static void parse_cps_configuration(struct platform_device *pdev, } } +static int msm_parse_ext_mclk_gpios(struct snd_soc_card *card, + struct ext_mclk_gpio_info **ext_mclk_gpios) +{ + int ret = 0; + uint32_t len = 0; + uint32_t num_gpios = 0; + struct device_node *np = NULL; + struct ext_mclk_gpio_info *gpio_info = NULL; + const char *ext_mclk_muxsel_str = NULL; + u32 ext_mclk_muxsel_val = 0; + int i = 0; + + if (!card || !card->dev || !card->dev->of_node) + return -EINVAL; + + np = card->dev->of_node; + + if (!of_get_property(np, "qcom,ext-mclk-gpios", &len)) { + dev_err(card->dev, "%s: ext mclk gpios not found in DT\n", __func__); + return -EINVAL; + } + + if (!len) { + dev_err(card->dev, "%s: invalid ext mclk gpios configuration in DT\n", + __func__); + return -EINVAL; + } + + num_gpios = len / sizeof(uint32_t); + dev_dbg(card->dev, "%d ext mclk gpios found\n", num_gpios); + + gpio_info = devm_kzalloc(card->dev, + num_gpios * sizeof(struct ext_mclk_gpio_info), + GFP_KERNEL); + if (!gpio_info) + return -ENOMEM; + + for (i = 0; i < num_gpios; i++) { + mutex_init(&gpio_info[i].lock); + gpio_info[i].ref_cnt = 0; + gpio_info[i].gpio_p = of_parse_phandle(np, "qcom,ext-mclk-gpios", i); + if (!gpio_info[i].gpio_p) { + dev_err(card->dev, "ext mclk gpio %d device node is NULL", i); + ret = -EINVAL; + goto free_gpio_info; + } + + /* aud_ref_mux is present only for LPI GPIOs on lahaina. + * Hence we mandate parsing of mux config only for LPI GPIOs. + * Review the existence of aud_ref_mux for LPI/TLMM GPIOs + * while porting this change to other platforms + */ + if (of_property_read_bool(gpio_info[i].gpio_p, "qcom,lpi-gpios")) { + ret = of_property_read_string(gpio_info[i].gpio_p, + "qcom,ext-mclk-muxsel-str", &ext_mclk_muxsel_str); + if (ret) { + dev_err(card->dev, "%s: qcom,ext-mclk-muxsel-str not found in DT\n", + __func__); + ret = -EINVAL; + goto free_gpio_info; + } else { + ret = of_property_read_u32(gpio_info[i].gpio_p, + "qcom,ext-mclk-muxsel-val", &ext_mclk_muxsel_val); + if (ret) { + dev_err(card->dev, "%s: qcom,ext-mclk-muxsel-val not found in DT\n", + __func__); + ret = -EINVAL; + goto free_gpio_info; + } + } + dev_dbg(card->dev, "%s: gpio_info[%d] - muxsel= %s, val= %u\n", + __func__, i, ext_mclk_muxsel_str, ext_mclk_muxsel_val); + gpio_info[i].ext_mclk_muxsel_str = ext_mclk_muxsel_str; + gpio_info[i].ext_mclk_muxsel_val = ext_mclk_muxsel_val; + } + } + + num_ext_mclk_gpios = num_gpios; + *ext_mclk_gpios = gpio_info; + dev_dbg(card->dev, "%s: ext mclk gpios probe successful\n", __func__); + return 0; + +free_gpio_info: + for (; i >= 0; i--) { + mutex_destroy(&gpio_info[i].lock); + of_node_put(gpio_info[i].gpio_p); + } + devm_kfree(card->dev, gpio_info); + gpio_info = NULL; + + *ext_mclk_gpios = NULL; + return ret; +} + +static int msm_parse_ext_mclk_srcs(struct snd_soc_card *card, + struct ext_mclk_src_cfg **ext_mclk_src, + uint32_t *n_srcs) +{ + int ret = 0; + struct ext_mclk_src_cfg *src_cfg = NULL; + struct device_node *np = NULL; + uint32_t len = 0; + uint32_t num_srcs = 0; + uint32_t cells = 0; + + if (!card || !card->dev || !card->dev->of_node) + return -EINVAL; + + np = card->dev->of_node; + + if (!of_get_property(np, "qcom,ext-mclk-srcs", &len)) { + dev_err(card->dev, "%s: ext mclk srcs cfg not found in DT\n", __func__); + return -EINVAL; + } + + ret = of_property_read_u32(np, "#ext-mclk-srcs-cells", &cells); + if (ret) { + dev_err(card->dev, "%s: ext mclk srcs cells not found in DT\n", __func__); + return ret; + } + + if (!len || (len % (cells * sizeof(uint32_t))) || + (cells != MCLK_SRCS_CELLS)) { + dev_err(card->dev, "%s: invalid ext mclk srcs cfg in DT\n", __func__); + return -EINVAL; + }; + + num_srcs = len / (cells * sizeof(uint32_t)); + + src_cfg = devm_kzalloc(card->dev, + num_srcs * sizeof(struct ext_mclk_src_cfg), + GFP_KERNEL); + if (!src_cfg) + return -ENOMEM; + + ret = of_property_read_u32_array(np, "qcom,ext-mclk-srcs", (u32 *)src_cfg, + cells * num_srcs); + if (ret) { + dev_err(card->dev, "%s: could not find %s entry in dt\n", + __func__, "qcom,ext-mclk-srcs"); + ret = -EINVAL; + goto free_mclk_array; + } + + *ext_mclk_src = src_cfg; + *n_srcs = num_srcs; + dev_dbg(card->dev, "%s: ext mclk srcs probe successful\n", __func__); + return 0; + +free_mclk_array: + devm_kfree(card->dev, src_cfg); + src_cfg = NULL; + + *ext_mclk_src = NULL; + *n_srcs = 0; + return ret; +} + +static int msm_parse_dt_ext_mclk_cfg(struct snd_soc_card *card, + enum afe_mclk_freq freq) +{ + int ret = 0; + struct ext_mclk_freq_cfg *mclk_cfg = NULL; + uint32_t len = 0; + uint32_t num_cfg = 0; + uint32_t cells = 0; + int i = 0; + struct device_node *np = NULL; + uint32_t *freq_cfg_tbl = NULL; + + if (!card || !card->dev || !card->dev->of_node) + return -EINVAL; + + np = card->dev->of_node; + + if (!of_get_property(np, ext_mclk_freq_info[freq].prop, &len)) { + dev_dbg(card->dev, "%s: ext mclk cfg for %s not found in DT\n", + __func__, ext_mclk_freq_info[freq].prop); + return 0; + } + + ret = of_property_read_u32(np, "#ext-mclk-cfg-cells", &cells); + if (ret) { + dev_err(card->dev, "%s: ext mclk cfg cells not found in DT\n", __func__); + return ret; + } + + if (!len || (len % (cells * sizeof(uint32_t))) || + (cells != MCLK_CFG_CELLS)) { + dev_err(card->dev, "%s: invalid mclk configuration in DT\n", __func__); + return -EINVAL; + }; + + num_cfg = len / (cells * sizeof(uint32_t)); + mclk_cfg = devm_kzalloc(card->dev, num_cfg * sizeof(struct ext_mclk_freq_cfg), + GFP_KERNEL); + if (!mclk_cfg) + return -ENOMEM; + + freq_cfg_tbl = devm_kzalloc(card->dev, cells * num_cfg * sizeof(uint32_t), + GFP_KERNEL); + if (!freq_cfg_tbl) { + ret = -ENOMEM; + goto free_mclk_cfg; + } + + ret = of_property_read_u32_array(np, ext_mclk_freq_info[freq].prop, + freq_cfg_tbl, cells * num_cfg); + if (ret) + goto free_freq_cfg_tbl; + + dev_dbg(card->dev, "table for %u freq\n", + ext_mclk_freq_info[freq].mclk_freq); + + for (i = 0; i < num_cfg; i++) { + memcpy(&mclk_cfg[i], &freq_cfg_tbl[i * cells], + sizeof(uint32_t) * cells); + dev_dbg(card->dev, + "clk freq (Hz): %u, div2x: %u, m: %u, n: %u, d: %u\n", + mclk_cfg[i].clk_freq, mclk_cfg[i].div2x, mclk_cfg[i].m, + mclk_cfg[i].n, mclk_cfg[i].d); + } + + ext_mclk_freq_info[freq].mclk_cfg = mclk_cfg; + ext_mclk_freq_info[freq].num_mclk_cfg = num_cfg; + + devm_kfree(card->dev, freq_cfg_tbl); + freq_cfg_tbl = NULL; + + return 0; + +free_freq_cfg_tbl: + devm_kfree(card->dev, freq_cfg_tbl); + freq_cfg_tbl = NULL; +free_mclk_cfg: + devm_kfree(card->dev, mclk_cfg); + mclk_cfg = NULL; + + return ret; +} + +static int msm_parse_ext_mclk_freq_cfg(struct snd_soc_card *card) +{ + int ret = 0; + int i = MCLK_FREQ_MIN; + + if (!card || !card->dev || !card->dev->of_node) + return -EINVAL; + + for (i = MCLK_FREQ_MIN; i < MCLK_FREQ_MAX; i++) { + ret = msm_parse_dt_ext_mclk_cfg(card, i); + if (ret < 0) + return ret; + } + + dev_dbg(card->dev, "%s: ext mclk freq probe successful!\n", __func__); + return ret; +} + +static void lahaina_ext_mclk_cfg_deinit(struct snd_soc_card *card) +{ + struct msm_asoc_mach_data *pdata = NULL; + int i = 0; + enum afe_mclk_freq freq = MCLK_FREQ_MIN; + + if (!card || !card->dev) + return; + + pdata = (struct msm_asoc_mach_data *) snd_soc_card_get_drvdata(card); + if (!pdata) + return; + + afe_unregister_ext_mclk_cb(); + + for (i = 0; i < pdata->num_ext_mclk_srcs; i++) + pdata->ext_mclk_srcs[i].gpio_info = NULL; + + pdata->num_ext_mclk_srcs = 0; + + if (ext_mclk_gpio_info) { + for (i = 0; i < num_ext_mclk_gpios; i++) { + mutex_destroy(&ext_mclk_gpio_info[i].lock); + of_node_put(ext_mclk_gpio_info[i].gpio_p); + } + devm_kfree(card->dev, ext_mclk_gpio_info); + ext_mclk_gpio_info = NULL; + } + + if (ext_mclk_src_info) { + devm_kfree(card->dev, ext_mclk_src_info); + ext_mclk_src_info = NULL; + } + + for (freq = MCLK_FREQ_MIN; freq < MCLK_FREQ_MAX; freq++) { + if (ext_mclk_freq_info[freq].mclk_cfg) + devm_kfree(card->dev, ext_mclk_freq_info[freq].mclk_cfg); + ext_mclk_freq_info[freq].mclk_cfg = NULL; + ext_mclk_freq_info[freq].num_mclk_cfg = 0; + } +} + +static int lahaina_ext_mclk_init_controls(struct snd_soc_card *card) +{ + int ret = 0; + + if (!card) { + pr_err("%s: sound card is NULL\n", __func__); + return -EINVAL; + } + + ret = snd_soc_add_card_controls(card, msm_ext_mclk_controls, + ARRAY_SIZE(msm_ext_mclk_controls)); + if (ret < 0) { + dev_err(card->dev, "%s: add_card_controls failed for ext mclk ctls: %d\n", + __func__, ret); + return ret; + } + + return ret; +} + +static int lahaina_ext_mclk_cfg_init(struct snd_soc_card *card) +{ + int ret = 0; + struct msm_asoc_mach_data *pdata = NULL; + uint32_t num_ext_mclk_srcs = 0; + int i = 0; + + if (!card || !card->dev || !card->dev->of_node) + return -EINVAL; + + pdata = (struct msm_asoc_mach_data *) snd_soc_card_get_drvdata(card); + if (!pdata) + return -EINVAL; + + ret = msm_parse_ext_mclk_gpios(card, &ext_mclk_gpio_info); + if (ret) { + dev_err(card->dev, "%s: unable to parse ext mclk gpios, ret: %d\n", + __func__, ret); + goto deinit; + } + + ret = msm_parse_ext_mclk_srcs(card, &ext_mclk_src_info, + &num_ext_mclk_srcs); + if (ret) { + dev_err(card->dev, "%s: unable to parse ext mclk srcs, ret: %d\n", + __func__, ret); + goto deinit; + } + + ret = msm_parse_ext_mclk_freq_cfg(card); + if (ret) { + dev_err(card->dev, "%s: unable to parse ext mclk freq cfg, ret: %d\n", + __func__, ret); + goto deinit; + } + + pdata->num_ext_mclk_srcs = num_ext_mclk_srcs; + + if (pdata->num_ext_mclk_srcs) { + pdata->ext_mclk_srcs = devm_kzalloc(card->dev, + num_ext_mclk_srcs * sizeof(struct ext_mclk_src_info), + GFP_KERNEL); + if (!pdata->ext_mclk_srcs) { + pdata->num_ext_mclk_srcs = 0; + ret = -ENOMEM; + goto deinit; + } + + for (i = 0; i < num_ext_mclk_srcs; i++) { + pdata->ext_mclk_srcs[i].clk_id = ext_mclk_src_info[i].clk_id; + pdata->ext_mclk_srcs[i].clk_root = ext_mclk_src_info[i].clk_root; + pdata->ext_mclk_srcs[i].gpio_info = &ext_mclk_gpio_info[ext_mclk_src_info[i].gpio_idx]; + + dev_dbg(card->dev, "%s: clk id: 0x%x clk root: 0x%x gpio idx: %d\n", + __func__, pdata->ext_mclk_srcs[i].clk_id, + pdata->ext_mclk_srcs[i].clk_root, + ext_mclk_src_info[i].gpio_idx); + } + + ret = lahaina_ext_mclk_init_controls(card); + if (ret) { + dev_err(card->dev, "%s: Could not init ext mclk mixer ctls, ret: %d\n", + __func__, ret); + goto deinit; + } + + ret = afe_register_ext_mclk_cb(lahaina_enable_and_get_mclk_cfg, + (void *)card); + if (ret) { + dev_err(card->dev, "%s: Could not register afe ext mclk cb, ret: %d\n", + __func__, ret); + goto deinit; + } + } + + dev_dbg(card->dev, "%s: ext mclk init successful!\n", __func__); + + return 0; + +deinit: + lahaina_ext_mclk_cfg_deinit(card); + return ret; +} + static int msm_asoc_machine_probe(struct platform_device *pdev) { struct snd_soc_card *card = NULL; @@ -8864,15 +9555,15 @@ static int msm_asoc_machine_probe(struct platform_device *pdev) goto err; } - /* Get maximum WSA device count for this platform */ - ret = of_property_read_u32(pdev->dev.of_node, - "qcom,wsa-max-devs", &pdata->wsa_max_devs); - if (ret) { - dev_info(&pdev->dev, - "%s: wsa-max-devs property missing in DT %s, ret = %d\n", - __func__, pdev->dev.of_node->full_name, ret); - pdata->wsa_max_devs = 0; - } + /* Get maximum WSA device count for this platform */ + ret = of_property_read_u32(pdev->dev.of_node, + "qcom,wsa-max-devs", &pdata->wsa_max_devs); + if (ret) { + dev_info(&pdev->dev, + "%s: wsa-max-devs property missing in DT %s, ret = %d\n", + __func__, pdev->dev.of_node->full_name, ret); + pdata->wsa_max_devs = 0; + } ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret == -EPROBE_DEFER) { @@ -8887,6 +9578,19 @@ static int msm_asoc_machine_probe(struct platform_device *pdev) dev_info(&pdev->dev, "%s: Sound card %s registered\n", __func__, card->name); + /* Enable ext clk support ONLY after sound card enumeration and + * registration has occurred with the internal clock + */ + if (of_property_read_bool(pdev->dev.of_node, "qcom,supports-ext-mclk")) { + ret = lahaina_ext_mclk_cfg_init(card); + if (ret) { + dev_err(&pdev->dev, "%s: ext mclk cfg init from DT failed: %d\n", + __func__, ret); + goto err; + } + pdata->supports_ext_mclk = 1; + } + ret = of_property_read_u32(pdev->dev.of_node, "qcom,tdm-max-slots", &pdata->tdm_max_slots); if (ret) { @@ -9029,7 +9733,11 @@ err: static int msm_asoc_machine_remove(struct platform_device *pdev) { struct snd_soc_card *card = platform_get_drvdata(pdev); + struct msm_asoc_mach_data *pdata = NULL; + pdata = snd_soc_card_get_drvdata(card); + if (pdata && pdata->supports_ext_mclk) + lahaina_ext_mclk_cfg_deinit(card); snd_event_master_deregister(&pdev->dev); snd_soc_unregister_card(card); msm_i2s_auxpcm_deinit(); diff --git a/techpack/audio/asoc/msm-common.c b/techpack/audio/asoc/msm-common.c new file mode 100644 index 000000000000..2fd559323f1b --- /dev/null +++ b/techpack/audio/asoc/msm-common.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm-common.h" + +int msm_lpass_audio_hw_vote_req(struct snd_pcm_substream *substream, bool enable) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + int rc = 0; + + if (enable) { + if (pdata->lpass_audio_hw_vote == NULL) { + dev_err(rtd->card->dev, "%s: Invalid lpass audio hw node\n", + __func__); + rc = -EINVAL; + goto rtn; + } + if (pdata->core_audio_vote_count == 0) { + rc = clk_prepare_enable(pdata->lpass_audio_hw_vote); + if (rc < 0) { + dev_err(rtd->card->dev, "%s: audio vote error\n", + __func__); + goto rtn; + } + } + + pdata->core_audio_vote_count++; + } else { + if (pdata->lpass_audio_hw_vote != NULL) { + if (--pdata->core_audio_vote_count == 0) { + clk_disable_unprepare( + pdata->lpass_audio_hw_vote); + } else if (pdata->core_audio_vote_count < 0) { + pr_err("%s: audio vote mismatch\n", __func__); + pdata->core_audio_vote_count = 0; + } + } else { + pr_err("%s: Invalid lpass audio hw node\n", __func__); + } + } + +rtn: + return rc; +} +EXPORT_SYMBOL(msm_lpass_audio_hw_vote_req); + diff --git a/techpack/audio/asoc/msm-common.h b/techpack/audio/asoc/msm-common.h new file mode 100644 index 000000000000..94ec25d27320 --- /dev/null +++ b/techpack/audio/asoc/msm-common.h @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef _MSM_COMMON_H_ +#define _MSM_COMMON_H_ + +#include +#include +#include + +enum { + PRIM_MI2S = 0, + SEC_MI2S, + TERT_MI2S, + QUAT_MI2S, + QUIN_MI2S, + SEN_MI2S, + MI2S_MAX, +}; + +struct msm_asoc_mach_data { + struct snd_info_entry *codec_root; + int usbc_en2_gpio; /* used by gpio driver API */ + int lito_v2_enabled; + struct device_node *dmic01_gpio_p; /* used by pinctrl API */ + struct device_node *dmic23_gpio_p; /* used by pinctrl API */ + struct device_node *dmic45_gpio_p; /* used by pinctrl API */ + struct device_node *mi2s_gpio_p[MI2S_MAX]; /* used by pinctrl API */ + atomic_t mi2s_gpio_ref_count[MI2S_MAX]; /* used by pinctrl API */ + struct device_node *us_euro_gpio_p; /* used by pinctrl API */ + struct pinctrl *usbc_en2_gpio_p; /* used by pinctrl API */ + struct device_node *hph_en1_gpio_p; /* used by pinctrl API */ + struct device_node *hph_en0_gpio_p; /* used by pinctrl API */ + bool supports_ext_mclk; + struct ext_mclk_src_info *ext_mclk_srcs; + u32 num_ext_mclk_srcs; + bool is_afe_config_done; + struct device_node *fsa_handle; + struct clk *lpass_audio_hw_vote; + int core_audio_vote_count; + u32 wsa_max_devs; + u32 tdm_max_slots; /* Max TDM slots used */ + int wcd_disabled; + int (*get_wsa_dev_num)(struct snd_soc_component*); + struct afe_cps_hw_intf_cfg cps_config; +}; + +int msm_lpass_audio_hw_vote_req(struct snd_pcm_substream *substream, bool enable); + +#endif diff --git a/techpack/audio/asoc/msm-compress-q6-v2.c b/techpack/audio/asoc/msm-compress-q6-v2.c index 6097ca2a7ea3..ce177a4b990f 100644 --- a/techpack/audio/asoc/msm-compress-q6-v2.c +++ b/techpack/audio/asoc/msm-compress-q6-v2.c @@ -1,39 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved. - * - * Changes from Qualcomm Innovation Center are provided under the following license: - * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted (subject to the limitations in the - * disclaimer below) provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * * Neither the name of Qualcomm Innovation Center, Inc. nor the - * names of its contributors may be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE - * GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT - * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ @@ -5219,8 +5186,13 @@ static int msm_compr_channel_mixer_cfg_ctl_put(struct snd_kcontrol *kcontrol, if (prtd && prtd->audio_client) { stream_id = prtd->audio_client->session; be_id = chmixer_pspd->port_idx; +#ifdef CONFIG_PLATFORM_AUTO + msm_pcm_routing_set_channel_mixer_runtime(fe_id, be_id, + stream_id, session_type, chmixer_pspd); +#else msm_pcm_routing_set_channel_mixer_runtime(be_id, stream_id, session_type, chmixer_pspd); +#endif } } diff --git a/techpack/audio/asoc/msm-dai-q6-hdmi-v2.c b/techpack/audio/asoc/msm-dai-q6-hdmi-v2.c index 42d53e721928..2e394cb3fe25 100644 --- a/techpack/audio/asoc/msm-dai-q6-hdmi-v2.c +++ b/techpack/audio/asoc/msm-dai-q6-hdmi-v2.c @@ -22,6 +22,7 @@ enum { DP_CONTROLLER0 = 0, DP_CONTROLLER1, + HDMI_CONTROLLER, DP_CONTROLLER_MAX, }; diff --git a/techpack/audio/asoc/msm-dai-q6-v2.c b/techpack/audio/asoc/msm-dai-q6-v2.c index bde03c63f5a7..5e3a4186b744 100644 --- a/techpack/audio/asoc/msm-dai-q6-v2.c +++ b/techpack/audio/asoc/msm-dai-q6-v2.c @@ -49,7 +49,18 @@ SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_S32_LE) +#define SAMPLING_RATE_11P025KHZ 11025 +#define SAMPLING_RATE_22P05KHZ 22050 +#define SAMPLING_RATE_44P1KHZ 44100 +#define SAMPLING_RATE_88P2KHZ 88200 +#define SAMPLING_RATE_176P4KHZ 176400 +#define SAMPLING_RATE_352P8KHZ 352800 + +#define IS_TDM_INTERFACE(x) \ +((x >= AFE_PORT_ID_TDM_PORT_RANGE_START) && (x < AFE_PORT_ID_TDM_PORT_RANGE_END)) + static int msm_mi2s_get_port_id(u32 mi2s_id, int stream, u16 *port_id); +int msm_lpass_audio_hw_vote_req(struct snd_pcm_substream *substream, bool enable); enum { ENC_FMT_NONE, @@ -300,6 +311,11 @@ enum { IDX_GROUP_TDM_MAX, }; +#define IS_FRACTIONAL(x) \ +((x == SAMPLING_RATE_11P025KHZ) || (x == SAMPLING_RATE_22P05KHZ) || \ +(x == SAMPLING_RATE_44P1KHZ) || (x == SAMPLING_RATE_88P2KHZ) || \ +(x == SAMPLING_RATE_176P4KHZ) || (x == SAMPLING_RATE_352P8KHZ)) + struct msm_dai_q6_dai_data { DECLARE_BITMAP(status_mask, STATUS_MAX); DECLARE_BITMAP(hwfree_status, STATUS_MAX); @@ -11665,6 +11681,9 @@ static int msm_dai_q6_tdm_prepare(struct snd_pcm_substream *substream, struct msm_dai_q6_tdm_dai_data *dai_data = dev_get_drvdata(dai->dev); u16 group_id = dai_data->group_cfg.tdm_cfg.group_id; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int index = rtd->cpu_dai->id; + int sample_rate = dai_data->rate; int group_idx = 0; atomic_t *group_ref = NULL; int intf_idx = PORT_ID_TO_INTF_IDX(dai->id); @@ -11688,6 +11707,15 @@ static int msm_dai_q6_tdm_prepare(struct snd_pcm_substream *substream, group_ref = &tdm_group_ref[group_idx]; if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + if (IS_TDM_INTERFACE(index) && (IS_FRACTIONAL(sample_rate))) { + rc = msm_lpass_audio_hw_vote_req(substream, true); + if (rc < 0) { + dev_err(dai->dev, "%s: fail to enable audio hw clk 0x%x\n", + __func__, dai->id); + goto rtn; + } + } + if (msm_dai_q6_get_tdm_clk_ref(group_idx) == 0) { /* TX and RX share the same clk. So enable the clk * per TDM interface. */ @@ -11746,6 +11774,9 @@ static int msm_dai_q6_tdm_prepare(struct snd_pcm_substream *substream, msm_dai_q6_tdm_set_clk(dai_data, dai->id, false); } + if (IS_TDM_INTERFACE(index) && (IS_FRACTIONAL(sample_rate))) + msm_lpass_audio_hw_vote_req(substream, false); + dev_err(dai->dev, "%s: fail to open AFE port 0x%x\n", __func__, dai->id); } else { @@ -11775,6 +11806,10 @@ static void msm_dai_q6_tdm_shutdown(struct snd_pcm_substream *substream, int group_idx = 0; atomic_t *group_ref = NULL; int intf_idx = PORT_ID_TO_INTF_IDX(dai->id); + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int index = cpu_dai->id; + int sample_rate = dai_data->rate; group_idx = msm_dai_q6_get_group_idx(dai->id); if (group_idx < 0) { @@ -11831,6 +11866,9 @@ static void msm_dai_q6_tdm_shutdown(struct snd_pcm_substream *substream, } + if (IS_TDM_INTERFACE(index) && (IS_FRACTIONAL(sample_rate))) + msm_lpass_audio_hw_vote_req(substream, false); + mutex_unlock(&tdm_mutex); } diff --git a/techpack/audio/asoc/msm-lsm-client.c b/techpack/audio/asoc/msm-lsm-client.c index 860ad10e79d3..9b82d8c49a51 100644 --- a/techpack/audio/asoc/msm-lsm-client.c +++ b/techpack/audio/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-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include #include @@ -879,6 +879,13 @@ static int msm_lsm_dereg_model(struct snd_pcm_substream *substream, if (p_info->model_id != 0 && p_info->param_type == LSM_DEREG_MULTI_SND_MODEL) { + + if(list_empty(&client->stage_cfg[p_info->stage_idx].sound_models)) { + pr_err("%s: sound_models list is empty \n", + __func__); + return -EINVAL; + } + list_for_each_entry(sm, &client->stage_cfg[p_info->stage_idx].sound_models, list) { @@ -2379,8 +2386,13 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream, prtd->lsm_client->get_param_payload = NULL; goto done; } + if (__builtin_uadd_overflow(sizeof(p_info_32), p_info_32.param_size, &size)) { + pr_err("%s: param size exceeds limit of %u bytes.\n", + __func__, UINT_MAX); + err = -EINVAL; + goto done; + } - size = sizeof(p_info_32) + p_info_32.param_size; param_info_rsp = kzalloc(size, GFP_KERNEL); if (!param_info_rsp) { diff --git a/techpack/audio/asoc/msm-pcm-loopback-v2.c b/techpack/audio/asoc/msm-pcm-loopback-v2.c index 0746ffd0c349..6d9a3a11c968 100644 --- a/techpack/audio/asoc/msm-pcm-loopback-v2.c +++ b/techpack/audio/asoc/msm-pcm-loopback-v2.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2013-2021, The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ /* * Add support for 24 and 32bit format for ASM loopback and playback session. @@ -1022,10 +1022,17 @@ static int msm_pcm_channel_mixer_cfg_ctl_put(struct snd_kcontrol *kcontrol, if (prtd->audio_client) { stream_id = prtd->audio_client->session; be_id = chmixer_pspd->port_idx; +#ifdef CONFIG_PLATFORM_AUTO + msm_pcm_routing_set_channel_mixer_runtime(fe_id, be_id, + stream_id, + session_type, + chmixer_pspd); +#else msm_pcm_routing_set_channel_mixer_runtime(be_id, stream_id, session_type, chmixer_pspd); +#endif } } mutex_unlock(&loopback_session_lock); diff --git a/techpack/audio/asoc/msm-pcm-q6-v2.c b/techpack/audio/asoc/msm-pcm-q6-v2.c index bb19c4b7d386..ac78ef98229a 100644 --- a/techpack/audio/asoc/msm-pcm-q6-v2.c +++ b/techpack/audio/asoc/msm-pcm-q6-v2.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ /* * Add support for 24 and 32bit format for ASM loopback and playback session. @@ -1329,7 +1329,7 @@ static int msm_pcm_capture_copy(struct snd_pcm_substream *substream, offset = prtd->in_frame_info[idx].offset; pr_debug("Offset value = %d\n", offset); - if (offset >= size) { + if (size && offset >= size) { pr_err("%s: Invalid dsp buf offset\n", __func__); ret = -EFAULT; q6asm_cpu_buf_release(OUT, prtd->audio_client); @@ -2681,10 +2681,17 @@ static int msm_pcm_channel_mixer_cfg_ctl_put(struct snd_kcontrol *kcontrol, if (chmixer_pspd->enable && prtd && prtd->audio_client) { stream_id = prtd->audio_client->session; be_id = chmixer_pspd->port_idx; +#ifdef CONFIG_PLATFORM_AUTO + msm_pcm_routing_set_channel_mixer_runtime(fe_id, be_id, + stream_id, + session_type, + chmixer_pspd); +#else msm_pcm_routing_set_channel_mixer_runtime(be_id, stream_id, session_type, chmixer_pspd); +#endif } if (reset_override_out_ch_map) diff --git a/techpack/audio/asoc/msm-pcm-routing-auto.c b/techpack/audio/asoc/msm-pcm-routing-auto.c index 14114c820b29..93340557082a 100644 --- a/techpack/audio/asoc/msm-pcm-routing-auto.c +++ b/techpack/audio/asoc/msm-pcm-routing-auto.c @@ -2361,6 +2361,7 @@ static int msm_pcm_routing_channel_mixer(int fe_id, bool perf_mode, * msm_pcm_routing_set_channel_mixer_runtime - apply channel mixer * setting during runtime. * + * @fe_id: frontend index * @be_id: backend index * @session_id: session index * @session_type: session type @@ -2368,12 +2369,13 @@ static int msm_pcm_routing_channel_mixer(int fe_id, bool perf_mode, * * Retuen: 0 for success, else error */ -int msm_pcm_routing_set_channel_mixer_runtime(int be_id, int session_id, +int msm_pcm_routing_set_channel_mixer_runtime(int fe_id, int be_id, int session_id, int session_type, struct msm_pcm_channel_mixer *params) { int rc = 0; int port_id, copp_idx = 0; + bool tmp = false; be_id--; if (be_id < 0 || be_id >= MSM_BACKEND_DAI_MAX) { @@ -2382,8 +2384,13 @@ int msm_pcm_routing_set_channel_mixer_runtime(int be_id, int session_id, return -EINVAL; } - port_id = msm_bedais[be_id].port_id; - copp_idx = adm_get_default_copp_idx(port_id); + tmp = msm_pcm_routing_get_portid_copp_idx(fe_id, session_type, &port_id, &copp_idx); + + if(!tmp){ + pr_err("%s: Could not find copp_idx for fe_id: %d, will use default copp_idx\n", + __func__, fe_id); + copp_idx = adm_get_default_copp_idx(port_id); + } pr_debug("%s: port_id - %d, copp_idx %d session id - %d\n", __func__, port_id, copp_idx, session_id); diff --git a/techpack/audio/asoc/msm-pcm-routing-v2.c b/techpack/audio/asoc/msm-pcm-routing-v2.c index 4f9843a66d8c..0e05f6f1545f 100644 --- a/techpack/audio/asoc/msm-pcm-routing-v2.c +++ b/techpack/audio/asoc/msm-pcm-routing-v2.c @@ -136,6 +136,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 int32_t mclk_cfg_be_idx; +static int32_t mclk_cfg_src_id; +static uint32_t mclk_cfg_freq; + #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]; @@ -30766,6 +30770,10 @@ static const struct snd_kcontrol_new wsa_cdc_dma_rx_0_port_mixer_controls[] = { MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_7_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, + MSM_BACKEND_DAI_SLIMBUS_7_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM, MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer, @@ -36837,6 +36845,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"SLIM1_UL_HL", NULL, "SLIMBUS_1_TX"}, {"SLIM3_UL_HL", NULL, "SLIMBUS_3_TX"}, {"SLIM4_UL_HL", NULL, "SLIMBUS_4_TX"}, + {"SLIM7_UL_HL", NULL, "SLIMBUS_7_TX"}, {"SLIM8_UL_HL", NULL, "SLIMBUS_8_TX"}, {"WSA_CDC_DMA_RX_0_DL_HL", "Switch", "CDC_DMA_DL_HL"}, {"WSA_CDC_DMA_RX_0", NULL, "WSA_CDC_DMA_RX_0_DL_HL"}, @@ -36962,6 +36971,7 @@ static const struct snd_soc_dapm_route intercon[] = { /* connect to INT4_MI2S_DL_HL since same pcm_id */ {"WSA_CDC_DMA_RX_0 Port Mixer", "VA_CDC_DMA_TX_0", "VA_CDC_DMA_TX_0"}, {"WSA_CDC_DMA_RX_0 Port Mixer", "TX_CDC_DMA_TX_3", "TX_CDC_DMA_TX_3"}, + {"WSA_CDC_DMA_RX_0 Port Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"}, {"WSA_CDC_DMA_RX_0 Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, {"WSA_CDC_DMA_RX_0", NULL, "WSA_CDC_DMA_RX_0 Port Mixer"}, @@ -42813,6 +42823,16 @@ static const struct snd_kcontrol_new }, }; +static int msm_routing_get_mclk_src_cfg(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = mclk_cfg_be_idx; + ucontrol->value.integer.value[1] = mclk_cfg_src_id; + ucontrol->value.integer.value[2] = mclk_cfg_freq; + + return 0; +} + static int msm_routing_put_mclk_src_cfg(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -42825,6 +42845,10 @@ static int msm_routing_put_mclk_src_cfg(struct snd_kcontrol *kcontrol, mclk_src_id = ucontrol->value.integer.value[1]; mclk_freq = ucontrol->value.integer.value[2]; + mclk_cfg_be_idx = be_idx; + mclk_cfg_src_id = mclk_src_id; + mclk_cfg_freq = mclk_freq; + if (be_idx < 0 || be_idx >= MSM_BACKEND_DAI_MAX) { pr_err("%s: Invalid be id %d\n", __func__, be_idx); return -EINVAL; @@ -42854,7 +42878,8 @@ static int msm_routing_put_mclk_src_cfg(struct snd_kcontrol *kcontrol, static const struct snd_kcontrol_new mclk_src_controls[] = { SOC_SINGLE_MULTI_EXT("MCLK_SRC CFG", SND_SOC_NOPM, 0, 24576000, 0, 3, - NULL, msm_routing_put_mclk_src_cfg), + msm_routing_get_mclk_src_cfg, + msm_routing_put_mclk_src_cfg), }; static int msm_routing_stereo_channel_reverse_control_get( diff --git a/techpack/audio/asoc/msm-pcm-routing-v2.h b/techpack/audio/asoc/msm-pcm-routing-v2.h index d57851b7bec4..6db54df75cd6 100644 --- a/techpack/audio/asoc/msm-pcm-routing-v2.h +++ b/techpack/audio/asoc/msm-pcm-routing-v2.h @@ -1,6 +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. + * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ /* * Add support for 24 and 32bit format for ASM loopback and playback session. @@ -818,10 +818,17 @@ int msm_pcm_routing_set_channel_mixer_cfg( int fe_id, int session_type, struct msm_pcm_channel_mixer *params); +#ifdef CONFIG_PLATFORM_AUTO int msm_pcm_routing_set_channel_mixer_runtime( - int be_id, int session_id, + int fe_id, int be_id, int session_id, int session_type, struct msm_pcm_channel_mixer *params); +#else +int msm_pcm_routing_set_channel_mixer_runtime( + int be_id, int session_id, + int session_type, + struct msm_pcm_channel_mixer *params); +#endif int msm_pcm_routing_set_stream_ec_ref_chmix_cfg( int fedai_id, struct msm_pcm_channel_mixer *cfg_data); diff --git a/techpack/audio/dsp/q6adm.c b/techpack/audio/dsp/q6adm.c index 32a5b29d3a1c..7455252e2f08 100644 --- a/techpack/audio/dsp/q6adm.c +++ b/techpack/audio/dsp/q6adm.c @@ -1713,16 +1713,11 @@ static int32_t adm_callback(struct apr_client_data *data, void *priv) if (data->opcode == APR_BASIC_RSP_RESULT) { pr_debug("%s: APR_BASIC_RSP_RESULT id 0x%x\n", __func__, payload[0]); - - if (!((client_id != ADM_CLIENT_ID_SOURCE_TRACKING) && - ((payload[0] == ADM_CMD_SET_PP_PARAMS_V5) || - (payload[0] == ADM_CMD_SET_PP_PARAMS_V6)))) { - if (data->payload_size < - (2 * sizeof(uint32_t))) { - pr_err("%s: Invalid payload size %d\n", - __func__, data->payload_size); - return 0; - } + if (data->payload_size < + (2 * sizeof(uint32_t))) { + pr_err("%s: Invalid payload size %d\n", + __func__, data->payload_size); + return 0; } if (payload[1] != 0) { diff --git a/techpack/audio/dsp/q6afe.c b/techpack/audio/dsp/q6afe.c index 3ab1499c8e32..d5c16bfc5829 100644 --- a/techpack/audio/dsp/q6afe.c +++ b/techpack/audio/dsp/q6afe.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2024, Qualcomm Innovation Center, Inc. All rights reserved. */ #include #include @@ -785,8 +785,14 @@ static int32_t sp_make_afe_callback(uint32_t opcode, uint32_t *payload, switch (param_hdr.param_id) { case AFE_PARAM_ID_CALIB_RES_CFG_V2: + num_ch = data_start[0]; expected_size += sizeof(struct asm_calib_res_cfg); - if (param_hdr.param_size != sizeof(struct asm_calib_res_cfg)) { + if (num_ch > SP_V2_NUM_MAX_SPKRS) { + pr_err("%s: Error: num_ch %d is greater than expected\n", + __func__,num_ch); + return -EINVAL; + } + if (param_hdr.param_size != (sizeof(struct asm_calib_res_cfg) - ((2 - num_ch) * 4))) { pr_err("%s: Error: param_size %d is greater than expected\n", __func__,param_hdr.param_size); return -EINVAL; @@ -794,8 +800,14 @@ static int32_t sp_make_afe_callback(uint32_t opcode, uint32_t *payload, data_dest = (u32 *) &this_afe.calib_data; break; case AFE_PARAM_ID_SP_V2_TH_VI_FTM_PARAMS: + num_ch = data_start[0]; expected_size += sizeof(struct afe_sp_th_vi_ftm_params); - if (param_hdr.param_size != sizeof(struct afe_sp_th_vi_ftm_params)) { + if (num_ch > SP_V2_NUM_MAX_SPKRS) { + pr_err("%s: Error: num_ch %d is greater than expected\n", + __func__,num_ch); + return -EINVAL; + } + if (param_hdr.param_size != (sizeof(struct afe_sp_th_vi_ftm_params) - ((2 - num_ch) * 4 * 3))) { pr_err("%s: Error: param_size %d is greater than expected\n", __func__,param_hdr.param_size); return -EINVAL; @@ -803,8 +815,14 @@ static int32_t sp_make_afe_callback(uint32_t opcode, uint32_t *payload, data_dest = (u32 *) &this_afe.th_vi_resp; break; case AFE_PARAM_ID_SP_V2_TH_VI_V_VALI_PARAMS: + num_ch = data_start[0]; expected_size += sizeof(struct afe_sp_th_vi_v_vali_params); - if (param_hdr.param_size != sizeof(struct afe_sp_th_vi_v_vali_params)) { + if (num_ch > SP_V2_NUM_MAX_SPKRS) { + pr_err("%s: Error: num_ch %d is greater than expected\n", + __func__,num_ch); + return -EINVAL; + } + if (param_hdr.param_size != (sizeof(struct afe_sp_th_vi_v_vali_params) - ((2 - num_ch) * 4 * 2))) { pr_err("%s: Error: param_size %d is greater than expected\n", __func__,param_hdr.param_size); return -EINVAL; @@ -812,8 +830,14 @@ static int32_t sp_make_afe_callback(uint32_t opcode, uint32_t *payload, data_dest = (u32 *) &this_afe.th_vi_v_vali_resp; break; case AFE_PARAM_ID_SP_V2_EX_VI_FTM_PARAMS: + num_ch = data_start[0]; expected_size += sizeof(struct afe_sp_ex_vi_ftm_params); - if (param_hdr.param_size != sizeof(struct afe_sp_ex_vi_ftm_params)) { + if (num_ch > SP_V2_NUM_MAX_SPKRS) { + pr_err("%s: Error: num_ch %d is greater than expected\n", + __func__,num_ch); + return -EINVAL; + } + if (param_hdr.param_size != (sizeof(struct afe_sp_ex_vi_ftm_params) - ((2 - num_ch) * 4 * 4))) { pr_err("%s: Error: param_size %d is greater than expected\n", __func__,param_hdr.param_size); return -EINVAL; @@ -821,9 +845,15 @@ static int32_t sp_make_afe_callback(uint32_t opcode, uint32_t *payload, data_dest = (u32 *) &this_afe.ex_vi_resp; break; case AFE_PARAM_ID_SP_RX_TMAX_XMAX_LOGGING: + num_ch = data_start[0]; expected_size += sizeof( struct afe_sp_rx_tmax_xmax_logging_param); - if (param_hdr.param_size != sizeof(struct afe_sp_rx_tmax_xmax_logging_param)) { + if (num_ch > SP_V2_NUM_MAX_SPKRS) { + pr_err("%s: Error: num_ch %d is greater than expected\n", + __func__,num_ch); + return -EINVAL; + } + if (param_hdr.param_size != (num_ch * sizeof(struct afe_sp_rx_tmax_xmax_logging_param))) { pr_err("%s: Error: param_size %d is greater than expected\n", __func__,param_hdr.param_size); return -EINVAL; @@ -831,10 +861,16 @@ static int32_t sp_make_afe_callback(uint32_t opcode, uint32_t *payload, data_dest = (u32 *) &this_afe.xt_logging_resp; break; case AFE_PARAM_ID_SP_V4_CALIB_RES_CFG: + num_ch = data_start[0]; expected_size += sizeof( struct afe_sp_v4_param_th_vi_calib_res_cfg); - if (param_hdr.param_size != sizeof( - struct afe_sp_v4_param_th_vi_calib_res_cfg)) { + if (num_ch > SP_V2_NUM_MAX_SPKRS) { + pr_err("%s: Error: num_ch %d is greater than expected\n", + __func__,num_ch); + return -EINVAL; + } + if (param_hdr.param_size != (sizeof( + struct afe_sp_v4_param_th_vi_calib_res_cfg) - (2 - num_ch) * 4)) { pr_err("%s: Error: param_size %d is greater than expected\n", __func__,param_hdr.param_size); return -EINVAL; @@ -9858,8 +9894,8 @@ int afe_set_pll_clk_drift(u16 port_id, int32_t set_clk_drift, } EXPORT_SYMBOL(afe_set_pll_clk_drift); -static int afe_set_lpass_clk_cfg_ext_mclk(int index, struct afe_clk_set *cfg, - uint32_t mclk_freq) +int afe_set_lpass_clk_cfg_ext_mclk(int index, struct afe_clk_set *cfg, + uint32_t mclk_freq) { struct param_hdr_v3 param_hdr; struct afe_param_id_clock_set_v2_t dyn_mclk_cfg; @@ -9871,6 +9907,12 @@ static int afe_set_lpass_clk_cfg_ext_mclk(int index, struct afe_clk_set *cfg, return ret; } + if (!afe_ext_mclk.ext_mclk_cb) { + pr_debug("%s: ext_mclk_cb not registered; platform doesn't support ext clk\n", + __func__); + return -EOPNOTSUPP; + } + if (index < 0 || index >= AFE_MAX_PORTS) { pr_err("%s: index[%d] invalid!\n", __func__, index); return -EINVAL; @@ -9883,34 +9925,33 @@ static int afe_set_lpass_clk_cfg_ext_mclk(int index, struct afe_clk_set *cfg, param_hdr.param_size = sizeof(struct afe_param_id_clock_set_v2_t); memset(&dyn_mclk_cfg, 0, sizeof(dyn_mclk_cfg)); - dyn_mclk_cfg.clk_freq_in_hz = cfg->clk_freq_in_hz; - if (afe_ext_mclk.ext_mclk_cb) { - ret = afe_ext_mclk.ext_mclk_cb(afe_ext_mclk.private_data, - 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_freq_in_hz = cfg->clk_freq_in_hz; dyn_mclk_cfg.clk_set_minor_version = 1; 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); + 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); + dyn_mclk_cfg.clk_freq_in_hz, dyn_mclk_cfg.clk_attri); + pr_debug("%s: clk input freq (Hz) = %d clk enable = 0x%x\n", __func__, + mclk_freq, dyn_mclk_cfg.enable); + + if (dyn_mclk_cfg.enable) { + /* callback to populate Div2x, M, N, D, clock root and enable GPIOs */ + 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: mclk cb during enable failed %d\n", + __func__, ret); + return ret; + } + + pr_debug("%s: clk root = 0x%x divider_2x = %d m = %d n = %d d = %d\n", + __func__, dyn_mclk_cfg.clk_root, 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) { @@ -9922,23 +9963,108 @@ static int afe_set_lpass_clk_cfg_ext_mclk(int index, struct afe_clk_set *cfg, 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) + if (ret < 0) { pr_err_ratelimited("%s: ext MCLK clk cfg failed with ret %d\n", - __func__, ret); + __func__, ret); + mutex_unlock(&this_afe.afe_cmd_lock); + goto stop_mclk; + } mutex_unlock(&this_afe.afe_cmd_lock); + if (!dyn_mclk_cfg.enable) { + /* callback to disable GPIOs */ + 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: mclk cb during disable failed %d\n", + __func__, ret); + return ret; + } + } + if (ret >= 0) return ret; stop_mclk: - if (afe_ext_mclk.ext_mclk_cb && cfg->enable) { - afe_ext_mclk.ext_mclk_cb(afe_ext_mclk.private_data, - cfg->enable, mclk_freq, &dyn_mclk_cfg); - } + dyn_mclk_cfg.enable = 0; + 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); + + +int afe_set_lpass_ext_mclk_mux_cfg(const char *mux_str, + uint32_t mux_val) +{ + struct param_hdr_v3 param_hdr; + struct afe_param_id_clock_mux_cfg_t clk_mux_cfg; + uint32_t build_major_version = 0; + uint32_t build_minor_version = 0; + uint32_t build_branch_version = 0; + int afe_api_version = 0; + int ret = 0; + + if (!mux_str) { + pr_err("%s: mux_str is NULL\n", __func__); + ret = -EINVAL; + return ret; + } + + ret = q6core_get_avcs_avs_build_version_info( + &build_major_version, &build_minor_version, + &build_branch_version); + if (ret < 0) + return ret; + + ret = q6core_get_avcs_api_version_per_service( + APRV2_IDS_SERVICE_ID_ADSP_AFE_V); + if (ret < 0) + return ret; + + afe_api_version = ret; + pr_debug("%s: mjor: %u, mnor: %u, brnch: %u, afe_api: %u\n", + __func__, build_major_version, build_minor_version, + build_branch_version, afe_api_version); + if ((build_major_version != AVS_BUILD_MAJOR_VERSION_V2) || + (build_minor_version != AVS_BUILD_MINOR_VERSION_V9) || + (build_branch_version != AVS_BUILD_BRANCH_VERSION_V0) || + (afe_api_version < AFE_API_VERSION_V11)) { + pr_err("%s: AFE_PARAM_ID_CLOCK_MUX_CFG unsupported by AVS\n", __func__); + 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_MUX_CFG; + param_hdr.param_size = sizeof(struct afe_param_id_clock_mux_cfg_t); + memset(&clk_mux_cfg, 0, sizeof(clk_mux_cfg)); + strlcpy(clk_mux_cfg.mux_string, mux_str, sizeof(clk_mux_cfg.mux_string)); + clk_mux_cfg.mux_value = mux_val; + pr_debug("%s: ext mclk mux cfg - mux_string: %s, mux_value = %u\n", __func__, + clk_mux_cfg.mux_string, clk_mux_cfg.mux_value); + + ret = afe_q6_interface_prepare(); + if (ret != 0) { + pr_err_ratelimited("%s: Q6 interface prepare failed %d\n", + __func__, ret); + return ret; + } + + mutex_lock(&this_afe.afe_cmd_lock); + ret = q6afe_svc_pack_and_set_param_in_band(IDX_RSVD_3, param_hdr, + (u8 *) &clk_mux_cfg); + if (ret < 0) { + pr_err_ratelimited("%s: ext mclk mux cfg failed with ret %d\n", + __func__, ret); + } + mutex_unlock(&this_afe.afe_cmd_lock); + return ret; +} +EXPORT_SYMBOL(afe_set_lpass_ext_mclk_mux_cfg); static int ext_dyn_mclk_port_id; static int ext_dyn_clk_root = Q6AFE_LPASS_CLK_ROOT_DEFAULT; @@ -10162,7 +10288,8 @@ int afe_set_lpass_clock_v2(u16 port_id, struct afe_clk_set *cfg) build_branch_version, afe_api_version); if ((build_major_version != AVS_BUILD_MAJOR_VERSION_V2) || (build_minor_version != AVS_BUILD_MINOR_VERSION_V9) || - (build_branch_version != AVS_BUILD_BRANCH_VERSION_V3) || + ((build_branch_version != AVS_BUILD_BRANCH_VERSION_V0) && + (build_branch_version != AVS_BUILD_BRANCH_VERSION_V3)) || (afe_api_version < AFE_API_VERSION_V8)) { pr_err("%s: ext mclk not supported by AVS\n", __func__); return -EINVAL; diff --git a/techpack/audio/dsp/q6asm.c b/techpack/audio/dsp/q6asm.c index 5ba45f3c775d..3016cf5ff05b 100644 --- a/techpack/audio/dsp/q6asm.c +++ b/techpack/audio/dsp/q6asm.c @@ -8843,6 +8843,8 @@ static int q6asm_memory_map_regions(struct audio_client *ac, int dir, } mmap_regions = (struct avs_cmd_shared_mem_map_regions *) mmap_region_cmd; + + mutex_lock(&ac->cmd_lock); q6asm_add_mmaphdr(ac, &mmap_regions->hdr, cmd_size, dir); atomic_set(&ac->mem_state, -1); pr_debug("%s: mmap_region=0x%pK token=0x%x\n", __func__, @@ -8900,7 +8902,6 @@ static int q6asm_memory_map_regions(struct audio_client *ac, int dir, buffer_node = NULL; goto fail_cmd; } - mutex_lock(&ac->cmd_lock); for (i = 0; i < bufcnt; i++) { ab = &port->buf[i]; @@ -8913,9 +8914,9 @@ static int q6asm_memory_map_regions(struct audio_client *ac, int dir, buffer_node[i].mmap_hdl); } ac->port[dir].tmp_hdl = 0; - mutex_unlock(&ac->cmd_lock); rc = 0; fail_cmd: + mutex_unlock(&ac->cmd_lock); kfree(mmap_region_cmd); mmap_region_cmd = NULL; return rc; diff --git a/techpack/audio/dsp/q6core.c b/techpack/audio/dsp/q6core.c index df72815babc9..1df46209377f 100644 --- a/techpack/audio/dsp/q6core.c +++ b/techpack/audio/dsp/q6core.c @@ -1506,8 +1506,10 @@ int q6core_map_mdf_memory_regions(uint64_t *buf_add, uint32_t mempool_id, * bufcnt; mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL); - if (mmap_region_cmd == NULL) + if (mmap_region_cmd == NULL) { + mutex_unlock(&q6core_lcl.cmd_lock); return -ENOMEM; + } mmap_regions = (struct avs_cmd_shared_mem_map_regions *)mmap_region_cmd; mmap_regions->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, diff --git a/techpack/audio/dsp/q6lsm.c b/techpack/audio/dsp/q6lsm.c index e1aad637d8be..175c8fcb1a7e 100644 --- a/techpack/audio/dsp/q6lsm.c +++ b/techpack/audio/dsp/q6lsm.c @@ -1,7 +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. + * Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include #include @@ -2130,6 +2130,12 @@ static int q6lsm_mmapcallback(struct apr_client_data *data, void *priv) return 0; } + if (data->payload_size < (2 * sizeof(uint32_t))) { + pr_err("%s: payload has invalid size[%d]\n", __func__, + data->payload_size); + return -EINVAL; + } + command = payload[0]; retcode = payload[1]; sid = (data->token >> 8) & 0x0F; @@ -2454,6 +2460,12 @@ int q6lsm_set_one_param(struct lsm_client *client, sizeof(struct param_hdr_v2); if (param_type == LSM_REG_MULTI_SND_MODEL) { + if(list_empty(&client->stage_cfg[p_info->stage_idx].sound_models)) { + pr_err("%s: sound_models list is empty \n", + __func__); + return -EINVAL; + } + list_for_each_entry(sm, &client->stage_cfg[p_info->stage_idx].sound_models, list) { diff --git a/techpack/audio/dsp/q6voice.c b/techpack/audio/dsp/q6voice.c index f5115d225655..769fb9ff41a3 100644 --- a/techpack/audio/dsp/q6voice.c +++ b/techpack/audio/dsp/q6voice.c @@ -6898,6 +6898,7 @@ int voc_set_device_config(uint32_t session_id, uint8_t path_dir, struct media_format_info *finfo) { struct voice_data *v = voice_get_session(session_id); + int ret = 0; if (v == NULL) { pr_err("%s: Invalid session_id 0x%x\n", __func__, session_id); @@ -6929,12 +6930,12 @@ int voc_set_device_config(uint32_t session_id, uint8_t path_dir, break; default: pr_err("%s: Invalid path_dir %d\n", __func__, path_dir); - return -EINVAL; + ret = -EINVAL; } mutex_unlock(&v->lock); - return 0; + return ret; } EXPORT_SYMBOL(voc_set_device_config); diff --git a/techpack/audio/include/dsp/apr_audio-v2.h b/techpack/audio/include/dsp/apr_audio-v2.h index ee0bfd5f2f23..79e258589e1d 100644 --- a/techpack/audio/include/dsp/apr_audio-v2.h +++ b/techpack/audio/include/dsp/apr_audio-v2.h @@ -12549,6 +12549,17 @@ struct afe_param_id_clock_set_v2_t { uint32_t d; }; +#define AFE_PARAM_ID_CLOCK_MUX_CFG 0x000102fd +struct afe_param_id_clock_mux_cfg_t { + char mux_string[128]; + /* Name of the Mux string. + * String name may varies for each target, so HLOS must pass the proper string based on IPCAT. + * @values Valid string with a maximum of 128 characters + */ + uint32_t mux_value; + /* Value of the external m-clock. */ +}; + struct afe_clk_cfg { /* Minor version used for tracking the version of the I2S * configuration interface. diff --git a/techpack/audio/include/dsp/q6afe-v2.h b/techpack/audio/include/dsp/q6afe-v2.h index 900fe1699528..13a534a40158 100644 --- a/techpack/audio/include/dsp/q6afe-v2.h +++ b/techpack/audio/include/dsp/q6afe-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. */ #ifndef __Q6AFE_V2_H__ #define __Q6AFE_V2_H__ @@ -55,6 +55,8 @@ /* for external mclk dynamic switch */ #define AFE_API_VERSION_V8 8 #define AFE_API_VERSION_V10 10 +/* for external mclk selection through mux */ +#define AFE_API_VERSION_V11 11 #define AFE_SAMPLING_RATE_8KHZ 8000 @@ -419,6 +421,7 @@ enum afe_mclk_freq { MCLK_FREQ_11P2896_MHZ = MCLK_FREQ_MIN, MCLK_FREQ_12P288_MHZ, MCLK_FREQ_16P384_MHZ, + MCLK_FREQ_19P200_MHZ, MCLK_FREQ_22P5792_MHZ, MCLK_FREQ_24P576_MHZ, MCLK_FREQ_MAX, @@ -603,8 +606,11 @@ int afe_port_send_afe_limiter_param(u16 port_id, struct afe_param_id_port_afe_limiter_disable_t *disable_limiter); 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(int index, struct afe_clk_set *cfg, + uint32_t mclk_freq); 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_set_lpass_ext_mclk_mux_cfg(const char *mux_str, uint32_t mux_val); int afe_get_sp_rx_tmax_xmax_logging_data( struct afe_sp_rx_tmax_xmax_logging_param *xt_logging, u16 port_id); @@ -634,6 +640,14 @@ int afe_set_source_clk(u16 port_id, const char *clk_src); void afe_set_clk_src_array(const char *clk_src[CLK_SRC_MAX]); int afe_set_mclk_src_cfg(u16 port_id, uint32_t mclk_src_id, uint32_t mclk_freq); +/* Client of AFE registers afe_enable_mclk_and_get_info_cb_func callback function via + * afe_register_ext_mclk_cb() when external clock is supported by the platform. During + * clock enable sequence, AFE triggers this callback before requesting DSP for clock + * enable. Client can provide Div2X, M, N, D and clock root as part of this callback. + * Client can also enable required GPIO that are essential to route external clock in + * this callback. During clock disable sequence, AFE triggers this callback after + * requesting DSP for clock disable. Client can disable required GPIOs in this callback. + */ typedef int (*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); diff --git a/techpack/audio/include/soc/soundwire.h b/techpack/audio/include/soc/soundwire.h index a8c5ec83e9e2..dffb4db05491 100644 --- a/techpack/audio/include/soc/soundwire.h +++ b/techpack/audio/include/soc/soundwire.h @@ -18,6 +18,12 @@ #define SWR_CLK_RATE_4P8MHZ 4800000 #define SWR_CLK_RATE_9P6MHZ 9600000 #define SWR_CLK_RATE_11P2896MHZ 11289600 +#define SWR_CLK_RATE_0P384MHZ 384000 +#define SWR_CLK_RATE_0P768MHZ 768000 +#define SWR_CLK_RATE_1P536MHZ 1536000 +#define SWR_CLK_RATE_3P072MHZ 3072000 +#define SWR_CLK_RATE_6P144MHZ 6144000 +#define SWR_CLK_RATE_12P288MHZ 12288000 extern struct bus_type soundwire_type; struct swr_device; diff --git a/techpack/audio/include/soc/swr-wcd.h b/techpack/audio/include/soc/swr-wcd.h index 8ed9f0a05e3b..140d4eecd81d 100644 --- a/techpack/audio/include/soc/swr-wcd.h +++ b/techpack/audio/include/soc/swr-wcd.h @@ -34,6 +34,7 @@ struct swr_mstr_port { #define MCLK_FREQ 9600000 #define MCLK_FREQ_LP 600000 #define MCLK_FREQ_NATIVE 11289600 +#define MCLK_FREQ_12P288MHZ 12288000 #if (IS_ENABLED(CONFIG_SOUNDWIRE_WCD_CTRL) || \ IS_ENABLED(CONFIG_SOUNDWIRE_MSTR_CTRL)) diff --git a/techpack/audio/soc/pinctrl-lpi.c b/techpack/audio/soc/pinctrl-lpi.c index 4a383a65d4f4..8b0781d6071f 100644 --- a/techpack/audio/soc/pinctrl-lpi.c +++ b/techpack/audio/soc/pinctrl-lpi.c @@ -26,9 +26,6 @@ #include "core.h" #include "pinctrl-utils.h" -#define LPI_AUTO_SUSPEND_DELAY 100 /* delay in msec */ -#define LPI_AUTO_SUSPEND_DELAY_ERROR 1 /* delay in msec */ - #define LPI_ADDRESS_SIZE 0x20000 #define LPI_SLEW_ADDRESS_SIZE 0x1000 @@ -118,8 +115,6 @@ struct lpi_gpio_state { struct clk *lpass_core_hw_vote; struct clk *lpass_audio_hw_vote; struct mutex slew_access_lock; - bool core_hw_vote_status; - struct mutex core_hw_vote_lock; }; static const char *const lpi_gpio_groups[] = { @@ -144,12 +139,54 @@ static const char *const lpi_gpio_functions[] = { [LPI_GPIO_FUNC_INDEX_FUNC5] = LPI_GPIO_FUNC_FUNC5, }; -int lpi_pinctrl_runtime_suspend(struct device *dev); +int lpi_pinctrl_hw_vote(struct device *dev) +{ + int ret = 0; + struct lpi_gpio_state *state = dev_get_drvdata(dev); + struct clk *hw_vote = state->lpass_core_hw_vote; + if (state->lpass_core_hw_vote == NULL) { + dev_dbg(dev, "%s: Invalid core hw node\n", __func__); + if (state->lpass_audio_hw_vote == NULL) { + dev_dbg(dev, "%s: Invalid audio hw node\n", __func__); + return -EINVAL; + } + hw_vote = state->lpass_audio_hw_vote; + } + + dev_dbg(dev, "%s: lpi_pinctrl_hw_vote\n", __func__); + ret = digital_cdc_rsc_mgr_hw_vote_enable(hw_vote); + if (ret < 0) { + dev_err(dev, "%s:lpass core hw island enable failed\n", + __func__); + } + + dev_dbg(dev, "%s: lpi_pinctrl_hw_vote done\n", __func__); + return ret; +} + +int lpi_pinctrl_hw_unvote(struct device *dev) +{ + struct lpi_gpio_state *state = dev_get_drvdata(dev); + struct clk *hw_vote = state->lpass_core_hw_vote; + + if (state->lpass_core_hw_vote == NULL) { + dev_dbg(dev, "%s: Invalid core hw node\n", __func__); + if (state->lpass_audio_hw_vote == NULL) { + dev_dbg(dev, "%s: Invalid audio hw node\n", __func__); + return -EINVAL; + } + hw_vote = state->lpass_audio_hw_vote; + } + + dev_dbg(dev, "%s: lpi_pinctrl_hw_unvote\n", __func__); + digital_cdc_rsc_mgr_hw_vote_disable(hw_vote); + dev_dbg(dev, "%s: lpi_pinctrl_hw_unvote done\n", __func__); + return 0; +} static int lpi_gpio_read(struct lpi_gpio_pad *pad, unsigned int addr) { int ret = 0; - struct lpi_gpio_state *state = dev_get_drvdata(lpi_dev); static DEFINE_RATELIMIT_STATE(rtl, 1 * HZ, 1); if (!lpi_dev_up) { @@ -158,52 +195,38 @@ static int lpi_gpio_read(struct lpi_gpio_pad *pad, unsigned int addr) __func__); return 0; } - pm_runtime_get_sync(lpi_dev); - mutex_lock(&state->core_hw_vote_lock); - if (!state->core_hw_vote_status) { - if (__ratelimit(&rtl)) - pr_err("%s: core hw vote clk is not enabled\n", - __func__); - ret = -EINVAL; + + ret = lpi_pinctrl_hw_vote(lpi_dev); + if (ret < 0) goto err; - } ret = ioread32(pad->base + pad->offset + addr); if (ret < 0) pr_err("%s: read 0x%x failed\n", __func__, addr); + lpi_pinctrl_hw_unvote(lpi_dev); + err: - mutex_unlock(&state->core_hw_vote_lock); - pm_runtime_mark_last_busy(lpi_dev); - pm_runtime_put_autosuspend(lpi_dev); return ret; } static int lpi_gpio_write(struct lpi_gpio_pad *pad, unsigned int addr, unsigned int val) { - struct lpi_gpio_state *state = dev_get_drvdata(lpi_dev); int ret = 0; - static DEFINE_RATELIMIT_STATE(rtl, 1 * HZ, 1); if (!lpi_dev_up) { return 0; } - pm_runtime_get_sync(lpi_dev); - mutex_lock(&state->core_hw_vote_lock); - if (!state->core_hw_vote_status) { - if (__ratelimit(&rtl)) - pr_err("%s: core hw vote clk is not enabled\n", - __func__); - ret = -EINVAL; + + ret = lpi_pinctrl_hw_vote(lpi_dev); + if (ret < 0) goto err; - } iowrite32(val, pad->base + pad->offset + addr); + + lpi_pinctrl_hw_unvote(lpi_dev); err: - mutex_unlock(&state->core_hw_vote_lock); - pm_runtime_mark_last_busy(lpi_dev); - pm_runtime_put_autosuspend(lpi_dev); return ret; } @@ -516,39 +539,6 @@ static int lpi_notifier_service_cb(struct notifier_block *this, return NOTIFY_OK; } -int lpi_pinctrl_suspend(struct device *dev) -{ - int ret = 0; - - dev_dbg(dev, "%s: system suspend\n", __func__); - - if ((!pm_runtime_enabled(dev) || !pm_runtime_suspended(dev))) { - ret = lpi_pinctrl_runtime_suspend(dev); - if (!ret) { - /* - * Synchronize runtime-pm and system-pm states: - * At this point, we are already suspended. If - * runtime-pm still thinks its active, then - * make sure its status is in sync with HW - * status. The three below calls let the - * runtime-pm know that we are suspended - * already without re-invoking the suspend - * callback - */ - pm_runtime_disable(dev); - pm_runtime_set_suspended(dev); - pm_runtime_enable(dev); - } - } - - return ret; -} - -int lpi_pinctrl_resume(struct device *dev) -{ - return 0; -} - static struct notifier_block service_nb = { .notifier_call = lpi_notifier_service_cb, .priority = -INT_MAX, @@ -557,7 +547,6 @@ static struct notifier_block service_nb = { static void lpi_pinctrl_ssr_disable(struct device *dev, void *data) { lpi_dev_up = false; - lpi_pinctrl_suspend(dev); } static const struct snd_event_ops lpi_pinctrl_ssr_ops = { @@ -773,7 +762,6 @@ static int lpi_pinctrl_probe(struct platform_device *pdev) state->chip.can_sleep = false; mutex_init(&state->slew_access_lock); - mutex_init(&state->core_hw_vote_lock); state->ctrl = devm_pinctrl_register(dev, pctrldesc, state); if (IS_ERR(state->ctrl)) @@ -832,12 +820,6 @@ static int lpi_pinctrl_probe(struct platform_device *pdev) } state->lpass_audio_hw_vote = lpass_audio_hw_vote; - state->core_hw_vote_status = false; - pm_runtime_set_autosuspend_delay(&pdev->dev, LPI_AUTO_SUSPEND_DELAY); - pm_runtime_use_autosuspend(&pdev->dev); - pm_runtime_set_suspended(&pdev->dev); - pm_runtime_enable(&pdev->dev); - return 0; err_snd_evt: @@ -845,7 +827,6 @@ err_snd_evt: err_range: gpiochip_remove(&state->chip); err_chip: - mutex_destroy(&state->core_hw_vote_lock); mutex_destroy(&state->slew_access_lock); err_io: return ret; @@ -855,13 +836,9 @@ static int lpi_pinctrl_remove(struct platform_device *pdev) { struct lpi_gpio_state *state = platform_get_drvdata(pdev); - pm_runtime_disable(&pdev->dev); - pm_runtime_set_suspended(&pdev->dev); - snd_event_client_deregister(&pdev->dev); audio_notifier_deregister("lpi_tlmm"); gpiochip_remove(&state->chip); - mutex_destroy(&state->core_hw_vote_lock); mutex_destroy(&state->slew_access_lock); return 0; @@ -874,79 +851,9 @@ static const struct of_device_id lpi_pinctrl_of_match[] = { MODULE_DEVICE_TABLE(of, lpi_pinctrl_of_match); -int lpi_pinctrl_runtime_resume(struct device *dev) -{ - struct lpi_gpio_state *state = dev_get_drvdata(dev); - int ret = 0; - struct clk *hw_vote = state->lpass_core_hw_vote; - - if (state->lpass_core_hw_vote == NULL) { - dev_dbg(dev, "%s: Invalid core hw node\n", __func__); - if (state->lpass_audio_hw_vote == NULL) { - dev_dbg(dev, "%s: Invalid audio hw node\n", __func__); - return 0; - } - hw_vote = state->lpass_audio_hw_vote; - } - - mutex_lock(&state->core_hw_vote_lock); - ret = digital_cdc_rsc_mgr_hw_vote_enable(hw_vote); - if (ret < 0) { - pm_runtime_set_autosuspend_delay(dev, - LPI_AUTO_SUSPEND_DELAY_ERROR); - dev_err(dev, "%s:lpass core hw island enable failed\n", - __func__); - goto exit; - } else { - state->core_hw_vote_status = true; - } - - pm_runtime_set_autosuspend_delay(dev, LPI_AUTO_SUSPEND_DELAY); - -exit: - mutex_unlock(&state->core_hw_vote_lock); - return 0; -} - -int lpi_pinctrl_runtime_suspend(struct device *dev) -{ - struct lpi_gpio_state *state = dev_get_drvdata(dev); - struct clk *hw_vote = state->lpass_core_hw_vote; - - if (state->lpass_core_hw_vote == NULL) { - dev_dbg(dev, "%s: Invalid core hw node\n", __func__); - if (state->lpass_audio_hw_vote == NULL) { - dev_dbg(dev, "%s: Invalid audio hw node\n", __func__); - return 0; - } - hw_vote = state->lpass_audio_hw_vote; - } - - mutex_lock(&state->core_hw_vote_lock); - if (state->core_hw_vote_status) { - digital_cdc_rsc_mgr_hw_vote_disable(hw_vote); - state->core_hw_vote_status = false; - } - mutex_unlock(&state->core_hw_vote_lock); - return 0; -} - -static const struct dev_pm_ops lpi_pinctrl_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS( - lpi_pinctrl_suspend, - lpi_pinctrl_resume - ) - SET_RUNTIME_PM_OPS( - lpi_pinctrl_runtime_suspend, - lpi_pinctrl_runtime_resume, - NULL - ) -}; - static struct platform_driver lpi_pinctrl_driver = { .driver = { .name = "qcom-lpi-pinctrl", - .pm = &lpi_pinctrl_dev_pm_ops, .of_match_table = lpi_pinctrl_of_match, .suppress_bind_attrs = true, }, diff --git a/techpack/audio/soc/swr-mstr-ctrl.c b/techpack/audio/soc/swr-mstr-ctrl.c index 098f2fe1c36c..7622d18963b6 100644 --- a/techpack/audio/soc/swr-mstr-ctrl.c +++ b/techpack/audio/soc/swr-mstr-ctrl.c @@ -1160,8 +1160,8 @@ static void swrm_switch_frame_shape(struct swr_mstr_ctrl *swrm, int mclk_freq) } else { n_col = SWR_MIN_COL; col = SWRM_COL_02; - n_row = SWR_ROW_50; - row = SWRM_ROW_50; + n_row = (mclk_freq == MCLK_FREQ_12P288MHZ) ? SWR_ROW_64 : SWR_ROW_50; + row = (mclk_freq == MCLK_FREQ_12P288MHZ) ? SWRM_ROW_64 : SWRM_ROW_50; frame_sync = SWRM_FRAME_SYNC_SEL; } @@ -1222,16 +1222,25 @@ int swrm_get_clk_div_rate(int mclk_freq, int bus_clk_freq) if (mclk_freq == SWR_CLK_RATE_9P6MHZ) { if (bus_clk_freq <= SWR_CLK_RATE_0P6MHZ) bus_clk_freq = SWR_CLK_RATE_0P6MHZ; - else if (bus_clk_freq <= SWR_CLK_RATE_1P2MHZ) - bus_clk_freq = SWR_CLK_RATE_4P8MHZ; - else if (bus_clk_freq <= SWR_CLK_RATE_2P4MHZ) - bus_clk_freq = SWR_CLK_RATE_4P8MHZ; - else if(bus_clk_freq <= SWR_CLK_RATE_4P8MHZ) + else if ((bus_clk_freq <= SWR_CLK_RATE_1P2MHZ) || + (bus_clk_freq <= SWR_CLK_RATE_2P4MHZ) || + (bus_clk_freq <= SWR_CLK_RATE_4P8MHZ)) bus_clk_freq = SWR_CLK_RATE_4P8MHZ; else if(bus_clk_freq <= SWR_CLK_RATE_9P6MHZ) bus_clk_freq = SWR_CLK_RATE_9P6MHZ; else bus_clk_freq = SWR_CLK_RATE_9P6MHZ; + } else if (mclk_freq == SWR_CLK_RATE_12P288MHZ) { + if (bus_clk_freq <= SWR_CLK_RATE_0P768MHZ) + bus_clk_freq = SWR_CLK_RATE_0P768MHZ; + else if ((bus_clk_freq <= SWR_CLK_RATE_1P536MHZ) || + (bus_clk_freq <= SWR_CLK_RATE_3P072MHZ) || + (bus_clk_freq <= SWR_CLK_RATE_6P144MHZ)) + bus_clk_freq = SWR_CLK_RATE_6P144MHZ; + else if(bus_clk_freq <= SWR_CLK_RATE_12P288MHZ) + bus_clk_freq = SWR_CLK_RATE_12P288MHZ; + else + bus_clk_freq = SWR_CLK_RATE_12P288MHZ; } else if (mclk_freq == SWR_CLK_RATE_11P2896MHZ) bus_clk_freq = SWR_CLK_RATE_11P2896MHZ; @@ -1368,12 +1377,14 @@ static void swrm_get_device_frame_shape(struct swr_mstr_ctrl *swrm, if ((swrm->master_id == MASTER_ID_TX) && (swrm->use_custom_phy_addr) && - ((swrm->bus_clk == SWR_CLK_RATE_9P6MHZ) || + ((swrm->bus_clk == SWR_CLK_RATE_12P288MHZ) || + (swrm->bus_clk == SWR_CLK_RATE_9P6MHZ) || (swrm->bus_clk == SWR_CLK_RATE_0P6MHZ) || (swrm->bus_clk == SWR_CLK_RATE_4P8MHZ))) { dev_num = swrm_get_device_id(swrm, port_req->dev_num); port_id = port_req->slave_port_id; - if (swrm->bus_clk == SWR_CLK_RATE_9P6MHZ) + if ((swrm->bus_clk == SWR_CLK_RATE_12P288MHZ) || + (swrm->bus_clk == SWR_CLK_RATE_9P6MHZ)) pp_dev = swrdev_frame_params_9p6MHz[dev_num].pp; else if (swrm->bus_clk == SWR_CLK_RATE_0P6MHZ) pp_dev = swrdev_frame_params_0p6MHz[dev_num].pp; @@ -1681,7 +1692,8 @@ static int swrm_slvdev_datapath_control(struct swr_master *master, bool enable) /* set col = 16 */ n_col = SWR_MAX_COL; col = SWRM_COL_16; - if (swrm->bus_clk == MCLK_FREQ_LP) { + if ((swrm->bus_clk == MCLK_FREQ_LP) || + (swrm->bus_clk == SWR_CLK_RATE_0P768MHZ)) { n_col = SWR_MIN_COL; col = SWRM_COL_02; } @@ -1699,16 +1711,15 @@ static int swrm_slvdev_datapath_control(struct swr_master *master, bool enable) } /* Use default 50 * x, frame shape. Change based on mclk */ if (swrm->mclk_freq == MCLK_FREQ_NATIVE) { - dev_dbg(swrm->dev, "setting 64 x %d frameshape\n", col); n_row = SWR_ROW_64; row = SWRM_ROW_64; frame_sync = SWRM_FRAME_SYNC_SEL_NATIVE; } else { - dev_dbg(swrm->dev, "setting 50 x %d frameshape\n", col); - n_row = SWR_ROW_50; - row = SWRM_ROW_50; + n_row = (swrm->mclk_freq == MCLK_FREQ_12P288MHZ) ? SWR_ROW_64 : SWR_ROW_50; + row = (swrm->mclk_freq == MCLK_FREQ_12P288MHZ) ? SWRM_ROW_64 : SWRM_ROW_50; frame_sync = SWRM_FRAME_SYNC_SEL; } + dev_dbg(swrm->dev, "setting %d x %d frameshape\n", row, col); ssp_period = swrm_get_ssp_period(swrm, row, col, frame_sync); bus_clk_div_factor = swrm_get_clk_div(swrm->mclk_freq, swrm->bus_clk); dev_dbg(swrm->dev, "%s: ssp_period: %d, bus_clk_div:%d \n", __func__, @@ -2209,6 +2220,15 @@ handle_irq: swrm->clk_stop_wakeup = false; } break; + case SWRM_INTERRUPT_STATUS_DOUT_RATE_MISMATCH: + dev_err(swrm->dev, + "%s: SWR Port Channel rate mismatch\n", + __func__); + swrm->intr_mask &= + ~SWRM_INTERRUPT_STATUS_DOUT_RATE_MISMATCH; + swr_master_write(swrm, + SWRM_CPU1_INTERRUPT_EN, swrm->intr_mask); + break; default: dev_err_ratelimited(swrm->dev, "%s: SWR unknown interrupt value: %d\n", @@ -2450,14 +2470,28 @@ static int swrm_master_init(struct swr_mstr_ctrl *swrm) { int ret = 0, i = 0; u32 val; - u8 row_ctrl = SWR_ROW_50; - u8 col_ctrl = SWR_MIN_COL; + u8 row_ctrl = 0, row = 0; + u8 col_ctrl = SWR_MIN_COL, col = SWRM_COL_02; u8 ssp_period = 1; u8 retry_cmd_num = 3; u32 reg[SWRM_MAX_INIT_REG]; u32 value[SWRM_MAX_INIT_REG]; u32 temp = 0; int len = 0; + int frame_sync = SWRM_FRAME_SYNC_SEL; + + if (swrm->mclk_freq == MCLK_FREQ_NATIVE) { + row_ctrl = SWR_ROW_64; + row = SWRM_ROW_64; + frame_sync = SWRM_FRAME_SYNC_SEL_NATIVE; + + } else { + row_ctrl = (swrm->mclk_freq == MCLK_FREQ_12P288MHZ) ? + SWR_ROW_64 : SWR_ROW_50; + row = (swrm->mclk_freq == MCLK_FREQ_12P288MHZ) ? + SWRM_ROW_64 : SWRM_ROW_50; + frame_sync = SWRM_FRAME_SYNC_SEL; + } /* Change no of retry counts to 1 for wsa to avoid underflow */ if (swrm->master_id == MASTER_ID_WSA) @@ -2475,8 +2509,7 @@ static int swrm_master_init(struct swr_mstr_ctrl *swrm) __func__, temp); } } - ssp_period = swrm_get_ssp_period(swrm, SWRM_ROW_50, - SWRM_COL_02, SWRM_FRAME_SYNC_SEL); + ssp_period = swrm_get_ssp_period(swrm, row, col, frame_sync); dev_dbg(swrm->dev, "%s: ssp_period: %d\n", __func__, ssp_period); /* Clear Rows and Cols */ @@ -2595,7 +2628,7 @@ static int swrm_probe(struct platform_device *pdev) { struct swr_mstr_ctrl *swrm; struct swr_ctrl_platform_data *pdata; - u32 i, num_ports, port_num, port_type, ch_mask, swrm_hctl_reg = 0; + u32 i, num_ports, port_num, port_type, ch_mask, swrm_hctl_reg = 0, mclk_freq = 0; u32 *temp, map_size, map_length, ch_iter = 0, old_port_num = 0; int ret = 0; struct clk *lpass_core_hw_vote = NULL; @@ -2830,8 +2863,6 @@ static int swrm_probe(struct platform_device *pdev) swrm->num_rx_chs = 0; swrm->clk_ref_count = 0; swrm->swr_irq_wakeup_capable = 0; - swrm->mclk_freq = MCLK_FREQ; - swrm->bus_clk = MCLK_FREQ; swrm->dev_up = true; swrm->state = SWR_MSTR_UP; swrm->ipc_wakeup = false; @@ -2861,6 +2892,17 @@ static int swrm_probe(struct platform_device *pdev) for (i = 0 ; i < SWR_MSTR_PORT_LEN; i++) INIT_LIST_HEAD(&swrm->mport_cfg[i].port_req_list); + ret = of_property_read_u32(pdev->dev.of_node, "qcom,swrm-mclk-clk-rate", + &mclk_freq); + if (ret) { + swrm->mclk_freq = MCLK_FREQ; + dev_dbg(&pdev->dev, "%s SWRM MCLK not specified, using default: %d,\n", + __func__, swrm->mclk_freq); + } else { + swrm->mclk_freq = mclk_freq; + } + swrm->bus_clk = swrm->mclk_freq; + if (of_property_read_u32(pdev->dev.of_node, "qcom,disable-div2-clk-switch", &swrm->disable_div2_clk_switch)) { @@ -3092,6 +3134,12 @@ static int swrm_runtime_resume(struct device *dev) struct swr_master *mstr = &swrm->master; struct swr_device *swr_dev; u32 temp = 0; + u8 row_ctrl = SWR_ROW_50; + u8 col_ctrl = SWR_MIN_COL; + + if ((swrm->mclk_freq == MCLK_FREQ_NATIVE) || + (swrm->mclk_freq == MCLK_FREQ_12P288MHZ)) + row_ctrl = SWR_ROW_64; dev_dbg(dev, "%s: pm_runtime: resume, state:%d\n", __func__, swrm->state); @@ -3178,7 +3226,7 @@ static int swrm_runtime_resume(struct device *dev) SWRS_SCP_INT_STATUS_MASK_1); if (swrm->state == SWR_MSTR_SSR) { mutex_unlock(&swrm->reslock); - enable_bank_switch(swrm, 0, SWR_ROW_50, SWR_MIN_COL); + enable_bank_switch(swrm, 0, row_ctrl, col_ctrl); mutex_lock(&swrm->reslock); } } else { @@ -3235,6 +3283,13 @@ static int swrm_runtime_suspend(struct device *dev) int current_state = 0; struct irq_data *irq_data = NULL; + u8 row_ctrl = SWR_ROW_50; + u8 col_ctrl = SWR_MIN_COL; + + if ((swrm->mclk_freq == MCLK_FREQ_NATIVE) || + (swrm->mclk_freq == MCLK_FREQ_12P288MHZ)) + row_ctrl = SWR_ROW_64; + dev_dbg(dev, "%s: pm_runtime: suspend state: %d\n", __func__, swrm->state); if (swrm->state == SWR_MSTR_SSR_RESET) { @@ -3268,7 +3323,7 @@ static int swrm_runtime_suspend(struct device *dev) dev_err(dev, "%s: clk stop mode not supported or SSR entry\n", __func__); mutex_unlock(&swrm->reslock); - enable_bank_switch(swrm, 0, SWR_ROW_50, SWR_MIN_COL); + enable_bank_switch(swrm, 0, row_ctrl, col_ctrl); mutex_lock(&swrm->reslock); swrm_clk_pause(swrm); swr_master_write(swrm, SWRM_COMP_CFG, 0x00); diff --git a/techpack/audio/soc/swr-mstr-registers.h b/techpack/audio/soc/swr-mstr-registers.h index 365a81405fe3..fca39739e5d3 100644 --- a/techpack/audio/soc/swr-mstr-registers.h +++ b/techpack/audio/soc/swr-mstr-registers.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2015, 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef _SWRM_REGISTERS_H @@ -125,6 +126,7 @@ #endif /* CONFIG_SWRM_VER_1P1 */ #define SWRM_INTERRUPT_STATUS_EXT_CLK_STOP_WAKEUP 0x10000 +#define SWRM_INTERRUPT_STATUS_DOUT_RATE_MISMATCH 0x20000 #define SWRM_COMP_PARAMS_WR_FIFO_DEPTH 0x00007C00 #define SWRM_COMP_PARAMS_RD_FIFO_DEPTH 0x000F8000