From: Mohammad Rafi Shaik <mohammad.rafi.shaik@oss.qualcomm.com>
To: Srinivas Kandagatla <srini@kernel.org>,
Liam Girdwood <lgirdwood@gmail.com>,
Mark Brown <broonie@kernel.org>, Rob Herring <robh@kernel.org>,
Krzysztof Kozlowski <krzk+dt@kernel.org>,
Conor Dooley <conor+dt@kernel.org>,
Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.com>
Cc: Krzysztof Kozlowski <krzk@kernel.org>,
linux-arm-msm@vger.kernel.org, linux-sound@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
Neil Armstrong <neil.armstrong@linaro.org>
Subject: [PATCH v3 3/3] ASoC: qcom: sc8280xp: ASoC: qcom: sc8280xp: enhance machine driver for board-specific config
Date: Mon, 6 Jul 2026 18:50:09 +0530 [thread overview]
Message-ID: <20260706132009.1496321-4-mohammad.rafi.shaik@oss.qualcomm.com> (raw)
In-Reply-To: <20260706132009.1496321-1-mohammad.rafi.shaik@oss.qualcomm.com>
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.
Tested-by: Neil Armstrong <neil.armstrong@linaro.org>
Signed-off-by: Mohammad Rafi Shaik <mohammad.rafi.shaik@oss.qualcomm.com>
---
sound/soc/qcom/sc8280xp.c | 245 ++++++++++++++++++++++++++++++++++----
1 file changed, 225 insertions(+), 20 deletions(-)
diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c
index 7925aa3f63ba..b13f39dc5afc 100644
--- a/sound/soc/qcom/sc8280xp.c
+++ b/sound/soc/qcom/sc8280xp.c
@@ -12,17 +12,78 @@
#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 I2S_MCLKFS 256
+
+#define I2S_MCLK_RATE(rate) \
+ ((rate) * (I2S_MCLKFS))
+#define I2S_BIT_RATE(rate, channels, format) \
+ ((rate) * (channels) * (format))
+
+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 {
+ const 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;
+ const struct snd_kcontrol_new *controls;
+ int num_controls;
+ unsigned int codec_dai_fmt;
+ bool codec_sysclk_set;
+ bool mi2s_mclk_enable;
+ bool mi2s_bclk_enable;
+ bool wcd_jack;
+};
+
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_freq(struct snd_pcm_hw_params *params)
+{
+ int rate = params_rate(params);
+
+ switch (rate) {
+ case 11025:
+ case 44100:
+ case 88200:
+ return I2S_MCLK_RATE(44100);
+ default:
+ break;
+ }
+
+ return I2S_MCLK_RATE(rate);
+}
+
+static inline int sc8280xp_get_bclk_freq(struct snd_pcm_hw_params *params)
+{
+ return I2S_BIT_RATE(params_rate(params),
+ params_channels(params),
+ snd_pcm_format_width(params_format(params)));
+}
+
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 +93,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:
/*
@@ -64,7 +121,10 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd)
if (dp_jack)
return qcom_snd_dp_jack_setup(rtd, dp_jack, dp_pcm_id);
- return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup);
+ if (data->snd_soc_common_priv->wcd_jack)
+ return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup);
+
+ return 0;
}
static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
@@ -96,6 +156,63 @@ 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 *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ 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_freq(params);
+ int bclk_freq = sc8280xp_get_bclk_freq(params);
+ int ret;
+
+ switch (cpu_dai->id) {
+ case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX:
+ case QUINARY_MI2S_RX ... QUINARY_MI2S_TX:
+ case SENARY_MI2S_RX ... SENARY_MI2S_TX:
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP);
+ if (ret && ret != -ENOTSUPP)
+ return ret;
+
+ if (data->snd_soc_common_priv->codec_dai_fmt) {
+ ret = snd_soc_dai_set_fmt(codec_dai,
+ data->snd_soc_common_priv->codec_dai_fmt);
+ if (ret && ret != -ENOTSUPP)
+ return ret;
+ }
+
+ if (data->snd_soc_common_priv->mi2s_mclk_enable) {
+ ret = snd_soc_dai_set_sysclk(cpu_dai,
+ LPAIF_MI2S_MCLK, mclk_freq,
+ SND_SOC_CLOCK_OUT);
+ if (ret)
+ return ret;
+ }
+
+ if (data->snd_soc_common_priv->mi2s_bclk_enable) {
+ ret = snd_soc_dai_set_sysclk(cpu_dai,
+ LPAIF_MI2S_BCLK, bclk_freq,
+ SND_SOC_CLOCK_OUT);
+ if (ret)
+ return ret;
+ }
+
+ if (data->snd_soc_common_priv->codec_sysclk_set) {
+ ret = snd_soc_dai_set_sysclk(codec_dai,
+ 0, mclk_freq,
+ SND_SOC_CLOCK_IN);
+ if (ret)
+ return ret;
+ }
+ 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 +234,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,
};
@@ -127,7 +245,7 @@ static void sc8280xp_add_be_ops(struct snd_soc_card *card)
int i;
for_each_card_prelinks(card, i, link) {
- if (link->no_pcm == 1) {
+ if (link->no_pcm == 1 || link->num_codecs > 0) {
link->init = sc8280xp_snd_init;
link->be_hw_params_fixup = sc8280xp_be_hw_params_fixup;
link->ops = &sc8280xp_be_ops;
@@ -145,37 +263,124 @@ 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 -ENODEV;
+
+ 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;
+ card->controls = data->snd_soc_common_priv->controls;
+ card->num_controls = data->snd_soc_common_priv->num_controls;
+
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),
+ .wcd_jack = true,
+};
+
+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),
+ .wcd_jack = true,
+};
+
+static struct snd_soc_common qcs6490_priv_data = {
+ .driver_name = "qcs6490",
+ .dapm_widgets = sc8280xp_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets),
+ .wcd_jack = true,
+};
+
+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),
+ .wcd_jack = true,
+};
+
+static struct snd_soc_common sm8450_priv_data = {
+ .driver_name = "sm8450",
+ .dapm_widgets = sc8280xp_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets),
+ .wcd_jack = true,
+};
+
+static struct snd_soc_common sm8550_priv_data = {
+ .driver_name = "sm8550",
+ .dapm_widgets = sc8280xp_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets),
+ .wcd_jack = true,
+};
+
+static struct snd_soc_common sm8650_priv_data = {
+ .driver_name = "sm8650",
+ .dapm_widgets = sc8280xp_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets),
+ .wcd_jack = true,
+};
+
+static struct snd_soc_common sm8750_priv_data = {
+ .driver_name = "sm8750",
+ .dapm_widgets = sc8280xp_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets),
+ .wcd_jack = true,
+};
+
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},
{}
};
--
2.34.1
next prev parent reply other threads:[~2026-07-06 13:20 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-06 13:20 [PATCH v3 0/3] ASoC: qcom: qdsp6: Add MI2S clock control Mohammad Rafi Shaik
2026-07-06 13:20 ` [PATCH v3 1/3] ASoC: dt-bindings: qcom,q6apm-lpass-dais: Document DAI subnode Mohammad Rafi Shaik
2026-07-06 13:30 ` sashiko-bot
2026-07-06 13:20 ` [PATCH v3 2/3] ASoC: qcom: q6apm-lpass-dais: Add MI2S clock control Mohammad Rafi Shaik
2026-07-06 13:33 ` sashiko-bot
2026-07-06 18:18 ` Mark Brown
2026-07-06 13:20 ` Mohammad Rafi Shaik [this message]
2026-07-06 13:34 ` [PATCH v3 3/3] ASoC: qcom: sc8280xp: ASoC: qcom: sc8280xp: enhance machine driver for board-specific config sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260706132009.1496321-4-mohammad.rafi.shaik@oss.qualcomm.com \
--to=mohammad.rafi.shaik@oss.qualcomm.com \
--cc=broonie@kernel.org \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=krzk+dt@kernel.org \
--cc=krzk@kernel.org \
--cc=lgirdwood@gmail.com \
--cc=linux-arm-msm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-sound@vger.kernel.org \
--cc=neil.armstrong@linaro.org \
--cc=perex@perex.cz \
--cc=robh@kernel.org \
--cc=srini@kernel.org \
--cc=tiwai@suse.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox