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
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
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
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);
>
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);
>>
>
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
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
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
>
© 2016 - 2025 Red Hat, Inc.