From 2c817ed5d856026698acbb9dfa21d876e40d0e2e Mon Sep 17 00:00:00 2001 From: Vaibhav Raut Date: Mon, 11 Sep 2023 16:02:05 +0530 Subject: [PATCH 01/31] asoc: msm-compress : Fix for CTS-on-gsi with gki compress_pause was failing on gki mode due to change in stream state. The change is done to enable pausing even when the stream state is in the draining state. Change-Id: I3c29236298f4257f3606827cd82f1b962b6ad802 Signed-off-by: Vaibhav Raut --- asoc/msm-compress-q6-v2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/asoc/msm-compress-q6-v2.c b/asoc/msm-compress-q6-v2.c index 745ff4771efd..6097ca2a7ea3 100644 --- a/asoc/msm-compress-q6-v2.c +++ b/asoc/msm-compress-q6-v2.c @@ -2017,6 +2017,7 @@ static int msm_compr_playback_open(struct snd_compr_stream *cstream) atomic_set(&prtd->start, 0); atomic_set(&prtd->drain, 0); #if !IS_ENABLED(CONFIG_AUDIO_QGKI) + snd_compr_use_pause_in_draining(cstream); atomic_set(&prtd->partial_drain, 0); #endif atomic_set(&prtd->xrun, 0); From 71d95e95f39391be61ad30300f70f020e497268b Mon Sep 17 00:00:00 2001 From: Soumya Managoli Date: Wed, 27 Sep 2023 10:56:27 +0530 Subject: [PATCH 02/31] dsp: q6voice: Handle mutex lock-unlock Release lock before return from function. Change-Id: I7889ae660c7062514159013bbd78d4731de49fd7 Signed-off-by: Soumya Managoli --- dsp/q6voice.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dsp/q6voice.c b/dsp/q6voice.c index f5115d225655..769fb9ff41a3 100644 --- a/dsp/q6voice.c +++ b/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); From bfe2d34edeb54cb31e292a718812346d9b3425cc Mon Sep 17 00:00:00 2001 From: Abhilash Sathyanarayana Date: Wed, 20 Sep 2023 18:25:19 +0530 Subject: [PATCH 03/31] dsp: afe: Enhance and expose API for configuring ext clk To configure the external clock, the client (machine driver) is expected to configure the clock attributes (M, N, D, div2x, clk root, etc.) and if required, to toggle the external clock source in the registered callback function, before sending the param to the DSP. Expose the API to the clock driver for the same. During clock disable, the sequence of operations is reversed. Change-Id: I289625e40c3a9c102270eae3c10ad7bf1a112f4f Signed-off-by: Abhilash Sathyanarayana --- dsp/q6afe.c | 80 ++++++++++++++++++++++++++---------------- include/dsp/q6afe-v2.h | 10 ++++++ 2 files changed, 60 insertions(+), 30 deletions(-) diff --git a/dsp/q6afe.c b/dsp/q6afe.c index 3ab1499c8e32..898ca06b0821 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -9858,8 +9858,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 +9871,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 +9889,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 +9927,37 @@ 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); static int ext_dyn_mclk_port_id; static int ext_dyn_clk_root = Q6AFE_LPASS_CLK_ROOT_DEFAULT; @@ -10162,7 +10181,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/include/dsp/q6afe-v2.h b/include/dsp/q6afe-v2.h index 900fe1699528..3056afc991fb 100644 --- a/include/dsp/q6afe-v2.h +++ b/include/dsp/q6afe-v2.h @@ -603,6 +603,8 @@ 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_get_sp_rx_tmax_xmax_logging_data( @@ -634,6 +636,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); From 789301acc793afbeee7345b36ee950be6d2db9fa Mon Sep 17 00:00:00 2001 From: Abhilash Sathyanarayana Date: Thu, 21 Sep 2023 14:59:02 +0530 Subject: [PATCH 04/31] asoc: ext-clk: Add support for configuring ext clk In the case an external clock is used to source the MCLK, configure additional clock attributes to be sent to the DSP. Introduce parsing of new DT properties about the external clock frequency which is required to find the appropriate clock attributes whilst providing backward compatibility to not send any additional clock attributes to the DSP. Change-Id: I029080fe509f9545fb9785bf4a9225e9bb7dff46 Signed-off-by: Abhilash Sathyanarayana --- asoc/codecs/audio-ext-clk-up.c | 115 ++++++++++++++++++++++++++------- 1 file changed, 90 insertions(+), 25 deletions(-) diff --git a/asoc/codecs/audio-ext-clk-up.c b/asoc/codecs/audio-ext-clk-up.c index 8ce627ac86b3..45f430d5e065 100644 --- a/asoc/codecs/audio-ext-clk-up.c +++ b/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", From 981ef85c0e3067f7790cea50d612f60be010bb11 Mon Sep 17 00:00:00 2001 From: Abhilash Sathyanarayana Date: Wed, 20 Sep 2023 18:28:29 +0530 Subject: [PATCH 05/31] asoc: lahaina: Add ext clk source support For audio interfaces to be sourced from an external clock, one needs to configure clock attributes such as the clock root and RCG registers for operation at an appropriate clock frequency. Add support for this by registering for a callback with Q6AFE driver which during clock enable, provides clock attribute values and toggles the GPIO pin sourcing the external clock. Change-Id: I64a849e045a13dedd876b6a82c727a0e0610cbb7 Signed-off-by: Abhilash Sathyanarayana --- asoc/lahaina.c | 755 +++++++++++++++++++++++++++++++++++++- asoc/msm-pcm-routing-v2.c | 21 +- include/dsp/q6afe-v2.h | 1 + 3 files changed, 767 insertions(+), 10 deletions(-) diff --git a/asoc/lahaina.c b/asoc/lahaina.c index 82b18769c339..536f1738eb22 100644 --- a/asoc/lahaina.c +++ b/asoc/lahaina.c @@ -182,6 +182,51 @@ enum { AFE_LOOPBACK_TX_IDX_MAX, }; +#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; +}; + +/* Coupled with "qcom,ext-mclk-oe-cfg" DTSI property */ +#define MCLK_OE_CFG_CELLS 3 +struct ext_mclk_oe_cfg { + char __iomem *reg; + uint32_t reset_val; + uint32_t active_val; +}; + +struct ext_mclk_gpio_info { + struct device_node *gpio_p; /* used by pinctrl API */ + struct ext_mclk_oe_cfg oe_cfg; + 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 msm_asoc_mach_data { struct snd_info_entry *codec_root; int usbc_en2_gpio; /* used by gpio driver API */ @@ -195,6 +240,9 @@ struct msm_asoc_mach_data { 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; @@ -227,6 +275,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 +1088,227 @@ 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->oe_cfg.reg != NULL) { + ret = lahaina_audio_vote(card, true); + if (ret) { + dev_err(card->dev, "%s: HW voting failed, ret: %d\n", + __func__, ret); + goto unlock; + } + + iowrite32(gpio_info->oe_cfg.active_val, + gpio_info->oe_cfg.reg); + + 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 sleep mclk pinctrl\n", + __func__); + ret = 0; + } + + if (gpio_info->oe_cfg.reg != NULL) { + ret = lahaina_audio_vote(card, true); + if (ret) { + dev_err(card->dev, "%s: HW voting failed, ret: %d\n", + __func__, ret); + goto unlock; + } + + iowrite32(gpio_info->oe_cfg.reset_val, + gpio_info->oe_cfg.reg); + + lahaina_audio_vote(card, false); + } + } + } + +unlock: + mutex_unlock(&gpio_info->lock); + + 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; @@ -4742,6 +5024,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 +9107,425 @@ 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; + uint32_t cells = 0; + struct device_node *np = NULL; + struct ext_mclk_gpio_info *gpio_info = NULL; + u32 ext_mclk_oe_cfg_arr[MCLK_OE_CFG_CELLS]; + 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; + } + + /* Parse ext clk OE register info from DT if it's a LPI GPIO */ + if (of_property_read_bool(gpio_info[i].gpio_p, "qcom,lpi-gpios")) { + + ret = of_property_read_u32(gpio_info[i].gpio_p, "#ext-mclk-oe-cfg-cells", &cells); + if (ret) { + dev_err(card->dev, "%s: ext mclk oe cfg cells not found in DT\n", __func__); + ret = -EINVAL; + goto free_gpio_info; + } + + if (cells != MCLK_OE_CFG_CELLS) { + dev_err(card->dev, "%s: invalid ext-mclk-oe-cfg-cells in DT\n", __func__); + ret = -EINVAL; + goto free_gpio_info; + }; + + ret = of_property_read_u32_array(gpio_info[i].gpio_p, + "qcom,ext-mclk-oe-cfg", + ext_mclk_oe_cfg_arr, + cells); + if (ret) { + dev_err(card->dev, "could not find qcom,ext-mclk-oe-cfg DT entry for lpi-gpio"); + ret = -EINVAL; + goto free_gpio_info; + } + + gpio_info[i].oe_cfg.reg = devm_ioremap(card->dev, + ext_mclk_oe_cfg_arr[0], 0x4); + if (!gpio_info[i].oe_cfg.reg) { + dev_err(card->dev, "%s: failed to remap ext mclk OE reg 0x%x", + __func__, ext_mclk_oe_cfg_arr[0]); + ret = -ENOMEM; + goto free_gpio_info; + } + gpio_info[i].oe_cfg.reset_val = ext_mclk_oe_cfg_arr[1]; + gpio_info[i].oe_cfg.active_val = ext_mclk_oe_cfg_arr[2]; + dev_dbg(card->dev, "%s: oe_cfg[%d].reg = 0x%x, reset = %u, active = %u\n", + __func__, i, ext_mclk_oe_cfg_arr[0], gpio_info[i].oe_cfg.reset_val, + gpio_info[i].oe_cfg.active_val); + } else { + gpio_info[i].oe_cfg.reg = NULL; + } + } + + 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 +9584,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 +9607,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 +9762,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/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index 053262d07c87..6767b2c2b564 100644 --- a/asoc/msm-pcm-routing-v2.c +++ b/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]; @@ -42813,6 +42817,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 +42839,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 +42872,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/include/dsp/q6afe-v2.h b/include/dsp/q6afe-v2.h index 3056afc991fb..be75f2c37148 100644 --- a/include/dsp/q6afe-v2.h +++ b/include/dsp/q6afe-v2.h @@ -419,6 +419,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, From bc1ce40dc0eda1e9bf59607ab5d770086332c5b1 Mon Sep 17 00:00:00 2001 From: Abhilash Sathyanarayana Date: Mon, 9 Oct 2023 12:34:12 +0530 Subject: [PATCH 06/31] asoc: codec: make mclk freq configurable in Bolero Add support to make mclk freq configurable in Bolero Macros - VA, TX and RX. Fallback to 9.6MHz if mclk frequency is not defined in DTSI. Change-Id: I27034d6bd31e1ed225adfc8de1b43cd68da84db3 Signed-off-by: Abhilash Sathyanarayana --- asoc/codecs/bolero/rx-macro.c | 16 ++++++++++++++-- asoc/codecs/bolero/tx-macro.c | 24 ++++++++++++++++++------ asoc/codecs/bolero/va-macro.c | 28 ++++++++++++++++++++++------ 3 files changed, 54 insertions(+), 14 deletions(-) diff --git a/asoc/codecs/bolero/rx-macro.c b/asoc/codecs/bolero/rx-macro.c index 1d1153b510ff..509a139144ec 100644 --- a/asoc/codecs/bolero/rx-macro.c +++ b/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: @@ -4166,13 +4168,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 +4210,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/asoc/codecs/bolero/tx-macro.c b/asoc/codecs/bolero/tx-macro.c index 6b0d1d82be81..208d480c0075 100644 --- a/asoc/codecs/bolero/tx-macro.c +++ b/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/asoc/codecs/bolero/va-macro.c b/asoc/codecs/bolero/va-macro.c index 6cd9e9402b64..363c7a41ce2c 100644 --- a/asoc/codecs/bolero/va-macro.c +++ b/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) { From 5206840c7d959e501fa15cd3be8f2878dfa55ddc Mon Sep 17 00:00:00 2001 From: Harendra Gautam Date: Wed, 13 Sep 2023 10:59:58 +0530 Subject: [PATCH 07/31] audio-kernel:swr: Add dynamic SWRM clk support - Add support to load dynamic soundwire master clk from devicetree file. Change-Id: I59527f42aa7d3a7b00a0b161c3867c16aaecb263 Signed-off-by: Harendra Gautam --- include/soc/soundwire.h | 6 +++ include/soc/swr-wcd.h | 1 + soc/swr-mstr-ctrl.c | 92 ++++++++++++++++++++++++++++++----------- 3 files changed, 76 insertions(+), 23 deletions(-) diff --git a/include/soc/soundwire.h b/include/soc/soundwire.h index a8c5ec83e9e2..dffb4db05491 100644 --- a/include/soc/soundwire.h +++ b/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/include/soc/swr-wcd.h b/include/soc/swr-wcd.h index 8ed9f0a05e3b..140d4eecd81d 100644 --- a/include/soc/swr-wcd.h +++ b/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/soc/swr-mstr-ctrl.c b/soc/swr-mstr-ctrl.c index 098f2fe1c36c..b1209a68dcac 100644 --- a/soc/swr-mstr-ctrl.c +++ b/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__, @@ -2450,14 +2461,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 +2500,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 +2619,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 +2854,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 +2883,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 +3125,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 +3217,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 +3274,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 +3314,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); From 5a4223f2494d8c574605b0810e7e59ead702feb1 Mon Sep 17 00:00:00 2001 From: Soumya Managoli Date: Wed, 18 Oct 2023 04:43:17 -0700 Subject: [PATCH 08/31] Revert "asoc: msm-compress : Fix for CTS-on-gsi with gki" This reverts commit 2c817ed5d856026698acbb9dfa21d876e40d0e2e. Reason for revert: Reverting it untill the dependent gerrit https://review-android.quicinc.com/c/kernel/msm-5.4/+/4887724 is not merged Change-Id: I6774e50ffb263a42e4bb0fef5d36ce8982b224f9 Signed-off-by: Soumya Managoli --- asoc/msm-compress-q6-v2.c | 1 - 1 file changed, 1 deletion(-) diff --git a/asoc/msm-compress-q6-v2.c b/asoc/msm-compress-q6-v2.c index 6097ca2a7ea3..745ff4771efd 100644 --- a/asoc/msm-compress-q6-v2.c +++ b/asoc/msm-compress-q6-v2.c @@ -2017,7 +2017,6 @@ static int msm_compr_playback_open(struct snd_compr_stream *cstream) atomic_set(&prtd->start, 0); atomic_set(&prtd->drain, 0); #if !IS_ENABLED(CONFIG_AUDIO_QGKI) - snd_compr_use_pause_in_draining(cstream); atomic_set(&prtd->partial_drain, 0); #endif atomic_set(&prtd->xrun, 0); From a61f696990ded9af9d650be582262208498ca948 Mon Sep 17 00:00:00 2001 From: Vijay Kumar Maddula Date: Wed, 11 Oct 2023 19:04:24 +0530 Subject: [PATCH 09/31] asoc: support for fractional sample rate over tdm Vote for audio hw clk while fractional sample rate capture and playback over TDM interface. Change-Id: I0df0b335f1abec8db6503e7c341ec7c082a8639a --- asoc/msm-dai-q6-v2.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/asoc/msm-dai-q6-v2.c b/asoc/msm-dai-q6-v2.c index bde03c63f5a7..5e3a4186b744 100644 --- a/asoc/msm-dai-q6-v2.c +++ b/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); } From e30fce1a7d8c22157f16d2ee7e52e1a650ad6f1c Mon Sep 17 00:00:00 2001 From: Vijay Kumar Maddula Date: Wed, 11 Oct 2023 18:54:44 +0530 Subject: [PATCH 10/31] asoc: lahaina: fractional sample rate support for TDM Fractional sample rate doesn't work due to GSDC is not enabled. Add vote for audio hw clk for fractional sample rate. Change-Id: I12d5315b0f346d54e7f384b2e38bbfd0f35199ff --- asoc/Kbuild | 2 + asoc/lahaina.c | 100 ++++++++++++++++++++++++---------------------- asoc/msm-common.c | 60 ++++++++++++++++++++++++++++ asoc/msm-common.h | 52 ++++++++++++++++++++++++ 4 files changed, 167 insertions(+), 47 deletions(-) mode change 100755 => 100644 asoc/Kbuild create mode 100644 asoc/msm-common.c create mode 100644 asoc/msm-common.h diff --git a/asoc/Kbuild b/asoc/Kbuild old mode 100755 new mode 100644 index b80f81a35fed..90eb471defb4 --- a/asoc/Kbuild +++ b/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/asoc/lahaina.c b/asoc/lahaina.c index 536f1738eb22..c271688302fa 100644 --- a/asoc/lahaina.c +++ b/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, @@ -227,33 +218,6 @@ struct ext_mclk_src_info { struct ext_mclk_gpio_info *gpio_info; }; -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; -}; - struct tdm_port { u32 mode; u32 channel; @@ -2096,20 +2060,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; @@ -2125,23 +2110,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; diff --git a/asoc/msm-common.c b/asoc/msm-common.c new file mode 100644 index 000000000000..2fd559323f1b --- /dev/null +++ b/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/asoc/msm-common.h b/asoc/msm-common.h new file mode 100644 index 000000000000..94ec25d27320 --- /dev/null +++ b/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 From b8a853fbe698c5d7fd8425d13c07aa3b6d355e1e Mon Sep 17 00:00:00 2001 From: Yanhong Wu Date: Thu, 19 Oct 2023 15:43:03 +0800 Subject: [PATCH 11/31] ASoC : add support of HDMI controller for rb3gen2 platform - add support for HDMI controller and HDMI external display detection mechanism - add controls for HDMI Change-Id: I5da8a12d337d649e49aa2e56c90566a530706287 --- asoc/codecs/msm_hdmi_codec_rx.c | 27 ++++++++++++++++----------- asoc/msm-dai-q6-hdmi-v2.c | 1 + 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/asoc/codecs/msm_hdmi_codec_rx.c b/asoc/codecs/msm_hdmi_codec_rx.c index b0208ededa3b..dc61de473237 100644 --- a/asoc/codecs/msm_hdmi_codec_rx.c +++ b/asoc/codecs/msm_hdmi_codec_rx.c @@ -32,6 +32,7 @@ enum { DP_CONTROLLER0 = 0, + HDMI_CONTROLLER, DP_CONTROLLER_MAX, }; @@ -96,7 +97,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 +149,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 +215,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 +307,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 +367,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 +399,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 +499,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 +527,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 +588,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 +662,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 +706,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/asoc/msm-dai-q6-hdmi-v2.c b/asoc/msm-dai-q6-hdmi-v2.c index 42d53e721928..2e394cb3fe25 100644 --- a/asoc/msm-dai-q6-hdmi-v2.c +++ b/asoc/msm-dai-q6-hdmi-v2.c @@ -22,6 +22,7 @@ enum { DP_CONTROLLER0 = 0, DP_CONTROLLER1, + HDMI_CONTROLLER, DP_CONTROLLER_MAX, }; From 5f2bfaae5b866f8f00ca0d9c5755440f6026b75c Mon Sep 17 00:00:00 2001 From: Soumya Managoli Date: Mon, 30 Oct 2023 16:40:06 +0530 Subject: [PATCH 12/31] ASoC: bolero: Add check for CMPDR switch Do not disable CMPDR for EAR if ear mode is on as it can lead to mute issues on ear. Change-Id: Ibf54fe6f14c5f08c2004fae36204398109ad53c1 Signed-off-by: Soumya Managoli --- asoc/codecs/bolero/rx-macro.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/asoc/codecs/bolero/rx-macro.c b/asoc/codecs/bolero/rx-macro.c index 509a139144ec..50fb7dc9341d 100644 --- a/asoc/codecs/bolero/rx-macro.c +++ b/asoc/codecs/bolero/rx-macro.c @@ -2691,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, From 9d1d4d6fb086d0a9e575dbe696aeacba20324a6a Mon Sep 17 00:00:00 2001 From: Soumya Managoli Date: Fri, 27 Oct 2023 18:53:56 +0530 Subject: [PATCH 13/31] ASoC: wcd937x: Add flag to decide RX_MUTE for HPHL and EAR Check if HPHL or EAR is enabled before sending RX_MUTE event. Change-Id: I4ffc42a0d79c8edea77745a2f52361a4cf3dddaa Signed-off-by: Soumya Managoli --- asoc/codecs/wcd937x/wcd937x.c | 53 ++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/asoc/codecs/wcd937x/wcd937x.c b/asoc/codecs/wcd937x/wcd937x.c index 7bec6b399a87..3306130ccd78 100644 --- a/asoc/codecs/wcd937x/wcd937x.c +++ b/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; From 82a064bd22e1f164daa6e81a97cd36ebcd5b0734 Mon Sep 17 00:00:00 2001 From: Soumya Managoli Date: Tue, 7 Nov 2023 11:24:19 +0530 Subject: [PATCH 14/31] ASoC: dsp: Release lock before return Before return with error, release the mutex. Change-Id: I034a90f9c4f52df8bc8e30312f5241ac4ba7bba0 Signed-off-by: Soumya Managoli --- dsp/q6core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dsp/q6core.c b/dsp/q6core.c index df72815babc9..1df46209377f 100644 --- a/dsp/q6core.c +++ b/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, From a64408fc2f615e5f529cfe062534bba6f3115f56 Mon Sep 17 00:00:00 2001 From: Abhilash Sathyanarayana Date: Thu, 9 Nov 2023 15:58:31 +0530 Subject: [PATCH 15/31] dsp: afe: add support for aud_ref_clk_sel mux External mclk to audio interfaces can be routed via different gpios as per platform by configuring aud_ref_clk_sel mux. Add support in q6afe to expose new set_param from DSP. Change-Id: I0677877d2ad0cabdf00a7b213652db4b62f0d8b5 Signed-off-by: Abhilash Sathyanarayana --- dsp/q6afe.c | 71 ++++++++++++++++++++++++++++++++++++++ include/dsp/apr_audio-v2.h | 11 ++++++ include/dsp/q6afe-v2.h | 5 ++- 3 files changed, 86 insertions(+), 1 deletion(-) diff --git a/dsp/q6afe.c b/dsp/q6afe.c index 898ca06b0821..2e4e58b86497 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -9959,6 +9959,77 @@ stop_mclk: } 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; static struct afe_param_id_clock_set_v2_t ext_dyn_mclk; diff --git a/include/dsp/apr_audio-v2.h b/include/dsp/apr_audio-v2.h index ee0bfd5f2f23..79e258589e1d 100644 --- a/include/dsp/apr_audio-v2.h +++ b/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/include/dsp/q6afe-v2.h b/include/dsp/q6afe-v2.h index be75f2c37148..13a534a40158 100644 --- a/include/dsp/q6afe-v2.h +++ b/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 @@ -608,6 +610,7 @@ 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); From df00ab04b16f7ca63dfacd0d49f8ff1573de725a Mon Sep 17 00:00:00 2001 From: Abhilash Sathyanarayana Date: Thu, 9 Nov 2023 17:03:24 +0530 Subject: [PATCH 16/31] asoc: lahaina: add support for aud_ref_clk_sel mux External mclk to audiocc can be routed via ext_mclk1_a or ext_mclk1_b gpio on lahaina by configuring aud_ref_clk_sel mux. Add changes to configure aud_ref_clk_sel mux in machine driver based on DT property. Change-Id: I606cb148113263a53d6ad02011db0c5c8d042126 --- asoc/lahaina.c | 99 ++++++++++++++++---------------------------------- 1 file changed, 32 insertions(+), 67 deletions(-) diff --git a/asoc/lahaina.c b/asoc/lahaina.c index 536f1738eb22..ee4aab481c25 100644 --- a/asoc/lahaina.c +++ b/asoc/lahaina.c @@ -206,17 +206,10 @@ struct ext_mclk_cfg_info { u32 num_mclk_cfg; }; -/* Coupled with "qcom,ext-mclk-oe-cfg" DTSI property */ -#define MCLK_OE_CFG_CELLS 3 -struct ext_mclk_oe_cfg { - char __iomem *reg; - uint32_t reset_val; - uint32_t active_val; -}; - struct ext_mclk_gpio_info { struct device_node *gpio_p; /* used by pinctrl API */ - struct ext_mclk_oe_cfg oe_cfg; + const char *ext_mclk_muxsel_str; + uint32_t ext_mclk_muxsel_val; uint32_t ref_cnt; struct mutex lock; }; @@ -1191,7 +1184,7 @@ static int lahaina_handle_ext_mclk_gpio(struct snd_soc_card *card, goto unlock; } - if (gpio_info->oe_cfg.reg != NULL) { + 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", @@ -1199,9 +1192,8 @@ static int lahaina_handle_ext_mclk_gpio(struct snd_soc_card *card, goto unlock; } - iowrite32(gpio_info->oe_cfg.active_val, - gpio_info->oe_cfg.reg); - + 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); } } @@ -1209,29 +1201,16 @@ static int lahaina_handle_ext_mclk_gpio(struct snd_soc_card *card, 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 sleep mclk pinctrl\n", + dev_err(card->dev, "%s: couldn't deactivate mclk pinctrl\n", __func__); - ret = 0; - } - - if (gpio_info->oe_cfg.reg != NULL) { - ret = lahaina_audio_vote(card, true); - if (ret) { - dev_err(card->dev, "%s: HW voting failed, ret: %d\n", - __func__, ret); - goto unlock; - } - - iowrite32(gpio_info->oe_cfg.reset_val, - gpio_info->oe_cfg.reg); - - lahaina_audio_vote(card, false); } } } unlock: mutex_unlock(&gpio_info->lock); + if (ret) + enable ? --gpio_info->ref_cnt : ++gpio_info->ref_cnt; return ret; } @@ -9113,10 +9092,10 @@ static int msm_parse_ext_mclk_gpios(struct snd_soc_card *card, int ret = 0; uint32_t len = 0; uint32_t num_gpios = 0; - uint32_t cells = 0; struct device_node *np = NULL; struct ext_mclk_gpio_info *gpio_info = NULL; - u32 ext_mclk_oe_cfg_arr[MCLK_OE_CFG_CELLS]; + const char *ext_mclk_muxsel_str = NULL; + u32 ext_mclk_muxsel_val = 0; int i = 0; if (!card || !card->dev || !card->dev->of_node) @@ -9154,47 +9133,33 @@ static int msm_parse_ext_mclk_gpios(struct snd_soc_card *card, goto free_gpio_info; } - /* Parse ext clk OE register info from DT if it's a LPI GPIO */ + /* 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_u32(gpio_info[i].gpio_p, "#ext-mclk-oe-cfg-cells", &cells); + 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: ext mclk oe cfg cells not found in DT\n", __func__); + 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; + } } - - if (cells != MCLK_OE_CFG_CELLS) { - dev_err(card->dev, "%s: invalid ext-mclk-oe-cfg-cells in DT\n", __func__); - ret = -EINVAL; - goto free_gpio_info; - }; - - ret = of_property_read_u32_array(gpio_info[i].gpio_p, - "qcom,ext-mclk-oe-cfg", - ext_mclk_oe_cfg_arr, - cells); - if (ret) { - dev_err(card->dev, "could not find qcom,ext-mclk-oe-cfg DT entry for lpi-gpio"); - ret = -EINVAL; - goto free_gpio_info; - } - - gpio_info[i].oe_cfg.reg = devm_ioremap(card->dev, - ext_mclk_oe_cfg_arr[0], 0x4); - if (!gpio_info[i].oe_cfg.reg) { - dev_err(card->dev, "%s: failed to remap ext mclk OE reg 0x%x", - __func__, ext_mclk_oe_cfg_arr[0]); - ret = -ENOMEM; - goto free_gpio_info; - } - gpio_info[i].oe_cfg.reset_val = ext_mclk_oe_cfg_arr[1]; - gpio_info[i].oe_cfg.active_val = ext_mclk_oe_cfg_arr[2]; - dev_dbg(card->dev, "%s: oe_cfg[%d].reg = 0x%x, reset = %u, active = %u\n", - __func__, i, ext_mclk_oe_cfg_arr[0], gpio_info[i].oe_cfg.reset_val, - gpio_info[i].oe_cfg.active_val); - } else { - gpio_info[i].oe_cfg.reg = NULL; + 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; } } From b243c8109bb35814e610027ef811562726339fa7 Mon Sep 17 00:00:00 2001 From: Changxiang Wang Date: Fri, 10 Nov 2023 11:53:23 +0800 Subject: [PATCH 17/31] asoc : add configuration about SLIMBUS_7_TX - Add configuration about SLIMBUS_7_TX. Change-Id: Ic74ed0f5d63ddfa253b1f3577f7bce947f99f5c8 --- asoc/msm-pcm-routing-v2.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index 053262d07c87..370ee00cafcc 100644 --- a/asoc/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -30766,6 +30766,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 +36841,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 +36967,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"}, From 750376e36aba971ff6612d7a9d31b81759924c12 Mon Sep 17 00:00:00 2001 From: Soumya Managoli Date: Fri, 7 Jul 2023 11:23:34 +0530 Subject: [PATCH 18/31] soc: Address SWR rate mismatch interrupt Handle DOUT_RATE_MISMATCH irq. Change-Id: I06c113a9aa54add123b6b28f15b12263bcb63c60 Signed-off-by: Soumya Managoli --- soc/swr-mstr-ctrl.c | 9 +++++++++ soc/swr-mstr-registers.h | 2 ++ 2 files changed, 11 insertions(+) diff --git a/soc/swr-mstr-ctrl.c b/soc/swr-mstr-ctrl.c index b1209a68dcac..7622d18963b6 100644 --- a/soc/swr-mstr-ctrl.c +++ b/soc/swr-mstr-ctrl.c @@ -2220,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", diff --git a/soc/swr-mstr-registers.h b/soc/swr-mstr-registers.h index 365a81405fe3..fca39739e5d3 100644 --- a/soc/swr-mstr-registers.h +++ b/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 From 1fe88adb32fd190b23a0ab89aaea11fab5b4d377 Mon Sep 17 00:00:00 2001 From: Kumar Anurag Singh Date: Sun, 3 Dec 2023 21:32:51 -0800 Subject: [PATCH 19/31] asoc: codecs: add array bound check Add bound check to avoid out of bound access for array. Change-Id: Ia43a50dc0c4e956be00fba92ba6819dbcc9b0ee3 Signed-off-by: Kumar Anurag Singh --- asoc/codecs/msm_hdmi_codec_rx.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/asoc/codecs/msm_hdmi_codec_rx.c b/asoc/codecs/msm_hdmi_codec_rx.c index dc61de473237..be46409219f2 100644 --- a/asoc/codecs/msm_hdmi_codec_rx.c +++ b/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 @@ -367,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_DAI_MAX) { + if (dai_id < 0 || dai_id >= DP_DAI_MAX) { dev_err(component->dev, "%s: invalid dai id: %d\n", __func__, dai_id); rc = -EINVAL; @@ -399,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_DAI_MAX) { + if (dai_id < 0 || dai_id >= DP_DAI_MAX) { dev_err(component->dev, "%s: invalid dai id: %d\n", __func__, dai_id); rc = -EINVAL; From eb8bc3afa11ad519a4f8b2bdd76ee3af05581e47 Mon Sep 17 00:00:00 2001 From: Manoj Kumar N D Date: Thu, 14 Sep 2023 18:14:02 +0530 Subject: [PATCH 20/31] ASoC: msm: get CoPP index based on FE id Get the CoPP idx based on FE id before sending the channel mixer settings during runtime. Signed-off-by: Manoj Kumar N D Change-Id: I2a0fc517bf3e53523679e3108ca6849d2f96e79d (cherry picked from commit e9c96f97eda6069576731984a146343ccb32d900) (cherry picked from commit d6d824a975955075d02e2ed3b51f8ef4d6f202b7) --- asoc/msm-compress-q6-v2.c | 2 +- asoc/msm-pcm-loopback-v2.c | 2 +- asoc/msm-pcm-q6-v2.c | 2 +- asoc/msm-pcm-routing-auto.c | 13 ++++++++++--- asoc/msm-pcm-routing-v2.h | 2 +- 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/asoc/msm-compress-q6-v2.c b/asoc/msm-compress-q6-v2.c index 745ff4771efd..70c34d5bdd7a 100644 --- a/asoc/msm-compress-q6-v2.c +++ b/asoc/msm-compress-q6-v2.c @@ -5218,7 +5218,7 @@ 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; - msm_pcm_routing_set_channel_mixer_runtime(be_id, + msm_pcm_routing_set_channel_mixer_runtime(fe_id, be_id, stream_id, session_type, chmixer_pspd); } } diff --git a/asoc/msm-pcm-loopback-v2.c b/asoc/msm-pcm-loopback-v2.c index 0746ffd0c349..33a5661ccbe5 100644 --- a/asoc/msm-pcm-loopback-v2.c +++ b/asoc/msm-pcm-loopback-v2.c @@ -1022,7 +1022,7 @@ 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; - msm_pcm_routing_set_channel_mixer_runtime(be_id, + msm_pcm_routing_set_channel_mixer_runtime(fe_id, be_id, stream_id, session_type, chmixer_pspd); diff --git a/asoc/msm-pcm-q6-v2.c b/asoc/msm-pcm-q6-v2.c index bb19c4b7d386..5f764a2b36fd 100644 --- a/asoc/msm-pcm-q6-v2.c +++ b/asoc/msm-pcm-q6-v2.c @@ -2681,7 +2681,7 @@ 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; - msm_pcm_routing_set_channel_mixer_runtime(be_id, + msm_pcm_routing_set_channel_mixer_runtime(fe_id, be_id, stream_id, session_type, chmixer_pspd); diff --git a/asoc/msm-pcm-routing-auto.c b/asoc/msm-pcm-routing-auto.c index 14114c820b29..93340557082a 100644 --- a/asoc/msm-pcm-routing-auto.c +++ b/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/asoc/msm-pcm-routing-v2.h b/asoc/msm-pcm-routing-v2.h index d57851b7bec4..40c0045284c7 100644 --- a/asoc/msm-pcm-routing-v2.h +++ b/asoc/msm-pcm-routing-v2.h @@ -819,7 +819,7 @@ int msm_pcm_routing_set_channel_mixer_cfg( struct msm_pcm_channel_mixer *params); 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); From 70a4c76545f3b03ff822c7d0054d052d582a078b Mon Sep 17 00:00:00 2001 From: Manoj Kumar N D Date: Wed, 3 Jan 2024 13:24:35 +0530 Subject: [PATCH 21/31] ASoC : Add macro to differentiate auto code Macro needed for differentiation of auto code as the file is shared for non auto code as well. Signed-off-by: Manoj Kumar N D Change-Id: I8b464d5cacd2b5bcf51c54a3e50c930d9cb54e41 --- asoc/msm-compress-q6-v2.c | 7 ++++++- asoc/msm-pcm-loopback-v2.c | 9 ++++++++- asoc/msm-pcm-q6-v2.c | 9 ++++++++- asoc/msm-pcm-routing-v2.h | 9 ++++++++- 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/asoc/msm-compress-q6-v2.c b/asoc/msm-compress-q6-v2.c index 70c34d5bdd7a..3fd1c9ecf8a6 100644 --- a/asoc/msm-compress-q6-v2.c +++ b/asoc/msm-compress-q6-v2.c @@ -2,7 +2,7 @@ /* 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. + * Copyright (c) 2022-2024 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 @@ -5218,8 +5218,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/asoc/msm-pcm-loopback-v2.c b/asoc/msm-pcm-loopback-v2.c index 33a5661ccbe5..6d9a3a11c968 100644 --- a/asoc/msm-pcm-loopback-v2.c +++ b/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/asoc/msm-pcm-q6-v2.c b/asoc/msm-pcm-q6-v2.c index 5f764a2b36fd..2ef5e4c92158 100644 --- a/asoc/msm-pcm-q6-v2.c +++ b/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. @@ -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/asoc/msm-pcm-routing-v2.h b/asoc/msm-pcm-routing-v2.h index 40c0045284c7..6db54df75cd6 100644 --- a/asoc/msm-pcm-routing-v2.h +++ b/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 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); From acb96a6c93e79f60613586aef81783201451c3e9 Mon Sep 17 00:00:00 2001 From: Raza Kamal Date: Mon, 4 Dec 2023 15:33:09 +0530 Subject: [PATCH 22/31] Audio legacy: Integer overflow in msm_lsm_ioctl_compat during audio playback usecase. size = sizeof(p_info_32) + p_info_32.param_size; This overflow issue may result heap overflow during copying the data: memcpy(param_info_rsp, &p_info_32, sizeof(p_info_32)); The validation check is added so that heap overflow can be avoided. Change-Id: I11dcbe7ebb33e349dfd9f347f3ef25bc781075fc Signed-off-by: Raza Kamal --- asoc/msm-lsm-client.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/asoc/msm-lsm-client.c b/asoc/msm-lsm-client.c index 860ad10e79d3..443f62e84d01 100644 --- a/asoc/msm-lsm-client.c +++ b/asoc/msm-lsm-client.c @@ -2379,8 +2379,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) { From 0fd8563254f37219d87a2ab9a8922ff4199cdb18 Mon Sep 17 00:00:00 2001 From: Vaibhav Raut Date: Mon, 11 Sep 2023 16:02:05 +0530 Subject: [PATCH 23/31] asoc: msm-compress: Fix compress_pause failure on gki compress_pause was failing on gki mode due to change in stream state. The change is done to enable pausing even when the stream state is in the draining state. Change-Id: I3285b84a1e17dc483bf35a45c8cee693f18671c5 Signed-off-by: Vaibhav Raut --- asoc/msm-compress-q6-v2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/asoc/msm-compress-q6-v2.c b/asoc/msm-compress-q6-v2.c index 3fd1c9ecf8a6..3d58aeefe630 100644 --- a/asoc/msm-compress-q6-v2.c +++ b/asoc/msm-compress-q6-v2.c @@ -2017,6 +2017,7 @@ static int msm_compr_playback_open(struct snd_compr_stream *cstream) atomic_set(&prtd->start, 0); atomic_set(&prtd->drain, 0); #if !IS_ENABLED(CONFIG_AUDIO_QGKI) + snd_compr_use_pause_in_draining(cstream); atomic_set(&prtd->partial_drain, 0); #endif atomic_set(&prtd->xrun, 0); From f3c67e033cc24cda371d6a36719f7c270006ecc9 Mon Sep 17 00:00:00 2001 From: Abhilash Sathyanarayana Date: Tue, 16 Jan 2024 13:20:24 +0530 Subject: [PATCH 24/31] soc: pinctrl-lpi: remove pm ops pm runtime resume and suspend operation uses linux clock API to vote/unvote lpass hw resources. When autosuspend funtionality is used, suspend op is triggered from a worker thread upon timer expiration, can block if there is an ongoing clock operation holding global prepare/unprepare mutex lock in common clock framwork. If the ongoing clock operation calls get_sync on pinctrl-lpi, the get_sync gets scheduled out due to ongoing suspend by worker thread. This leads to dead lock between get_sync and suspend, waiting for each other to complete. Avoid using autosuspend functionality in pinctrl-lpi to avoid race conditions in clock APIs. As auto suspend functionality is not required, lpass hw voting/devoting happens in synchronous manner, can be done directly by calling DSP voting APIs. Hence remove runtime pm ops in lpi pinctrl driver. Change-Id: I86df5e8e502c6466feea196fe8649c34e046aa26 Signed-off-by: Abhilash Sathyanarayana --- soc/pinctrl-lpi.c | 201 +++++++++++++--------------------------------- 1 file changed, 54 insertions(+), 147 deletions(-) diff --git a/soc/pinctrl-lpi.c b/soc/pinctrl-lpi.c index 4a383a65d4f4..8b0781d6071f 100644 --- a/soc/pinctrl-lpi.c +++ b/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, }, From 93604dbb19b5270023a0ebd6fe511e9ed4193255 Mon Sep 17 00:00:00 2001 From: Yanhong Wu Date: Tue, 16 Jan 2024 14:55:45 +0800 Subject: [PATCH 25/31] dsp: afe: Add check for num_spks - Check for valid num_spks before accessing. Change-Id: Idec0edb1f2e6b37d75b007586f65f18c505772f9 Signed-off-by: Yanhong Wu --- dsp/q6afe.c | 52 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/dsp/q6afe.c b/dsp/q6afe.c index 2e4e58b86497..d5c16bfc5829 100644 --- a/dsp/q6afe.c +++ b/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; From 8f7501ee82dea21e48204112fa12066231bd2952 Mon Sep 17 00:00:00 2001 From: Manoj Kumar N D Date: Mon, 26 Feb 2024 15:34:59 +0530 Subject: [PATCH 26/31] ASoC : Add proper copyright marking. Signed-off-by: Manoj Kumar N D Change-Id: I07448a2f065ff106b0d81ad4ac4e895b8c9151a8 --- asoc/msm-compress-q6-v2.c | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/asoc/msm-compress-q6-v2.c b/asoc/msm-compress-q6-v2.c index 3d58aeefe630..ce177a4b990f 100644 --- a/asoc/msm-compress-q6-v2.c +++ b/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-2024 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. */ From c17bb7ef220993e32459685eaaf1fe216069d3f0 Mon Sep 17 00:00:00 2001 From: Soumya Managoli Date: Fri, 23 Feb 2024 10:04:53 +0530 Subject: [PATCH 27/31] ASoC: msm-pcm-q6-v2: Add size check Check if buf size is valid before comparing with buf offset. Signed-off-by: Soumya Managoli Change-Id: I8336be046c4922ce2f8bb333b58ca867059e9528 --- asoc/msm-pcm-q6-v2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asoc/msm-pcm-q6-v2.c b/asoc/msm-pcm-q6-v2.c index 2ef5e4c92158..ac78ef98229a 100644 --- a/asoc/msm-pcm-q6-v2.c +++ b/asoc/msm-pcm-q6-v2.c @@ -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); From 19475bc169d4a3806e3a12d0917d1bc8fc3d8786 Mon Sep 17 00:00:00 2001 From: Jing Wang Date: Fri, 1 Mar 2024 21:08:05 +0800 Subject: [PATCH 28/31] dsp: q6asm increase the locking range Increase the locking range to prevent shared resources from being abnormal modified. Change-Id: Icb7ecfd6110874e6ecfa5bea5c1bd2625127e08d Signed-off-by: Jing Wang (cherry picked from commit ebd90817721f88c9215b6e0511fcaff3125f8138) --- dsp/q6asm.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dsp/q6asm.c b/dsp/q6asm.c index 5ba45f3c775d..3016cf5ff05b 100644 --- a/dsp/q6asm.c +++ b/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; From fb09ec8587ea689fc274c0e1b9d096b55cea36c8 Mon Sep 17 00:00:00 2001 From: Kumar Anurag Singh Date: Thu, 4 Apr 2024 21:27:36 -0700 Subject: [PATCH 29/31] Fix for OOB access issue Added payload size check to avoid OOB read issues. Change-Id: I4f15bdfdcf15e388ebc49dd0e8cf7a99ed03d0d5 Signed-off-by: Kumar Anurag Singh --- dsp/q6adm.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/dsp/q6adm.c b/dsp/q6adm.c index 32a5b29d3a1c..7455252e2f08 100644 --- a/dsp/q6adm.c +++ b/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) { From bbe748c8dfa4de303462edaba96d4ce283c13bc7 Mon Sep 17 00:00:00 2001 From: Kumar Anurag Singh Date: Mon, 15 Apr 2024 00:50:50 -0700 Subject: [PATCH 30/31] dsp: q6lsm: Check size of payload before access check size of payload before access in q6lsm_mmapcallback. Change-Id: I6a755ca4cf54078f0d00f38e303f1b1da29b244c Signed-off-by: Kumar Anurag Singh --- dsp/q6lsm.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/dsp/q6lsm.c b/dsp/q6lsm.c index e1aad637d8be..f79298d0c31d 100644 --- a/dsp/q6lsm.c +++ b/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; From 044504518aa566f8b8676aabc93abe89a5a1d388 Mon Sep 17 00:00:00 2001 From: Sandhya Mutha Naga Venkata Date: Tue, 16 Apr 2024 22:16:56 +0530 Subject: [PATCH 31/31] Asoc: dsp: Fix to check the list is empty or not Added change to check the list before access it. Change-Id: Ibbf80f157caf06453daefd3a38607b0460157123 Signed-off-by: Sandhya Mutha Naga Venkata --- asoc/msm-lsm-client.c | 9 ++++++++- dsp/q6lsm.c | 6 ++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/asoc/msm-lsm-client.c b/asoc/msm-lsm-client.c index 443f62e84d01..9b82d8c49a51 100644 --- a/asoc/msm-lsm-client.c +++ b/asoc/msm-lsm-client.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2013-2020, The Linux Foundation. All rights reserved. - * Copyright (c) 2022-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) { diff --git a/dsp/q6lsm.c b/dsp/q6lsm.c index f79298d0c31d..175c8fcb1a7e 100644 --- a/dsp/q6lsm.c +++ b/dsp/q6lsm.c @@ -2460,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) {