[PATCH RFC 3/6] ASoC: soc: qcom: sc8280xp: add support for I2S clocks

Neil Armstrong posted 6 patches 2 months ago
There is a newer version of this series
[PATCH RFC 3/6] ASoC: soc: qcom: sc8280xp: add support for I2S clocks
Posted by Neil Armstrong 2 months ago
Add support for getting the I2S clocks used for the MI2S
interfaces, and enable/disable the clocks on the PCM
startup and shutdown card callbacks.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 sound/soc/qcom/sc8280xp.c | 104 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 103 insertions(+), 1 deletion(-)

diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c
index 78e327bc2f07767b1032f09af7f45b947e7eb67a..ad4ee5c6fab8994f18de572842f3dab6f4f5397e 100644
--- a/sound/soc/qcom/sc8280xp.c
+++ b/sound/soc/qcom/sc8280xp.c
@@ -4,6 +4,8 @@
 #include <dt-bindings/sound/qcom,q6afe.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/of_clk.h>
 #include <sound/soc.h>
 #include <sound/soc-dapm.h>
 #include <sound/pcm.h>
@@ -15,12 +17,16 @@
 #include "common.h"
 #include "sdw.h"
 
+#define I2S_MAX_CLKS	5
+
 struct sc8280xp_snd_data {
 	bool stream_prepared[AFE_PORT_MAX];
 	struct snd_soc_card *card;
 	struct sdw_stream_runtime *sruntime[AFE_PORT_MAX];
 	struct snd_soc_jack jack;
 	struct snd_soc_jack dp_jack[8];
+	struct clk *i2s_clk[I2S_MAX_CLKS];
+	struct clk *i2s_mclk[I2S_MAX_CLKS];
 	bool jack_setup;
 };
 
@@ -68,12 +74,66 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd)
 	return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup);
 }
 
+static int sc8280xp_snd_i2s_index(struct snd_soc_dai *dai)
+{
+	switch (dai->id) {
+	case PRIMARY_MI2S_RX..PRIMARY_MI2S_TX:
+		return 0;
+	case  SECONDARY_MI2S_RX.. SECONDARY_MI2S_TX:
+		return 1;
+	case TERTIARY_MI2S_RX..TERTIARY_MI2S_TX:
+		return 2;
+	case QUATERNARY_MI2S_RX..QUATERNARY_MI2S_TX:
+		return 3;
+	case QUINARY_MI2S_RX..QUINARY_MI2S_TX:
+		return 4;
+	default:
+		return -1;
+	}
+}
+
+static int sc8280xp_snd_startup(struct snd_pcm_substream *substream)
+{
+	unsigned int codec_dai_fmt = SND_SOC_DAIFMT_BC_FC | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct sc8280xp_snd_data *pdata = snd_soc_card_get_drvdata(rtd->card);
+	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);
+	int index;
+
+	switch (cpu_dai->id) {
+	case PRIMARY_MI2S_RX...QUATERNARY_MI2S_TX:
+	case QUINARY_MI2S_RX...QUINARY_MI2S_TX:
+		index = sc8280xp_snd_i2s_index(cpu_dai);
+		clk_enable(pdata->i2s_mclk[index]);
+		clk_enable(pdata->i2s_clk[index]);
+		snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt);
+		break;
+	default:
+		break;
+	}
+
+	return qcom_snd_sdw_startup(substream);
+}
+
 static void sc8280xp_snd_shutdown(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 sc8280xp_snd_data *pdata = snd_soc_card_get_drvdata(rtd->card);
 	struct sdw_stream_runtime *sruntime = pdata->sruntime[cpu_dai->id];
+	int index;
+
+	switch (cpu_dai->id) {
+	case PRIMARY_MI2S_RX..TERTIARY_MI2S_RX:
+	case QUINARY_MI2S_RX...QUINARY_MI2S_TX:
+		index = sc8280xp_snd_i2s_index(cpu_dai);
+		clk_disable(pdata->i2s_clk[index]);
+		clk_disable(pdata->i2s_mclk[index]);
+		break;
+	default:
+		break;
+	}
 
 	pdata->sruntime[cpu_dai->id] = NULL;
 	sdw_release_stream(sruntime);
@@ -141,7 +201,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,
+	.startup = sc8280xp_snd_startup,
 	.shutdown = sc8280xp_snd_shutdown,
 	.hw_params = sc8280xp_snd_hw_params,
 	.hw_free = sc8280xp_snd_hw_free,
@@ -162,6 +222,44 @@ static void sc8280xp_add_be_ops(struct snd_soc_card *card)
 	}
 }
 
+static const char * const i2s_bus_names[I2S_MAX_CLKS] = {
+	"primary",
+	"secondary",
+	"tertiary",
+	"quaternary",
+	"quinary",
+};
+
+static int sc8280xp_get_i2c_clocks(struct platform_device *pdev,
+				   struct sc8280xp_snd_data *data)
+{
+	struct device *dev = &pdev->dev;
+	int i;
+
+	if (!device_property_present(dev))
+		return 0;
+
+	for (i = 0; i < I2S_MAX_CLKS; ++i) {
+		char name[16];
+
+		snprintf(name, 16, "%s-mi2s", i2s_bus_names, i);
+		data->i2s_clk[i] = devm_clk_get_optional_prepared(dev, name);
+		if (IS_ERR(data->i2s_clk[i]))
+			return dev_err_probe(dev, PTR_ERR(data->i2s_clk[i]),
+					     "unable to get %s clock\n",
+					     name);
+
+		snprintf(name, 16, "%s-mclk", i2s_bus_names, i);
+		data->i2s_mclk[i] = devm_clk_get_optional_prepared(dev, name);
+		if (IS_ERR(data->i2s_mclk[i]))
+			return dev_err_probe(dev, PTR_ERR(data->i2s_mclk[i]),
+					     "unable to get %s clock\n",
+					     name);
+	}
+
+	return 0;
+}
+
 static int sc8280xp_platform_probe(struct platform_device *pdev)
 {
 	struct snd_soc_card *card;
@@ -185,6 +283,10 @@ static int sc8280xp_platform_probe(struct platform_device *pdev)
 	if (ret)
 		return ret;
 
+	ret = sc8280xp_get_i2c_clocks(pdev, data);
+	if (ret)
+		return ret;
+
 	card->driver_name = of_device_get_match_data(dev);
 	sc8280xp_add_be_ops(card);
 	return devm_snd_soc_register_card(dev, card);

-- 
2.34.1
Re: [PATCH RFC 3/6] ASoC: soc: qcom: sc8280xp: add support for I2S clocks
Posted by Alexey Klimov 2 months ago
On Mon Oct 6, 2025 at 7:37 PM BST, Neil Armstrong wrote:
> Add support for getting the I2S clocks used for the MI2S
> interfaces, and enable/disable the clocks on the PCM
> startup and shutdown card callbacks.
>
> Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
> ---
>  sound/soc/qcom/sc8280xp.c | 104 +++++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 103 insertions(+), 1 deletion(-)
>
> diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c
> index 78e327bc2f07767b1032f09af7f45b947e7eb67a..ad4ee5c6fab8994f18de572842f3dab6f4f5397e 100644
> --- a/sound/soc/qcom/sc8280xp.c
> +++ b/sound/soc/qcom/sc8280xp.c
> @@ -4,6 +4,8 @@
>  #include <dt-bindings/sound/qcom,q6afe.h>
>  #include <linux/module.h>
>  #include <linux/platform_device.h>
> +#include <linux/clk.h>
> +#include <linux/of_clk.h>

^^

[..]

>  static const struct snd_soc_ops sc8280xp_be_ops = {
> -	.startup = qcom_snd_sdw_startup,
> +	.startup = sc8280xp_snd_startup,
>  	.shutdown = sc8280xp_snd_shutdown,
>  	.hw_params = sc8280xp_snd_hw_params,
>  	.hw_free = sc8280xp_snd_hw_free,
> @@ -162,6 +222,44 @@ static void sc8280xp_add_be_ops(struct snd_soc_card *card)
>  	}
>  }
>  
> +static const char * const i2s_bus_names[I2S_MAX_CLKS] = {
> +	"primary",
> +	"secondary",
> +	"tertiary",
> +	"quaternary",
> +	"quinary",
> +};
> +
> +static int sc8280xp_get_i2c_clocks(struct platform_device *pdev,
> +				   struct sc8280xp_snd_data *data)
> +{

Could you please confirm that this should be _i2c_ clocks?

[..]

Best regards,
Alexey
Re: [PATCH RFC 3/6] ASoC: soc: qcom: sc8280xp: add support for I2S clocks
Posted by Neil Armstrong 2 months ago
On 10/7/25 03:35, Alexey Klimov wrote:
> On Mon Oct 6, 2025 at 7:37 PM BST, Neil Armstrong wrote:
>> Add support for getting the I2S clocks used for the MI2S
>> interfaces, and enable/disable the clocks on the PCM
>> startup and shutdown card callbacks.
>>
>> Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
>> ---
>>   sound/soc/qcom/sc8280xp.c | 104 +++++++++++++++++++++++++++++++++++++++++++++-
>>   1 file changed, 103 insertions(+), 1 deletion(-)
>>
>> diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c
>> index 78e327bc2f07767b1032f09af7f45b947e7eb67a..ad4ee5c6fab8994f18de572842f3dab6f4f5397e 100644
>> --- a/sound/soc/qcom/sc8280xp.c
>> +++ b/sound/soc/qcom/sc8280xp.c
>> @@ -4,6 +4,8 @@
>>   #include <dt-bindings/sound/qcom,q6afe.h>
>>   #include <linux/module.h>
>>   #include <linux/platform_device.h>
>> +#include <linux/clk.h>
>> +#include <linux/of_clk.h>
> 
> ^^

I'll fix that

> 
> [..]
> 
>>   static const struct snd_soc_ops sc8280xp_be_ops = {
>> -	.startup = qcom_snd_sdw_startup,
>> +	.startup = sc8280xp_snd_startup,
>>   	.shutdown = sc8280xp_snd_shutdown,
>>   	.hw_params = sc8280xp_snd_hw_params,
>>   	.hw_free = sc8280xp_snd_hw_free,
>> @@ -162,6 +222,44 @@ static void sc8280xp_add_be_ops(struct snd_soc_card *card)
>>   	}
>>   }
>>   
>> +static const char * const i2s_bus_names[I2S_MAX_CLKS] = {
>> +	"primary",
>> +	"secondary",
>> +	"tertiary",
>> +	"quaternary",
>> +	"quinary",
>> +};
>> +
>> +static int sc8280xp_get_i2c_clocks(struct platform_device *pdev,
>> +				   struct sc8280xp_snd_data *data)
>> +{
> 
> Could you please confirm that this should be _i2c_ clocks?

Damn, typo, it should obviously be i2s

Neil

> 
> [..]
> 
> Best regards,
> Alexey
Re: [PATCH RFC 3/6] ASoC: soc: qcom: sc8280xp: add support for I2S clocks
Posted by Srinivas Kandagatla 2 months ago

On 10/6/25 7:37 PM, Neil Armstrong wrote:
> Add support for getting the I2S clocks used for the MI2S
> interfaces, and enable/disable the clocks on the PCM
> startup and shutdown card callbacks.
> 
> Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
> ---
>  sound/soc/qcom/sc8280xp.c | 104 +++++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 103 insertions(+), 1 deletion(-)
> 
> diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c
> index 78e327bc2f07767b1032f09af7f45b947e7eb67a..ad4ee5c6fab8994f18de572842f3dab6f4f5397e 100644
> --- a/sound/soc/qcom/sc8280xp.c
> +++ b/sound/soc/qcom/sc8280xp.c
> @@ -4,6 +4,8 @@
>  #include <dt-bindings/sound/qcom,q6afe.h>
>  #include <linux/module.h>
>  #include <linux/platform_device.h>
> +#include <linux/clk.h>
> +#include <linux/of_clk.h>
>  #include <sound/soc.h>
>  #include <sound/soc-dapm.h>
>  #include <sound/pcm.h>
> @@ -15,12 +17,16 @@
>  #include "common.h"
>  #include "sdw.h"
>  
> +#define I2S_MAX_CLKS	5
> +
>  struct sc8280xp_snd_data {
>  	bool stream_prepared[AFE_PORT_MAX];
>  	struct snd_soc_card *card;
>  	struct sdw_stream_runtime *sruntime[AFE_PORT_MAX];
>  	struct snd_soc_jack jack;
>  	struct snd_soc_jack dp_jack[8];
> +	struct clk *i2s_clk[I2S_MAX_CLKS];
> +	struct clk *i2s_mclk[I2S_MAX_CLKS];
>  	bool jack_setup;
>  };
>  
> @@ -68,12 +74,66 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd)
>  	return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup);
>  }
>  
> +static int sc8280xp_snd_i2s_index(struct snd_soc_dai *dai)
> +{
> +	switch (dai->id) {
> +	case PRIMARY_MI2S_RX..PRIMARY_MI2S_TX:
> +		return 0;
> +	case  SECONDARY_MI2S_RX.. SECONDARY_MI2S_TX:
> +		return 1;
> +	case TERTIARY_MI2S_RX..TERTIARY_MI2S_TX:
> +		return 2;
> +	case QUATERNARY_MI2S_RX..QUATERNARY_MI2S_TX:
> +		return 3;
> +	case QUINARY_MI2S_RX..QUINARY_MI2S_TX:
> +		return 4;
> +	default:
> +		return -1;
> +	}
> +}
> +
> +static int sc8280xp_snd_startup(struct snd_pcm_substream *substream)
> +{
> +	unsigned int codec_dai_fmt = SND_SOC_DAIFMT_BC_FC | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S;
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	struct sc8280xp_snd_data *pdata = snd_soc_card_get_drvdata(rtd->card);
> +	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);
> +	int index;
> +
> +	switch (cpu_dai->id) {
> +	case PRIMARY_MI2S_RX...QUATERNARY_MI2S_TX:
> +	case QUINARY_MI2S_RX...QUINARY_MI2S_TX:
> +		index = sc8280xp_snd_i2s_index(cpu_dai);

What is the mclk and bitclk rate set here, we can not rely on the
default rate.
--srini
> +		clk_enable(pdata->i2s_mclk[index]);
> +		clk_enable(pdata->i2s_clk[index]);
> +		snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt);
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	return qcom_snd_sdw_startup(substream);
> +}
> +
>  static void sc8280xp_snd_shutdown(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 sc8280xp_snd_data *pdata = snd_soc_card_get_drvdata(rtd->card);
>  	struct sdw_stream_runtime *sruntime = pdata->sruntime[cpu_dai->id];
> +	int index;
> +
> +	switch (cpu_dai->id) {
> +	case PRIMARY_MI2S_RX..TERTIARY_MI2S_RX:
> +	case QUINARY_MI2S_RX...QUINARY_MI2S_TX:
> +		index = sc8280xp_snd_i2s_index(cpu_dai);
> +		clk_disable(pdata->i2s_clk[index]);
> +		clk_disable(pdata->i2s_mclk[index]);
> +		break;
> +	default:
> +		break;
> +	}
>  
>  	pdata->sruntime[cpu_dai->id] = NULL;
>  	sdw_release_stream(sruntime);
> @@ -141,7 +201,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,
> +	.startup = sc8280xp_snd_startup,
>  	.shutdown = sc8280xp_snd_shutdown,
>  	.hw_params = sc8280xp_snd_hw_params,
>  	.hw_free = sc8280xp_snd_hw_free,
> @@ -162,6 +222,44 @@ static void sc8280xp_add_be_ops(struct snd_soc_card *card)
>  	}
>  }
>  
> +static const char * const i2s_bus_names[I2S_MAX_CLKS] = {
> +	"primary",
> +	"secondary",
> +	"tertiary",
> +	"quaternary",
> +	"quinary",
> +};
> +
> +static int sc8280xp_get_i2c_clocks(struct platform_device *pdev,
> +				   struct sc8280xp_snd_data *data)
> +{
> +	struct device *dev = &pdev->dev;
> +	int i;
> +
> +	if (!device_property_present(dev))
> +		return 0;
> +
> +	for (i = 0; i < I2S_MAX_CLKS; ++i) {
> +		char name[16];
> +
> +		snprintf(name, 16, "%s-mi2s", i2s_bus_names, i);
> +		data->i2s_clk[i] = devm_clk_get_optional_prepared(dev, name);
> +		if (IS_ERR(data->i2s_clk[i]))
> +			return dev_err_probe(dev, PTR_ERR(data->i2s_clk[i]),
> +					     "unable to get %s clock\n",
> +					     name);
> +
> +		snprintf(name, 16, "%s-mclk", i2s_bus_names, i);
> +		data->i2s_mclk[i] = devm_clk_get_optional_prepared(dev, name);
> +		if (IS_ERR(data->i2s_mclk[i]))
> +			return dev_err_probe(dev, PTR_ERR(data->i2s_mclk[i]),
> +					     "unable to get %s clock\n",
> +					     name);
> +	}
> +
> +	return 0;
> +}
> +
>  static int sc8280xp_platform_probe(struct platform_device *pdev)
>  {
>  	struct snd_soc_card *card;
> @@ -185,6 +283,10 @@ static int sc8280xp_platform_probe(struct platform_device *pdev)
>  	if (ret)
>  		return ret;
>  
> +	ret = sc8280xp_get_i2c_clocks(pdev, data);
> +	if (ret)
> +		return ret;
> +
>  	card->driver_name = of_device_get_match_data(dev);
>  	sc8280xp_add_be_ops(card);
>  	return devm_snd_soc_register_card(dev, card);
>
Re: [PATCH RFC 3/6] ASoC: soc: qcom: sc8280xp: add support for I2S clocks
Posted by Neil Armstrong 2 months ago
On 10/7/25 00:21, Srinivas Kandagatla wrote:
> 
> 
> On 10/6/25 7:37 PM, Neil Armstrong wrote:
>> Add support for getting the I2S clocks used for the MI2S
>> interfaces, and enable/disable the clocks on the PCM
>> startup and shutdown card callbacks.
>>
>> Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
>> ---
>>   sound/soc/qcom/sc8280xp.c | 104 +++++++++++++++++++++++++++++++++++++++++++++-
>>   1 file changed, 103 insertions(+), 1 deletion(-)
>>
>> diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c
>> index 78e327bc2f07767b1032f09af7f45b947e7eb67a..ad4ee5c6fab8994f18de572842f3dab6f4f5397e 100644
>> --- a/sound/soc/qcom/sc8280xp.c
>> +++ b/sound/soc/qcom/sc8280xp.c
>> @@ -4,6 +4,8 @@
>>   #include <dt-bindings/sound/qcom,q6afe.h>
>>   #include <linux/module.h>
>>   #include <linux/platform_device.h>
>> +#include <linux/clk.h>
>> +#include <linux/of_clk.h>
>>   #include <sound/soc.h>
>>   #include <sound/soc-dapm.h>
>>   #include <sound/pcm.h>
>> @@ -15,12 +17,16 @@
>>   #include "common.h"
>>   #include "sdw.h"
>>   
>> +#define I2S_MAX_CLKS	5
>> +
>>   struct sc8280xp_snd_data {
>>   	bool stream_prepared[AFE_PORT_MAX];
>>   	struct snd_soc_card *card;
>>   	struct sdw_stream_runtime *sruntime[AFE_PORT_MAX];
>>   	struct snd_soc_jack jack;
>>   	struct snd_soc_jack dp_jack[8];
>> +	struct clk *i2s_clk[I2S_MAX_CLKS];
>> +	struct clk *i2s_mclk[I2S_MAX_CLKS];
>>   	bool jack_setup;
>>   };
>>   
>> @@ -68,12 +74,66 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd)
>>   	return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup);
>>   }
>>   
>> +static int sc8280xp_snd_i2s_index(struct snd_soc_dai *dai)
>> +{
>> +	switch (dai->id) {
>> +	case PRIMARY_MI2S_RX..PRIMARY_MI2S_TX:
>> +		return 0;
>> +	case  SECONDARY_MI2S_RX.. SECONDARY_MI2S_TX:
>> +		return 1;
>> +	case TERTIARY_MI2S_RX..TERTIARY_MI2S_TX:
>> +		return 2;
>> +	case QUATERNARY_MI2S_RX..QUATERNARY_MI2S_TX:
>> +		return 3;
>> +	case QUINARY_MI2S_RX..QUINARY_MI2S_TX:
>> +		return 4;
>> +	default:
>> +		return -1;
>> +	}
>> +}
>> +
>> +static int sc8280xp_snd_startup(struct snd_pcm_substream *substream)
>> +{
>> +	unsigned int codec_dai_fmt = SND_SOC_DAIFMT_BC_FC | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S;
>> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
>> +	struct sc8280xp_snd_data *pdata = snd_soc_card_get_drvdata(rtd->card);
>> +	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);
>> +	int index;
>> +
>> +	switch (cpu_dai->id) {
>> +	case PRIMARY_MI2S_RX...QUATERNARY_MI2S_TX:
>> +	case QUINARY_MI2S_RX...QUINARY_MI2S_TX:
>> +		index = sc8280xp_snd_i2s_index(cpu_dai);
> 
> What is the mclk and bitclk rate set here, we can not rely on the
> default rate.

The default rates are set in DT:
+		assigned-clocks = <&q6prmcc LPASS_CLK_ID_PRI_MI2S_IBIT LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
+				  <&q6prmcc LPASS_CLK_ID_MCLK_1 LPASS_CLK_ATTRIBUTE_COUPLE_NO>;
+		assigned-clock-rates = <1536000>,
+				       <24576000>;

Neil

> --srini
>> +		clk_enable(pdata->i2s_mclk[index]);
>> +		clk_enable(pdata->i2s_clk[index]);
>> +		snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt);
>> +		break;
>> +	default:
>> +		break;
>> +	}
>> +
>> +	return qcom_snd_sdw_startup(substream);
>> +}
>> +
>>   static void sc8280xp_snd_shutdown(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 sc8280xp_snd_data *pdata = snd_soc_card_get_drvdata(rtd->card);
>>   	struct sdw_stream_runtime *sruntime = pdata->sruntime[cpu_dai->id];
>> +	int index;
>> +
>> +	switch (cpu_dai->id) {
>> +	case PRIMARY_MI2S_RX..TERTIARY_MI2S_RX:
>> +	case QUINARY_MI2S_RX...QUINARY_MI2S_TX:
>> +		index = sc8280xp_snd_i2s_index(cpu_dai);
>> +		clk_disable(pdata->i2s_clk[index]);
>> +		clk_disable(pdata->i2s_mclk[index]);
>> +		break;
>> +	default:
>> +		break;
>> +	}
>>   
>>   	pdata->sruntime[cpu_dai->id] = NULL;
>>   	sdw_release_stream(sruntime);
>> @@ -141,7 +201,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,
>> +	.startup = sc8280xp_snd_startup,
>>   	.shutdown = sc8280xp_snd_shutdown,
>>   	.hw_params = sc8280xp_snd_hw_params,
>>   	.hw_free = sc8280xp_snd_hw_free,
>> @@ -162,6 +222,44 @@ static void sc8280xp_add_be_ops(struct snd_soc_card *card)
>>   	}
>>   }
>>   
>> +static const char * const i2s_bus_names[I2S_MAX_CLKS] = {
>> +	"primary",
>> +	"secondary",
>> +	"tertiary",
>> +	"quaternary",
>> +	"quinary",
>> +};
>> +
>> +static int sc8280xp_get_i2c_clocks(struct platform_device *pdev,
>> +				   struct sc8280xp_snd_data *data)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	int i;
>> +
>> +	if (!device_property_present(dev))
>> +		return 0;
>> +
>> +	for (i = 0; i < I2S_MAX_CLKS; ++i) {
>> +		char name[16];
>> +
>> +		snprintf(name, 16, "%s-mi2s", i2s_bus_names, i);
>> +		data->i2s_clk[i] = devm_clk_get_optional_prepared(dev, name);
>> +		if (IS_ERR(data->i2s_clk[i]))
>> +			return dev_err_probe(dev, PTR_ERR(data->i2s_clk[i]),
>> +					     "unable to get %s clock\n",
>> +					     name);
>> +
>> +		snprintf(name, 16, "%s-mclk", i2s_bus_names, i);
>> +		data->i2s_mclk[i] = devm_clk_get_optional_prepared(dev, name);
>> +		if (IS_ERR(data->i2s_mclk[i]))
>> +			return dev_err_probe(dev, PTR_ERR(data->i2s_mclk[i]),
>> +					     "unable to get %s clock\n",
>> +					     name);
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>>   static int sc8280xp_platform_probe(struct platform_device *pdev)
>>   {
>>   	struct snd_soc_card *card;
>> @@ -185,6 +283,10 @@ static int sc8280xp_platform_probe(struct platform_device *pdev)
>>   	if (ret)
>>   		return ret;
>>   
>> +	ret = sc8280xp_get_i2c_clocks(pdev, data);
>> +	if (ret)
>> +		return ret;
>> +
>>   	card->driver_name = of_device_get_match_data(dev);
>>   	sc8280xp_add_be_ops(card);
>>   	return devm_snd_soc_register_card(dev, card);
>>
>
Re: [PATCH RFC 3/6] ASoC: soc: qcom: sc8280xp: add support for I2S clocks
Posted by Konrad Dybcio 2 months ago
On 10/7/25 9:02 AM, Neil Armstrong wrote:
> On 10/7/25 00:21, Srinivas Kandagatla wrote:
>>
>>
>> On 10/6/25 7:37 PM, Neil Armstrong wrote:
>>> Add support for getting the I2S clocks used for the MI2S
>>> interfaces, and enable/disable the clocks on the PCM
>>> startup and shutdown card callbacks.
>>>
>>> Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
>>> ---

[...]

>>> +static int sc8280xp_snd_startup(struct snd_pcm_substream *substream)
>>> +{
>>> +    unsigned int codec_dai_fmt = SND_SOC_DAIFMT_BC_FC | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S;
>>> +    struct snd_soc_pcm_runtime *rtd = substream->private_data;
>>> +    struct sc8280xp_snd_data *pdata = snd_soc_card_get_drvdata(rtd->card);
>>> +    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);
>>> +    int index;
>>> +
>>> +    switch (cpu_dai->id) {
>>> +    case PRIMARY_MI2S_RX...QUATERNARY_MI2S_TX:
>>> +    case QUINARY_MI2S_RX...QUINARY_MI2S_TX:
>>> +        index = sc8280xp_snd_i2s_index(cpu_dai);
>>
>> What is the mclk and bitclk rate set here, we can not rely on the
>> default rate.
> 
> The default rates are set in DT:
> +        assigned-clocks = <&q6prmcc LPASS_CLK_ID_PRI_MI2S_IBIT LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
> +                  <&q6prmcc LPASS_CLK_ID_MCLK_1 LPASS_CLK_ATTRIBUTE_COUPLE_NO>;
> +        assigned-clock-rates = <1536000>,
> +                       <24576000>;

Is there a way to infer these rates based on the DT audio
connection graph?

Konrad
Re: [PATCH RFC 3/6] ASoC: soc: qcom: sc8280xp: add support for I2S clocks
Posted by Neil Armstrong 2 months ago
On 10/7/25 12:23, Konrad Dybcio wrote:
> On 10/7/25 9:02 AM, Neil Armstrong wrote:
>> On 10/7/25 00:21, Srinivas Kandagatla wrote:
>>>
>>>
>>> On 10/6/25 7:37 PM, Neil Armstrong wrote:
>>>> Add support for getting the I2S clocks used for the MI2S
>>>> interfaces, and enable/disable the clocks on the PCM
>>>> startup and shutdown card callbacks.
>>>>
>>>> Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
>>>> ---
> 
> [...]
> 
>>>> +static int sc8280xp_snd_startup(struct snd_pcm_substream *substream)
>>>> +{
>>>> +    unsigned int codec_dai_fmt = SND_SOC_DAIFMT_BC_FC | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S;
>>>> +    struct snd_soc_pcm_runtime *rtd = substream->private_data;
>>>> +    struct sc8280xp_snd_data *pdata = snd_soc_card_get_drvdata(rtd->card);
>>>> +    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);
>>>> +    int index;
>>>> +
>>>> +    switch (cpu_dai->id) {
>>>> +    case PRIMARY_MI2S_RX...QUATERNARY_MI2S_TX:
>>>> +    case QUINARY_MI2S_RX...QUINARY_MI2S_TX:
>>>> +        index = sc8280xp_snd_i2s_index(cpu_dai);
>>>
>>> What is the mclk and bitclk rate set here, we can not rely on the
>>> default rate.
>>
>> The default rates are set in DT:
>> +        assigned-clocks = <&q6prmcc LPASS_CLK_ID_PRI_MI2S_IBIT LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
>> +                  <&q6prmcc LPASS_CLK_ID_MCLK_1 LPASS_CLK_ATTRIBUTE_COUPLE_NO>;
>> +        assigned-clock-rates = <1536000>,
>> +                       <24576000>;
> 
> Is there a way to infer these rates based on the DT audio
> connection graph?

Good question, it's not done for pre-audioreach. Let me investigate.

Neil

> 
> Konrad

Re: [PATCH RFC 3/6] ASoC: soc: qcom: sc8280xp: add support for I2S clocks
Posted by Neil Armstrong 1 month, 4 weeks ago
On 10/7/25 17:48, Neil Armstrong wrote:
> On 10/7/25 12:23, Konrad Dybcio wrote:
>> On 10/7/25 9:02 AM, Neil Armstrong wrote:
>>> On 10/7/25 00:21, Srinivas Kandagatla wrote:
>>>>
>>>>
>>>> On 10/6/25 7:37 PM, Neil Armstrong wrote:
>>>>> Add support for getting the I2S clocks used for the MI2S
>>>>> interfaces, and enable/disable the clocks on the PCM
>>>>> startup and shutdown card callbacks.
>>>>>
>>>>> Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
>>>>> ---
>>
>> [...]
>>
>>>>> +static int sc8280xp_snd_startup(struct snd_pcm_substream *substream)
>>>>> +{
>>>>> +    unsigned int codec_dai_fmt = SND_SOC_DAIFMT_BC_FC | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S;
>>>>> +    struct snd_soc_pcm_runtime *rtd = substream->private_data;
>>>>> +    struct sc8280xp_snd_data *pdata = snd_soc_card_get_drvdata(rtd->card);
>>>>> +    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);
>>>>> +    int index;
>>>>> +
>>>>> +    switch (cpu_dai->id) {
>>>>> +    case PRIMARY_MI2S_RX...QUATERNARY_MI2S_TX:
>>>>> +    case QUINARY_MI2S_RX...QUINARY_MI2S_TX:
>>>>> +        index = sc8280xp_snd_i2s_index(cpu_dai);
>>>>
>>>> What is the mclk and bitclk rate set here, we can not rely on the
>>>> default rate.
>>>
>>> The default rates are set in DT:
>>> +        assigned-clocks = <&q6prmcc LPASS_CLK_ID_PRI_MI2S_IBIT LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
>>> +                  <&q6prmcc LPASS_CLK_ID_MCLK_1 LPASS_CLK_ATTRIBUTE_COUPLE_NO>;
>>> +        assigned-clock-rates = <1536000>,
>>> +                       <24576000>;
>>
>> Is there a way to infer these rates based on the DT audio
>> connection graph?
> 
> Good question, it's not done for pre-audioreach. Let me investigate.

OK so since we have fixed 48KHz stereo setup, we can hardcode those frequencies in the card...

FS is 512 and the MCLK / BCLK is 16, as standard so I can calculate those.

Will drop the DT rates and do it in the card.

Neil

> 
> Neil
> 
>>
>> Konrad
>