Re: [PATCH v1 4/4] ASoC: qcom: sc8280xp: ASoC: qcom: sc8280xp: enhance machine driver for board-specific config

From: Mohammad Rafi Shaik

Date: Tue Mar 17 2026 - 01:35:26 EST




On 3/10/2026 5:14 PM, Neil Armstrong wrote:
Hi,

On 3/9/26 12:13, Mohammad Rafi Shaik wrote:
The sc8280xp machine driver is currently written with a largely
SoC-centric view and assumes a uniform audio topology across all boards.
In practice, multiple products based on the same SoC use different board
designs and external audio components, which require board-specific
configuration to function correctly.

Several Qualcomm platforms like talos integrate third-party audio codecs
or use different external audio paths. These designs often require
additional configuration such as explicit MI2S MCLK settings for audio
to work.

This change enhances the sc8280xp machine driver to support board- specific
configuration such as allowing each board variant to provide its own DAPM
widgets and routes, reflecting the actual audio components and connectors
present and enabling MI2S MCLK programming for boards that use external
codecs requiring a stable master clock.

Signed-off-by: Mohammad Rafi Shaik <mohammad.rafi.shaik@xxxxxxxxxxxxxxxx>
---
  sound/soc/qcom/sc8280xp.c | 180 ++++++++++++++++++++++++++++++++++----
  1 file changed, 162 insertions(+), 18 deletions(-)

diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c
index 7925aa3f6..c2e9323df 100644
--- a/sound/soc/qcom/sc8280xp.c
+++ b/sound/soc/qcom/sc8280xp.c
@@ -12,17 +12,62 @@
  #include <sound/jack.h>
  #include <linux/input-event-codes.h>
  #include "qdsp6/q6afe.h"
+#include "qdsp6/q6apm.h"
+#include "qdsp6/q6prm.h"
  #include "common.h"
  #include "sdw.h"
+#define MCLK_FREQ        12288000
+#define MCLK_NATIVE_FREQ    11289600

Where does thos values come from ?


Thanks for the review,

These MCLK rates are default settings in Qualcomm platforms.
so hard codec thes rates

MCLK_FREQ        12288000 == 48000 * 256
MCLK_NATIVE_FREQ    11289600 == 44100 * 256

This is highly specific to a board, the sdm845.c uses:

#define DEFAULT_MCLK_RATE    24576000 == 48000 * 2 * 256
#define MI2S_BCLK_RATE        1536000 == 48000 * 2 * 16

And those are the values we use to output sound on HDMI on all HDK and Dragonboards.

I guess this should be configurable somewhere !


Thanks for the detailed rate calculations. I’ll consider the suggestions above and work toward a more generic and flexible approach for configuring clock rates.

Best Regards,
Rafi

+
+static struct snd_soc_dapm_widget sc8280xp_dapm_widgets[] = {
+    SND_SOC_DAPM_HP("Headphone Jack", NULL),
+    SND_SOC_DAPM_MIC("Mic Jack", NULL),
+    SND_SOC_DAPM_SPK("DP0 Jack", NULL),
+    SND_SOC_DAPM_SPK("DP1 Jack", NULL),
+    SND_SOC_DAPM_SPK("DP2 Jack", NULL),
+    SND_SOC_DAPM_SPK("DP3 Jack", NULL),
+    SND_SOC_DAPM_SPK("DP4 Jack", NULL),
+    SND_SOC_DAPM_SPK("DP5 Jack", NULL),
+    SND_SOC_DAPM_SPK("DP6 Jack", NULL),
+    SND_SOC_DAPM_SPK("DP7 Jack", NULL),
+};
+
+struct snd_soc_common {
+    char *driver_name;
+    const struct snd_soc_dapm_widget *dapm_widgets;
+    int num_dapm_widgets;
+    const struct snd_soc_dapm_route *dapm_routes;
+    int num_dapm_routes;
+    bool mi2s_mclk_enable;
+};
+
  struct sc8280xp_snd_data {
      bool stream_prepared[AFE_PORT_MAX];
      struct snd_soc_card *card;
      struct snd_soc_jack jack;
      struct snd_soc_jack dp_jack[8];
+    struct snd_soc_common *snd_soc_common_priv;
      bool jack_setup;
  };
+static inline int sc8280xp_get_mclk_feq(unsigned int rate)

                _freq

+{
+    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;
+        break;
+    default:
+        break;
+    }
+
+    return freq;
+}
+
  static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd)
  {
      struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd- >card);
@@ -32,10 +77,6 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd)
      int dp_pcm_id = 0;
      switch (cpu_dai->id) {
-    case PRIMARY_MI2S_RX...QUATERNARY_MI2S_TX:
-    case QUINARY_MI2S_RX...QUINARY_MI2S_TX:
-        snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP);
-        break;
      case WSA_CODEC_DMA_RX_0:
      case WSA_CODEC_DMA_RX_1:
          /*
@@ -96,6 +137,31 @@ static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
      return 0;
  }
+static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream,
+                  struct snd_pcm_hw_params *params)
+{
+    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 sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd- >card);
+    int mclk_freq = sc8280xp_get_mclk_feq(params_rate(params));
+
+    switch (cpu_dai->id) {
+    case PRIMARY_MI2S_RX...QUATERNARY_MI2S_TX:
+    case QUINARY_MI2S_RX...QUINARY_MI2S_TX:
+        snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP);
+
+        if (data->snd_soc_common_priv->mi2s_mclk_enable)
+            snd_soc_dai_set_sysclk(cpu_dai,
+                           LPAIF_MI2S_MCLK, mclk_freq,
+                           SND_SOC_CLOCK_IN);
+        break;
+    default:
+        break;
+    };
+
+    return 0;
+}
+
  static int sc8280xp_snd_prepare(struct snd_pcm_substream *substream)
  {
      struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
@@ -117,6 +183,7 @@ static int sc8280xp_snd_hw_free(struct snd_pcm_substream *substream)
  static const struct snd_soc_ops sc8280xp_be_ops = {
      .startup = qcom_snd_sdw_startup,
      .shutdown = qcom_snd_sdw_shutdown,
+    .hw_params = sc8280xp_snd_hw_params,
      .hw_free = sc8280xp_snd_hw_free,
      .prepare = sc8280xp_snd_prepare,
  };
@@ -145,37 +212,114 @@ static int sc8280xp_platform_probe(struct platform_device *pdev)
      card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
      if (!card)
          return -ENOMEM;
-    card->owner = THIS_MODULE;
+
      /* Allocate the private data */
      data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
      if (!data)
          return -ENOMEM;
+    data->snd_soc_common_priv = (struct snd_soc_common *)of_device_get_match_data(dev);
+    if (!data->snd_soc_common_priv)
+        return -ENOMEM;
+
+    card->owner = THIS_MODULE;
      card->dev = dev;
      dev_set_drvdata(dev, card);
      snd_soc_card_set_drvdata(card, data);
+    card->dapm_widgets = data->snd_soc_common_priv->dapm_widgets;
+    card->num_dapm_widgets = data->snd_soc_common_priv- >num_dapm_widgets;
+    card->dapm_routes = data->snd_soc_common_priv->dapm_routes;
+    card->num_dapm_routes = data->snd_soc_common_priv->num_dapm_routes;
+
      ret = qcom_snd_parse_of(card);
      if (ret)
          return ret;
-    card->driver_name = of_device_get_match_data(dev);
+    card->driver_name = data->snd_soc_common_priv->driver_name;
      sc8280xp_add_be_ops(card);
      return devm_snd_soc_register_card(dev, card);
  }
+static struct snd_soc_common kaanapali_priv_data = {
+    .driver_name = "kaanapali",
+    .dapm_widgets = sc8280xp_dapm_widgets,
+    .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets),
+};
+
+static struct snd_soc_common qcs9100_priv_data = {
+    .driver_name = "sa8775p",
+    .dapm_widgets = sc8280xp_dapm_widgets,
+    .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets),
+};
+
+static struct snd_soc_common qcs615_priv_data = {
+    .driver_name = "qcs615",
+    .dapm_widgets = sc8280xp_dapm_widgets,
+    .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets),
+    .mi2s_mclk_enable = true,
+};
+
+static struct snd_soc_common qcm6490_priv_data = {
+    .driver_name = "qcm6490",
+    .dapm_widgets = sc8280xp_dapm_widgets,
+    .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets),
+};
+
+static struct snd_soc_common qcs6490_priv_data = {
+    .driver_name = "qcs6490",
+    .dapm_widgets = sc8280xp_dapm_widgets,
+    .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets),
+};
+
+static struct snd_soc_common qcs8275_priv_data = {
+    .driver_name = "qcs8300",
+    .dapm_widgets = sc8280xp_dapm_widgets,
+    .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets),
+};
+
+static struct snd_soc_common sc8280xp_priv_data = {
+    .driver_name = "sc8280xp",
+    .dapm_widgets = sc8280xp_dapm_widgets,
+    .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets),
+};
+
+static struct snd_soc_common sm8450_priv_data = {
+    .driver_name = "sm8450",
+    .dapm_widgets = sc8280xp_dapm_widgets,
+    .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets),
+};
+
+static struct snd_soc_common sm8550_priv_data = {
+    .driver_name = "sm8550",
+    .dapm_widgets = sc8280xp_dapm_widgets,
+    .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets),
+};
+
+static struct snd_soc_common sm8650_priv_data = {
+    .driver_name = "sm8650",
+    .dapm_widgets = sc8280xp_dapm_widgets,
+    .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets),
+};
+
+static struct snd_soc_common sm8750_priv_data = {
+    .driver_name = "sm8750",
+    .dapm_widgets = sc8280xp_dapm_widgets,
+    .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets),
+};
+
  static const struct of_device_id snd_sc8280xp_dt_match[] = {
-    {.compatible = "qcom,kaanapali-sndcard", "kaanapali"},
-    {.compatible = "qcom,qcm6490-idp-sndcard", "qcm6490"},
-    {.compatible = "qcom,qcs615-sndcard", "qcs615"},
-    {.compatible = "qcom,qcs6490-rb3gen2-sndcard", "qcs6490"},
-    {.compatible = "qcom,qcs8275-sndcard", "qcs8300"},
-    {.compatible = "qcom,qcs9075-sndcard", "sa8775p"},
-    {.compatible = "qcom,qcs9100-sndcard", "sa8775p"},
-    {.compatible = "qcom,sc8280xp-sndcard", "sc8280xp"},
-    {.compatible = "qcom,sm8450-sndcard", "sm8450"},
-    {.compatible = "qcom,sm8550-sndcard", "sm8550"},
-    {.compatible = "qcom,sm8650-sndcard", "sm8650"},
-    {.compatible = "qcom,sm8750-sndcard", "sm8750"},
+    {.compatible = "qcom,kaanapali-sndcard", .data = &kaanapali_priv_data},
+    {.compatible = "qcom,qcm6490-idp-sndcard", .data = &qcm6490_priv_data},
+    {.compatible = "qcom,qcs615-sndcard", .data = &qcs615_priv_data},
+    {.compatible = "qcom,qcs6490-rb3gen2-sndcard", .data = &qcs6490_priv_data},
+    {.compatible = "qcom,qcs8275-sndcard", .data = &qcs8275_priv_data},
+    {.compatible = "qcom,qcs9075-sndcard", .data = &qcs9100_priv_data},
+    {.compatible = "qcom,qcs9100-sndcard", .data = &qcs9100_priv_data},
+    {.compatible = "qcom,sc8280xp-sndcard", .data = &sc8280xp_priv_data},
+    {.compatible = "qcom,sm8450-sndcard", .data = &sm8450_priv_data},
+    {.compatible = "qcom,sm8550-sndcard", .data = &sm8550_priv_data},
+    {.compatible = "qcom,sm8650-sndcard", .data = &sm8650_priv_data},
+    {.compatible = "qcom,sm8750-sndcard", .data = &sm8750_priv_data},
      {}
  };