Re: [PATCH v1 0/4] ASoC: qcom: qdsp6: Add MI2S clock control

From: Mohammad Rafi Shaik

Date: Tue Mar 17 2026 - 01:49:17 EST




On 3/10/2026 7:32 PM, Neil Armstrong wrote:
On 3/9/26 12:12, Mohammad Rafi Shaik wrote:
Add support for MI2S clock control within q6apm-lpass DAIs, including
handling of MCLK, BCLK, and ECLK via the DAI .set_sysclk callback.
Each MI2S port now retrieves its clock handles from the device tree,
allowing per-port clock configuration and proper enable/disable during
startup and shutdown.

Enhances the sc8280xp machine driver to set the boards spacific
configurations, some of the boards like talos using third party
codec's which need's additional MCLK settings for audio to work.

Mohammad Rafi Shaik (4):
   ASoC: dt-bindings: qcom,q6apm-lpass-dais: Document DAI subnode
   ASoC: qcom: qdsp6: q6prm: add the missing LPASS MCLK clock IDs
   ASoC: qcom: q6apm-lpass-dais: Add MI2S clock control
   ASoC: qcom: sc8280xp: ASoC: qcom: sc8280xp: enhance machine driver for
     board-specific config

  .../bindings/sound/qcom,q6apm-lpass-dais.yaml |  41 +++-
  sound/soc/qcom/qdsp6/q6apm-lpass-dais.c       | 137 ++++++++++++-
  sound/soc/qcom/qdsp6/q6prm-clocks.c           |   5 +
  sound/soc/qcom/qdsp6/q6prm.h                  |  15 ++
  sound/soc/qcom/sc8280xp.c                     | 180 ++++++++++++++++--
  5 files changed, 357 insertions(+), 21 deletions(-)


base-commit: a0ae2a256046c0c5d3778d1a194ff2e171f16e5f


I'm not sure about the overall architecture, but I managed to make the I2S HDMI audio
work on the SM8650 HDK with:



Thanks Neil for validating.

==============================><=======================================
diff --git a/arch/arm64/boot/dts/qcom/sm8650-hdk.dts b/arch/arm64/boot/ dts/qcom/sm8650-hdk.dts
index 5bf1af3308ce..8316e17dc76b 100644
--- a/arch/arm64/boot/dts/qcom/sm8650-hdk.dts
+++ b/arch/arm64/boot/dts/qcom/sm8650-hdk.dts
@@ -218,6 +218,22 @@ platform {
                 sound-dai = <&q6apm>;
             };
         };
+
+        pri-mi2s-dai-link {
+            link-name = "HDMI Playback";
+
+            cpu {
+                sound-dai = <&q6apmbedai PRIMARY_MI2S_RX>;
+            };
+
+            codec {
+                sound-dai = <&lt9611_codec 0>;
+            };
+
+            platform {
+                sound-dai = <&q6apm>;
+            };
+        };
     };

     vph_pwr: regulator-vph-pwr {
@@ -853,6 +869,7 @@ &i2c6 {
     lt9611_codec: hdmi-bridge@2b {
         compatible = "lontium,lt9611uxc";
         reg = <0x2b>;
+        #sound-dai-cells = <1>;

         interrupts-extended = <&tlmm 85 IRQ_TYPE_EDGE_FALLING>;

@@ -861,7 +878,10 @@ lt9611_codec: hdmi-bridge@2b {
         vdd-supply = <&lt9611_1v2>;
         vcc-supply = <&lt9611_3v3>;

-        pinctrl-0 = <&lt9611_irq_pin>, <&lt9611_rst_pin>;
+        pinctrl-0 = <&lt9611_irq_pin>,
+                <&lt9611_rst_pin>,
+                <&i2s0_default_state>,
+                <&audio_mclk0_default_state>;
         pinctrl-names = "default";

         ports {
@@ -1056,6 +1076,17 @@ &pon_resin {
     status = "okay";
 };

+&q6apmbedai {
+    #address-cells = <1>;
+    #size-cells = <0>;
+
+    reg = <PRIMARY_MI2S_RX>;
+    clocks = <&q6prmcc LPASS_CLK_ID_MCLK_1 LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
+         <&q6prmcc LPASS_CLK_ID_PRI_MI2S_IBIT LPASS_CLK_ATTRIBUTE_COUPLE_NO>;
+    clock-names = "mclk",
+              "bclk";
+};
+
 &qup_i2c3_data_clk {
     /* Use internal I2C pull-up */
     bias-pull-up = <2200>;
diff --git a/arch/arm64/boot/dts/qcom/sm8650.dtsi b/arch/arm64/boot/dts/ qcom/sm8650.dtsi
index f8e1950a74ac..9818fbd8094e 100644
--- a/arch/arm64/boot/dts/qcom/sm8650.dtsi
+++ b/arch/arm64/boot/dts/qcom/sm8650.dtsi
@@ -6237,6 +6237,46 @@ wake-pins {
                 };
             };

+            audio_mclk0_default_state: audio-mclk0-default-state {
+                pins = "gpio125";
+                function = "audio_ext_mclk0";
+                drive-strength = <8>;
+                bias-disable;
+                output-high;
+            };
+
+            i2s0_default_state: i2s0-default-state {
+                sck-pins {
+                    pins = "gpio126";
+                    function = "i2s0_sck";
+                    drive-strength = <8>;
+                    bias-disable;
+                    output-high;
+                };
+
+                data0-pins {
+                    pins = "gpio127";
+                    function = "i2s0_data0";
+                    drive-strength = <8>;
+                    bias-disable;
+                };
+
+                data1-pins {
+                    pins = "gpio128";
+                    function = "i2s0_data1";
+                    drive-strength = <8>;
+                    bias-disable;
+                };
+
+                ws-pins {
+                    pins = "gpio129";
+                    function = "i2s0_ws";
+                    drive-strength = <8>;
+                    bias-disable;
+                    output-high;
+                };
+            };
+
             qup_i2c0_data_clk: qup-i2c0-data-clk-state {
                 /* SDA, SCL */
                 pins = "gpio32", "gpio33";
diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c
index 7aa87e8a6cc5..c2bd5f2a696d 100644
--- a/sound/soc/qcom/sc8280xp.c
+++ b/sound/soc/qcom/sc8280xp.c
@@ -17,8 +17,12 @@
 #include "common.h"
 #include "sdw.h"

-#define MCLK_FREQ        12288000
-#define MCLK_NATIVE_FREQ    11289600
+#define I2S_MCLKFS    256
+#define I2S_SLOTSIZE    16
+#define I2S_MCLK_RATE(rate, channels) \
+        ((rate) * (channels) * I2S_MCLKFS)
+#define I2S_BIT_RATE(rate, channels) \
+        ((rate) * (channels) * I2S_SLOTSIZE)

 static struct snd_soc_dapm_widget sc8280xp_dapm_widgets[] = {
     SND_SOC_DAPM_HP("Headphone Jack", NULL),
@@ -40,6 +44,7 @@ struct snd_soc_common {
     const struct snd_soc_dapm_route *dapm_routes;
     int num_dapm_routes;
     bool mi2s_mclk_enable;
+    bool mi2s_bclk_enable;
 };

 struct sc8280xp_snd_data {
@@ -51,21 +56,22 @@ struct sc8280xp_snd_data {
     bool jack_setup;
 };

-static inline int sc8280xp_get_mclk_feq(unsigned int rate)
+static inline int sc8280xp_get_mclk_freq(struct snd_pcm_hw_params *params)
 {
-    int freq = MCLK_FREQ;
-
     switch (rate) {
     case SNDRV_PCM_RATE_11025:
     case SNDRV_PCM_RATE_44100:
     case SNDRV_PCM_RATE_88200:
-        freq = MCLK_NATIVE_FREQ;
+        return I2S_MCLK_RATE(44100, params_channels(params));
         break;
     default:
-        break;
+        return I2S_MCLK_RATE(params_rate(params), params_channels(params));
     }
+}

-    return freq;
+static inline int sc8280xp_get_bclk_freq(struct snd_pcm_hw_params *params)
+{
+    return I2S_BIT_RATE(params_rate(params), params_channels(params));
 }

 static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd)
@@ -142,8 +148,13 @@ static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream,
 {
     struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
     struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+    struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
     struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
-    int mclk_freq = sc8280xp_get_mclk_feq(params_rate(params));
+    int mclk_freq = sc8280xp_get_mclk_freq(params);
+    int bclk_freq = sc8280xp_get_bclk_freq(params);
+    unsigned int codec_dai_fmt = SND_SOC_DAIFMT_BC_FC |
+                     SND_SOC_DAIFMT_NB_NF |
+                     SND_SOC_DAIFMT_I2S;

     switch (cpu_dai->id) {
     case PRIMARY_MI2S_RX...QUATERNARY_MI2S_TX:
@@ -154,6 +165,15 @@ static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream,
             snd_soc_dai_set_sysclk(cpu_dai,
                            LPAIF_MI2S_MCLK, mclk_freq,
                            SND_SOC_CLOCK_IN);
+
+        if (data->snd_soc_common_priv->mi2s_bclk_enable) {
+            snd_soc_dai_set_sysclk(cpu_dai,
+                           LPAIF_MI2S_BCLK, bclk_freq,
+                           SND_SOC_CLOCK_IN);
+            snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_BC_FC |
+                        SND_SOC_DAIFMT_NB_NF |
+                        SND_SOC_DAIFMT_I2S);

Rather than hardcoding codec_dai_fmt, it would be better to supply it through board-specific data. This is codec-dependent configuration and not directly related to MCLK handling.

I will add the codec DAI format configuration in the next version in a more generic way, incorporating all your suggestions.

Best Regards,
Rafi

+        }
         break;
     default:
         break;
@@ -288,6 +308,7 @@ static struct snd_soc_common sm8450_priv_data = {
     .dapm_widgets = sc8280xp_dapm_widgets,
     .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets),
     .mi2s_mclk_enable = true,
+    .mi2s_bclk_enable = true,
 };

 static struct snd_soc_common sm8550_priv_data = {
@@ -295,6 +316,7 @@ static struct snd_soc_common sm8550_priv_data = {
     .dapm_widgets = sc8280xp_dapm_widgets,
     .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets),
     .mi2s_mclk_enable = true,
+    .mi2s_bclk_enable = true,
 };

 static struct snd_soc_common sm8650_priv_data = {
@@ -302,6 +324,7 @@ static struct snd_soc_common sm8650_priv_data = {
     .dapm_widgets = sc8280xp_dapm_widgets,
     .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets),
     .mi2s_mclk_enable = true,
+    .mi2s_bclk_enable = true,
 };

 static struct snd_soc_common sm8750_priv_data = {
==============================><========================================

Thanks,
Neil