diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml index f224e84919cdb..2cfbc9ddbc2c7 100644 --- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml @@ -33,6 +33,7 @@ properties: - fairphone,fp5-sndcard - qcom,apq8096-sndcard - qcom,glymur-sndcard + - qcom,nord-ride-sndcard - qcom,qcm6490-idp-sndcard - qcom,qcs615-sndcard - qcom,qcs6490-rb3gen2-sndcard diff --git a/arch/arm64/boot/dts/qcom/nord-embedded.dtsi b/arch/arm64/boot/dts/qcom/nord-embedded.dtsi index 0d1c561b1f9e0..2fa59bb14b264 100644 --- a/arch/arm64/boot/dts/qcom/nord-embedded.dtsi +++ b/arch/arm64/boot/dts/qcom/nord-embedded.dtsi @@ -1403,6 +1403,43 @@ }; &tlmm { + aud_intfc8_mi2s_active: aud-intfc8-mi2s-active-state { + data-pins { + pins = "gpio75", "gpio76"; + function = "aud_intfc8_data"; + drive-strength = <8>; + bias-disable; + }; + + sclk-pins { + pins = "gpio73"; + function = "aud_intfc8_clk"; + drive-strength = <8>; + bias-disable; + }; + + ws-pins { + pins = "gpio74"; + function = "aud_intfc8_ws"; + drive-strength = <8>; + bias-disable; + }; + }; + + aud_mclk0_mira_active: aud-mclk0-mira-active-state { + pins = "gpio99"; + function = "aud_mclk0_mira"; + drive-strength = <8>; + bias-disable; + }; + + aud_mclk1_mira_active: aud-mclk1-mira-active-state { + pins = "gpio100"; + function = "aud_mclk1_mira"; + drive-strength = <8>; + bias-disable; + }; + qup_i2c0_default: qup-i2c0-default-state { pins = "gpio111", "gpio112"; function = "qup0_se0"; diff --git a/arch/arm64/boot/dts/qcom/nord-ride-sx.dts b/arch/arm64/boot/dts/qcom/nord-ride-sx.dts index b8839e1b86421..461d644c31eb1 100644 --- a/arch/arm64/boot/dts/qcom/nord-ride-sx.dts +++ b/arch/arm64/boot/dts/qcom/nord-ride-sx.dts @@ -6,6 +6,7 @@ /dts-v1/; #include +#include #include #include "nord-embedded.dtsi" @@ -78,6 +79,54 @@ enable-active-high; regulator-always-on; }; + + sound { + compatible = "qcom,nord-ride-sndcard"; + model = "NORD-RIDE-SX"; + + pinctrl-0 = <&aud_intfc8_mi2s_active>, <&aud_mclk0_mira_active>; + pinctrl-names = "default"; + + aud-intfc8-mi2s-playback-dai-link { + link-name = "PCM1681 Playback"; + + codec { + sound-dai = <&pcm1681>; + }; + + cpu { + sound-dai = <&q6apmbedai TERTIARY_MI2S_RX>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + + aud-intfc8-mi2s-capture-dai-link { + link-name = "ADAU1977 Capture"; + + codec { + sound-dai = <&adau1979>; + }; + + cpu { + sound-dai = <&q6apmbedai TERTIARY_MI2S_TX>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + }; + + vreg_pre_reg_3p3: regulator-pre-reg-3p3 { + compatible = "regulator-fixed"; + regulator-name = "vreg_pre_reg_3p3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; }; &apps_rsc { @@ -561,6 +610,53 @@ clock-frequency = <400000>; status = "okay"; + expander0: gpio@20 { + compatible = "ti,tca6408"; + reg = <0x20>; + #gpio-cells = <2>; + gpio-controller; + #interrupt-cells = <2>; + interrupt-controller; + interrupts-extended = <&pmic_l_gpios 6 GPIO_ACTIVE_LOW>; + reset-gpios = <&pmic_l_gpios 5 GPIO_ACTIVE_LOW>; + + click-pop-mute-hog { + gpio-hog; + gpios = <5 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "click-pop-mute-n"; + }; + + dac-enable-hog { + gpio-hog; + gpios = <6 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "dac_enable"; + }; + }; + + /* PCM1681 DAC */ + pcm1681: pcm1681@4c { + compatible = "ti,pcm1681"; + reg = <0x4c>; + + clocks = <&q6prmcc LPASS_CLK_ID_MCLK_1 LPASS_CLK_ATTRIBUTE_COUPLE_NO>; + clock-names = "sclk"; + assigned-clocks = <&q6prmcc LPASS_CLK_ID_MCLK_1 LPASS_CLK_ATTRIBUTE_COUPLE_NO>; + assigned-clock-rates = <12288000>; + + #sound-dai-cells = <0>; + }; + + /* ADI ADAU1979 - 4-channel 24-bit ADC */ + adau1979: adau1979@31 { + compatible = "adi,adau1979"; + reg = <0x31>; + + AVDD-supply = <&vreg_pre_reg_3p3>; + IOVDD-supply = <&vreg_pre_reg_3p3>; + #sound-dai-cells = <0>; + }; }; &i2c19 { @@ -622,6 +718,23 @@ status = "okay"; }; +&q6apmbedai { + #address-cells = <1>; + #size-cells = <0>; + + dai@20 { + reg = ; + clocks = <&q6prmcc LPASS_CLK_ID_AUD_INTF8_IBIT LPASS_CLK_ATTRIBUTE_COUPLE_NO>; + clock-names = "bclk"; + }; + + dai@21 { + reg = ; + clocks = <&q6prmcc LPASS_CLK_ID_AUD_INTF8_IBIT LPASS_CLK_ATTRIBUTE_COUPLE_NO>; + clock-names = "bclk"; + }; +}; + &uart15 { status = "okay"; }; diff --git a/arch/arm64/boot/dts/qcom/nord-rrd.dts b/arch/arm64/boot/dts/qcom/nord-rrd.dts index ddc534e2214b4..ca6b45dc248d5 100644 --- a/arch/arm64/boot/dts/qcom/nord-rrd.dts +++ b/arch/arm64/boot/dts/qcom/nord-rrd.dts @@ -6,9 +6,11 @@ /dts-v1/; #include +#include #include #include "nord-embedded.dtsi" +#include "nord-pmics.dtsi" / { model = "Qualcomm Technologies, Inc. IQ10 RRD"; @@ -52,6 +54,46 @@ }; }; + sound { + compatible = "qcom,nord-ride-sndcard"; + model = "NORD-RRD"; + + pinctrl-0 = <&aud_intfc8_mi2s_active>, <&aud_mclk0_mira_active>; + pinctrl-names = "default"; + + aud-intfc8-mi2s-playback-dai-link { + link-name = "PCM1681 Playback"; + + codec { + sound-dai = <&pcm1681>; + }; + + cpu { + sound-dai = <&q6apmbedai TERTIARY_MI2S_RX>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + + aud-intfc8-mi2s-capture-dai-link { + link-name = "ADAU1977 Capture"; + + codec { + sound-dai = <&adau1979>; + }; + + cpu { + sound-dai = <&q6apmbedai TERTIARY_MI2S_TX>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + }; + vreg_pre_reg_3p3: regulator-pre-reg-3p3 { compatible = "regulator-fixed"; regulator-name = "vreg_pre_reg_3p3"; @@ -521,6 +563,77 @@ }; }; +&i2c18 { + clock-frequency = <400000>; + + status = "okay"; + + expander0: gpio@20 { + compatible = "ti,tca6408"; + reg = <0x20>; + #gpio-cells = <2>; + gpio-controller; + #interrupt-cells = <2>; + interrupt-controller; + interrupts-extended = <&pmic_l_gpios 6 GPIO_ACTIVE_LOW>; + reset-gpios = <&pmic_l_gpios 5 GPIO_ACTIVE_LOW>; + + click-pop-mute-hog { + gpio-hog; + gpios = <5 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "click-pop-mute-n"; + }; + + dac-enable-hog { + gpio-hog; + gpios = <6 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "dac_enable"; + }; + }; + + /* PCM1681 DAC */ + pcm1681: pcm1681@4c { + compatible = "ti,pcm1681"; + reg = <0x4c>; + + clocks = <&q6prmcc LPASS_CLK_ID_MCLK_1 LPASS_CLK_ATTRIBUTE_COUPLE_NO>; + clock-names = "sclk"; + assigned-clocks = <&q6prmcc LPASS_CLK_ID_MCLK_1 LPASS_CLK_ATTRIBUTE_COUPLE_NO>; + assigned-clock-rates = <12288000>; + + #sound-dai-cells = <0>; + }; + + /* ADI ADAU1979 - 4-channel 24-bit ADC */ + adau1979: adau1979@31 { + compatible = "adi,adau1979"; + reg = <0x31>; + + AVDD-supply = <&vreg_pre_reg_3p3>; + IOVDD-supply = <&vreg_pre_reg_3p3>; + #sound-dai-cells = <0>; + }; +}; + +&q6apmbedai { + #address-cells = <1>; + #size-cells = <0>; + + dai@20 { + reg = ; + clocks = <&q6prmcc LPASS_CLK_ID_AUD_INTF8_IBIT LPASS_CLK_ATTRIBUTE_COUPLE_NO>; + clock-names = "bclk"; + }; + + dai@21 { + reg = ; + clocks = <&q6prmcc LPASS_CLK_ID_AUD_INTF8_IBIT LPASS_CLK_ATTRIBUTE_COUPLE_NO>; + clock-names = "bclk"; + }; +}; + &qupv3_0 { firmware-name = "qcom/nord/qupv3fw.elf"; diff --git a/arch/arm64/boot/dts/qcom/nord.dtsi b/arch/arm64/boot/dts/qcom/nord.dtsi index 3d472d2434b64..6da4483f85b63 100644 --- a/arch/arm64/boot/dts/qcom/nord.dtsi +++ b/arch/arm64/boot/dts/qcom/nord.dtsi @@ -1862,6 +1862,42 @@ dma-coherent; }; }; + + gpr { + compatible = "qcom,gpr"; + qcom,glink-channels = "adsp_apps"; + qcom,domain = ; + qcom,intents = <512 20>; + label = "adsp"; + #address-cells = <1>; + #size-cells = <0>; + + q6apm: service@1 { + compatible = "qcom,q6apm"; + reg = ; + #sound-dai-cells = <0>; + + q6apmbedai: bedais { + compatible = "qcom,q6apm-lpass-dais"; + #sound-dai-cells = <1>; + }; + + q6apmdai: dais { + compatible = "qcom,q6apm-dais"; + iommus = <&apps_smmu_0 0x09E1 0x0400>; + }; + }; + + q6prm: service@2 { + compatible = "qcom,q6prm"; + reg = ; + + q6prmcc: clock-controller { + compatible = "qcom,q6prm-lpass-clocks"; + #clock-cells = <2>; + }; + }; + }; }; }; diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 60178ffbcb2e3..8955cfb5de699 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -1101,6 +1101,8 @@ CONFIG_SND_SOC_MT8195=m CONFIG_SND_SOC_MT8195_MT6359=m CONFIG_SND_SOC_MT8365=m CONFIG_SND_SOC_MT8365_MT6357=m +CONFIG_SND_SOC_PCM1681=m +CONFIG_SND_SOC_ADAU1977_I2C=m CONFIG_SND_MESON_AXG_SOUND_CARD=m CONFIG_SND_MESON_GX_SOUND_CARD=m CONFIG_SND_SOC_QCOM=m diff --git a/arch/arm64/configs/prune.config b/arch/arm64/configs/prune.config index 40665245e406f..804c32654fdd7 100644 --- a/arch/arm64/configs/prune.config +++ b/arch/arm64/configs/prune.config @@ -153,8 +153,6 @@ # CONFIG_GPIO_MB86S7X is not set # CONFIG_GPIO_XGENE is not set # CONFIG_GPIO_MAX732X is not set -# CONFIG_GPIO_PCA953X is not set -# CONFIG_GPIO_PCA953X_IRQ is not set # CONFIG_GPIO_BD9571MWV is not set # CONFIG_GPIO_MAX77620 is not set # CONFIG_POWER_RESET_BRCMSTB is not set diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 9ceb474a18de9..cf07b75e8d92a 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -529,7 +529,7 @@ config SND_SOC_ADAU1977_SPI select REGMAP_SPI config SND_SOC_ADAU1977_I2C - tristate + tristate "Texas Instruments ADAU1977 CODEC (I2C)" depends on I2C select SND_SOC_ADAU1977 select REGMAP_I2C diff --git a/sound/soc/codecs/adau1977-i2c.c b/sound/soc/codecs/adau1977-i2c.c index d1c6c4ddf5063..9f54fa8375b15 100644 --- a/sound/soc/codecs/adau1977-i2c.c +++ b/sound/soc/codecs/adau1977-i2c.c @@ -34,9 +34,18 @@ static const struct i2c_device_id adau1977_i2c_ids[] = { }; MODULE_DEVICE_TABLE(i2c, adau1977_i2c_ids); +static const struct of_device_id adau1977_i2c_of_match[] __maybe_unused = { + { .compatible = "adi,adau1977" }, + { .compatible = "adi,adau1978" }, + { .compatible = "adi,adau1979" }, + { }, +}; +MODULE_DEVICE_TABLE(of, adau1977_i2c_of_match); + static struct i2c_driver adau1977_i2c_driver = { .driver = { .name = "adau1977", + .of_match_table = of_match_ptr(adau1977_i2c_of_match), }, .probe = adau1977_i2c_probe, .id_table = adau1977_i2c_ids, diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c index cb923cecb47fc..0abcd0c730970 100644 --- a/sound/soc/codecs/pcm1681.c +++ b/sound/soc/codecs/pcm1681.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -74,6 +75,7 @@ struct pcm1681_private { unsigned int deemph; /* Current rate for deemphasis control */ unsigned int rate; + struct clk *sclk; }; static const int pcm1681_deemph[] = { 44100, 48000, 32000 }; @@ -304,6 +306,22 @@ static int pcm1681_i2c_probe(struct i2c_client *client) if (!priv) return -ENOMEM; + priv->sclk = devm_clk_get_optional(&client->dev, "sclk"); + if (IS_ERR(priv->sclk)) + return dev_err_probe(&client->dev, PTR_ERR(priv->sclk), + "Failed to get sclk\n"); + + ret = clk_prepare_enable(priv->sclk); + if (ret) + return dev_err_probe(&client->dev, ret, + "Failed to enable sclk\n"); + + ret = devm_add_action_or_reset(&client->dev, + (void (*)(void *))clk_disable_unprepare, + priv->sclk); + if (ret) + return ret; + priv->regmap = devm_regmap_init_i2c(client, &pcm1681_regmap); if (IS_ERR(priv->regmap)) { ret = PTR_ERR(priv->regmap); diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c index 767606ea3ab7b..27ffc88a9fb41 100644 --- a/sound/soc/qcom/common.c +++ b/sound/soc/qcom/common.c @@ -28,28 +28,28 @@ static struct device_node *qcom_snd_get_link_node(struct snd_soc_pcm_runtime *rt { struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); struct snd_soc_card *card = rtd->card; - struct device_node *np; - struct device_node *cpu_np; struct of_phandle_args args; int ret; if (!card->dev || !card->dev->of_node) return NULL; - for_each_available_child_of_node(card->dev->of_node, np) { - cpu_np = of_get_child_by_name(np, "cpu"); + for_each_available_child_of_node_scoped(card->dev->of_node, np) { + struct device_node *cpu_np __free(device_node) = + of_get_child_by_name(np, "cpu"); + if (!cpu_np) continue; - ret = of_parse_phandle_with_args(cpu_np, "sound-dai", "#sound-dai-cells", 0, &args); - of_node_put(cpu_np); + ret = of_parse_phandle_with_args(cpu_np, "sound-dai", "#sound-dai-cells", 0, + &args); if (ret) continue; if (args.np == rtd->dai_link->cpus[0].of_node && args.args_count == 1 && args.args[0] == cpu_dai->id) { of_node_put(args.np); - return np; + return of_node_get(np); } of_node_put(args.np); @@ -100,33 +100,24 @@ static int qcom_snd_parse_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd, struct qcom_snd_tdm_slot_cfg *cpu_cfg, struct qcom_snd_tdm_slot_cfg *codec_cfg) { - struct device_node *link_np; - struct device_node *cpu_np = NULL; - struct device_node *codec_np = NULL; + struct device_node *link_np __free(device_node) = qcom_snd_get_link_node(rtd); int ret; - link_np = qcom_snd_get_link_node(rtd); if (!link_np) return -EINVAL; - cpu_np = of_get_child_by_name(link_np, "cpu"); - codec_np = of_get_child_by_name(link_np, "codec"); - if (!cpu_np || !codec_np) { - ret = -EINVAL; - goto out; - } + struct device_node *cpu_np __free(device_node) = + of_get_child_by_name(link_np, "cpu"); + struct device_node *codec_np __free(device_node) = + of_get_child_by_name(link_np, "codec"); + if (!cpu_np || !codec_np) + return -EINVAL; ret = qcom_snd_parse_tdm_slot(cpu_np, cpu_cfg); if (ret) - goto out; - - ret = qcom_snd_parse_tdm_slot(codec_np, codec_cfg); -out: - of_node_put(codec_np); - of_node_put(cpu_np); - of_node_put(link_np); + return ret; - return ret; + return qcom_snd_parse_tdm_slot(codec_np, codec_cfg); } int qcom_snd_get_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd, @@ -143,39 +134,49 @@ int qcom_snd_get_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd, } EXPORT_SYMBOL_GPL(qcom_snd_get_dai_tdm_slots); -int qcom_snd_apply_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd) +int qcom_snd_apply_dai_tdm_slots_cfg(struct snd_soc_pcm_runtime *rtd, + const struct qcom_snd_tdm_slot_cfg *cpu_cfg, + const struct qcom_snd_tdm_slot_cfg *codec_cfg) { struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); struct snd_soc_dai *codec_dai; - struct qcom_snd_tdm_slot_cfg cpu_cfg; - struct qcom_snd_tdm_slot_cfg codec_cfg; int i; int ret; - ret = qcom_snd_get_dai_tdm_slots(rtd, &cpu_cfg, &codec_cfg); - if (ret) - return ret == -EINVAL ? 0 : ret; - - if (!cpu_cfg.slots) - return ret; + if (!cpu_cfg->slots) + return 0; - ret = snd_soc_dai_set_tdm_slot(cpu_dai, cpu_cfg.tx_mask, cpu_cfg.rx_mask, - cpu_cfg.slots, cpu_cfg.slot_width); - if (ret < 0 && ret != -EOPNOTSUPP) + ret = snd_soc_dai_set_tdm_slot(cpu_dai, cpu_cfg->tx_mask, cpu_cfg->rx_mask, + cpu_cfg->slots, cpu_cfg->slot_width); + if (ret) return ret; for_each_rtd_codec_dais(rtd, i, codec_dai) { ret = snd_soc_dai_set_tdm_slot(codec_dai, - codec_cfg.tx_mask, - codec_cfg.rx_mask, - codec_cfg.slots, - codec_cfg.slot_width); - if (ret < 0 && ret != -EOPNOTSUPP) + codec_cfg->tx_mask, + codec_cfg->rx_mask, + codec_cfg->slots, + codec_cfg->slot_width); + if (ret && ret != -ENOTSUPP && ret != -EOPNOTSUPP) return ret; } return 0; } +EXPORT_SYMBOL_GPL(qcom_snd_apply_dai_tdm_slots_cfg); + +int qcom_snd_apply_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd) +{ + struct qcom_snd_tdm_slot_cfg cpu_cfg; + struct qcom_snd_tdm_slot_cfg codec_cfg; + int ret; + + ret = qcom_snd_get_dai_tdm_slots(rtd, &cpu_cfg, &codec_cfg); + if (ret) + return ret == -EINVAL ? 0 : ret; + + return qcom_snd_apply_dai_tdm_slots_cfg(rtd, &cpu_cfg, &codec_cfg); +} EXPORT_SYMBOL_GPL(qcom_snd_apply_dai_tdm_slots); int qcom_snd_parse_of(struct snd_soc_card *card) @@ -246,7 +247,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) ret = of_property_read_string(np, "link-name", &link->name); if (ret) { - dev_err(card->dev, "error getting codec dai_link name\n"); + dev_err(dev, "error getting codec dai_link name\n"); return ret; } @@ -264,7 +265,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) ret = snd_soc_of_get_dlc(cpu, &args, link->cpus, 0); if (ret) { - dev_err_probe(card->dev, ret, + dev_err_probe(dev, ret, "%s: error getting cpu dai name\n", link->name); return ret; } @@ -281,7 +282,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) "sound-dai", 0); if (!link->platforms->of_node) { - dev_err(card->dev, "%s: platform dai not found\n", link->name); + dev_err(dev, "%s: platform dai not found\n", link->name); return -EINVAL; } } else { @@ -291,7 +292,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) if (codec) { ret = snd_soc_of_get_dai_link_codecs(dev, codec, link); if (ret < 0) { - dev_err_probe(card->dev, ret, + dev_err_probe(dev, ret, "%s: codec dai not found\n", link->name); return ret; } diff --git a/sound/soc/qcom/common.h b/sound/soc/qcom/common.h index b73c25045e672..02b24caec485e 100644 --- a/sound/soc/qcom/common.h +++ b/sound/soc/qcom/common.h @@ -20,6 +20,9 @@ int qcom_snd_parse_of(struct snd_soc_card *card); int qcom_snd_get_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd, struct qcom_snd_tdm_slot_cfg *cpu_cfg, struct qcom_snd_tdm_slot_cfg *codec_cfg); +int qcom_snd_apply_dai_tdm_slots_cfg(struct snd_soc_pcm_runtime *rtd, + const struct qcom_snd_tdm_slot_cfg *cpu_cfg, + const struct qcom_snd_tdm_slot_cfg *codec_cfg); int qcom_snd_apply_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd); int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd, struct snd_soc_jack *jack, bool *jack_setup); diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index ff92ac7e522d3..e204fd59e5129 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -24,10 +24,7 @@ struct q6apm_dai_priv_data { struct clk *mclk; struct clk *bclk; - struct clk *eclk; - bool mclk_enabled; - bool bclk_enabled; - bool eclk_enabled; + bool mclk_enabled, bclk_enabled; }; struct q6apm_lpass_dai_data { @@ -37,52 +34,35 @@ struct q6apm_lpass_dai_data { struct q6apm_dai_priv_data priv[APM_PORT_MAX]; }; -static struct clk *q6apm_lpass_get_child_clk(struct device *dev, struct device_node *node, - const char *name) +static void q6apm_lpass_dai_disable_clocks(struct q6apm_lpass_dai_data *dai_data, int id) { - struct clk *clk; - - clk = devm_get_clk_from_child(dev, node, name); - if (IS_ERR(clk)) { - if (PTR_ERR(clk) == -EPROBE_DEFER) - return ERR_PTR(dev_err_probe(dev, PTR_ERR(clk), - "unable to get %s\n", name)); - - return NULL; + if (dai_data->priv[id].mclk_enabled) { + clk_disable_unprepare(dai_data->priv[id].mclk); + dai_data->priv[id].mclk_enabled = false; } - return clk; + if (dai_data->priv[id].bclk_enabled) { + clk_disable_unprepare(dai_data->priv[id].bclk); + dai_data->priv[id].bclk_enabled = false; + } } -static int of_q6apm_parse_dai_data(struct device *dev, - struct q6apm_lpass_dai_data *data) +static void q6apm_lpass_dai_put_clocks(struct q6apm_lpass_dai_data *dai_data) { - struct device_node *node; - - for_each_child_of_node(dev->of_node, node) { - struct q6apm_dai_priv_data *priv; - int ret; - int id; - - ret = of_property_read_u32(node, "reg", &id); - if (ret || id < 0 || id >= APM_PORT_MAX) - continue; - - priv = &data->priv[id]; - priv->mclk = q6apm_lpass_get_child_clk(dev, node, "mclk"); - if (IS_ERR(priv->mclk)) - return PTR_ERR(priv->mclk); + int i; - priv->bclk = q6apm_lpass_get_child_clk(dev, node, "bclk"); - if (IS_ERR(priv->bclk)) - return PTR_ERR(priv->bclk); + for (i = 0; i < APM_PORT_MAX; i++) { + q6apm_lpass_dai_disable_clocks(dai_data, i); - priv->eclk = q6apm_lpass_get_child_clk(dev, node, "eclk"); - if (IS_ERR(priv->eclk)) - return PTR_ERR(priv->eclk); + if (dai_data->priv[i].mclk) { + clk_put(dai_data->priv[i].mclk); + dai_data->priv[i].mclk = NULL; + } + if (dai_data->priv[i].bclk) { + clk_put(dai_data->priv[i].bclk); + dai_data->priv[i].bclk = NULL; + } } - - return 0; } static int q6dma_set_channel_map(struct snd_soc_dai *dai, @@ -220,33 +200,6 @@ static void q6apm_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct } } -static void q6lpass_disable_clocks(struct q6apm_dai_priv_data *priv) -{ - if (priv->mclk_enabled) { - clk_disable_unprepare(priv->mclk); - priv->mclk_enabled = false; - } - - if (priv->bclk_enabled) { - clk_disable_unprepare(priv->bclk); - priv->bclk_enabled = false; - } - - if (priv->eclk_enabled) { - clk_disable_unprepare(priv->eclk); - priv->eclk_enabled = false; - } -} - -static void q6lpass_dai_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); - - q6lpass_disable_clocks(&dai_data->priv[dai->id]); - q6apm_lpass_dai_shutdown(substream, dai); -} - static int q6apm_lpass_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { @@ -290,7 +243,7 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s * It is recommend to load DSP with source graph first and then sink * graph, so sequence for playback and capture will be different */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && !dai_data->graph[dai->id]) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && dai_data->graph[dai->id] == NULL) { graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id, substream->stream); if (IS_ERR(graph)) { dev_err(dai->dev, "Failed to open graph (%d)\n", graph_id); @@ -339,6 +292,62 @@ static int q6apm_lpass_dai_startup(struct snd_pcm_substream *substream, struct s return 0; } +static int q6i2s_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + return q6apm_lpass_dai_startup(substream, dai); +} + +static void q6i2s_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); + + q6apm_lpass_dai_shutdown(substream, dai); + q6apm_lpass_dai_disable_clocks(dai_data, dai->id); +} + +static int q6i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) +{ + struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); + struct clk *sysclk = NULL; + bool *enabled = NULL; + int ret = 0; + + switch (clk_id) { + case LPAIF_MI2S_MCLK: + sysclk = dai_data->priv[dai->id].mclk; + enabled = &dai_data->priv[dai->id].mclk_enabled; + break; + case LPAIF_MI2S_BCLK: + sysclk = dai_data->priv[dai->id].bclk; + enabled = &dai_data->priv[dai->id].bclk_enabled; + break; + default: + return -EINVAL; + } + + if (sysclk) { + ret = clk_set_rate(sysclk, freq); + if (ret) { + dev_err(dai->dev, "Error, Unable to set rate (%d) for sysclk %d\n", + freq, clk_id); + return ret; + } + + if (*enabled) + return 0; + + ret = clk_prepare_enable(sysclk); + if (ret) { + dev_err(dai->dev, "Error, Unable to prepare (%d) sysclk\n", clk_id); + return ret; + } + + *enabled = true; + } + + return ret; +} + static int q6i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); @@ -356,99 +365,46 @@ static int q6tdm_set_tdm_slot(struct snd_soc_dai *dai, { struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); struct audioreach_module_config *cfg = &dai_data->module_config[dai->id]; - unsigned int cap_mask; + unsigned int cap_mask, slot_mask; if (slot_width != 16 && slot_width != 32) { - dev_err(dai->dev, "%s: invalid slot_width %d\n", - __func__, slot_width); + dev_err(dai->dev, "%s: invalid slot_width %d\n", __func__, slot_width); return -EINVAL; } switch (slots) { case 2: - cap_mask = 0x03; - break; case 4: - cap_mask = 0x0f; - break; case 8: - cap_mask = 0xff; - break; case 16: - cap_mask = 0xffff; + cap_mask = GENMASK(slots - 1, 0); break; default: - dev_err(dai->dev, "%s: invalid slots %d\n", - __func__, slots); + dev_err(dai->dev, "%s: invalid slots %d\n", __func__, slots); return -EINVAL; } switch (dai->id) { case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: + slot_mask = (dai->id & 0x1) ? tx_mask : rx_mask; + if (slot_mask & ~cap_mask) { + dev_err(dai->dev, "%s: invalid slot mask 0x%x for %d slots\n", + __func__, slot_mask, slots); + return -EINVAL; + } + cfg->nslots_per_frame = slots; cfg->slot_width = slot_width; - cfg->slot_mask = ((dai->id & 0x1) ? tx_mask : rx_mask) & cap_mask; + cfg->slot_mask = slot_mask; break; default: - dev_err(dai->dev, "%s: invalid dai id 0x%x\n", - __func__, dai->id); + dev_err(dai->dev, "%s: invalid dai id 0x%x\n", __func__, dai->id); return -EINVAL; } return 0; } -static int q6i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, - unsigned int freq, int dir) -{ - struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); - struct q6apm_dai_priv_data *priv = &dai_data->priv[dai->id]; - struct clk *sysclk = NULL; - bool *enabled = NULL; - int ret; - - switch (clk_id) { - case LPAIF_MI2S_TDM_MCLK: - sysclk = priv->mclk; - enabled = &priv->mclk_enabled; - break; - case LPAIF_MI2S_TDM_BCLK: - sysclk = priv->bclk; - enabled = &priv->bclk_enabled; - break; - case LPAIF_MI2S_TDM_ECLK: - sysclk = priv->eclk; - enabled = &priv->eclk_enabled; - break; - default: - return 0; - } - - if (!sysclk || !freq) - return 0; - - ret = clk_set_rate(sysclk, freq); - if (ret) - return ret; - - if (*enabled) - return 0; - - ret = clk_prepare_enable(sysclk); - if (ret) - return ret; - - *enabled = true; - - return 0; -} - -static int q6lpass_set_sysclk(struct snd_soc_dai *dai, int clk_id, - unsigned int freq, int dir) -{ - return q6i2s_set_sysclk(dai, clk_id, freq, dir); -} - static const struct snd_soc_dai_ops q6dma_ops = { .prepare = q6apm_lpass_dai_prepare, .startup = q6apm_lpass_dai_startup, @@ -460,12 +416,12 @@ static const struct snd_soc_dai_ops q6dma_ops = { static const struct snd_soc_dai_ops q6i2s_ops = { .prepare = q6apm_lpass_dai_prepare, - .startup = q6apm_lpass_dai_startup, - .shutdown = q6lpass_dai_shutdown, + .startup = q6i2s_dai_startup, + .shutdown = q6i2s_lpass_dai_shutdown, .set_channel_map = q6dma_set_channel_map, .hw_params = q6dma_hw_params, .set_fmt = q6i2s_set_fmt, - .set_sysclk = q6lpass_set_sysclk, + .set_sysclk = q6i2s_set_sysclk, .trigger = q6apm_lpass_dai_trigger, }; @@ -479,15 +435,14 @@ static const struct snd_soc_dai_ops q6hdmi_ops = { }; static const struct snd_soc_dai_ops q6tdm_ops = { - .prepare = q6apm_lpass_dai_prepare, - .startup = q6apm_lpass_dai_startup, - .shutdown = q6lpass_dai_shutdown, - .set_channel_map = q6dma_set_channel_map, - .set_tdm_slot = q6tdm_set_tdm_slot, - .hw_params = q6dma_hw_params, - .set_fmt = q6i2s_set_fmt, - .set_sysclk = q6lpass_set_sysclk, - .trigger = q6apm_lpass_dai_trigger, + .prepare = q6apm_lpass_dai_prepare, + .startup = q6apm_lpass_dai_startup, + .shutdown = q6i2s_lpass_dai_shutdown, + .set_tdm_slot = q6tdm_set_tdm_slot, + .hw_params = q6dma_hw_params, + .set_fmt = q6i2s_set_fmt, + .set_sysclk = q6i2s_set_sysclk, + .trigger = q6apm_lpass_dai_trigger, }; static const struct snd_soc_component_driver q6apm_lpass_dai_component = { @@ -498,6 +453,65 @@ static const struct snd_soc_component_driver q6apm_lpass_dai_component = { .remove_order = SND_SOC_COMP_ORDER_FIRST, }; +static int of_q6apm_parse_dai_data(struct device *dev, + struct q6apm_lpass_dai_data *data) +{ + int ret; + + for_each_child_of_node_scoped(dev->of_node, node) { + struct q6apm_dai_priv_data *priv; + int id; + + ret = of_property_read_u32(node, "reg", &id); + if (ret || id < 0 || id >= APM_PORT_MAX) { + dev_err(dev, "valid dai id not found:%d\n", ret); + continue; + } + + switch (id) { + /* MI2S specific properties */ + case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: + case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: + case SENARY_MI2S_RX ... SENARY_MI2S_TX: + case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: + priv = &data->priv[id]; + priv->mclk = of_clk_get_by_name(node, "mclk"); + if (IS_ERR(priv->mclk)) { + int err = PTR_ERR(priv->mclk); + + priv->mclk = NULL; + if (err == -EPROBE_DEFER) { + q6apm_lpass_dai_put_clocks(data); + return dev_err_probe(dev, err, + "unable to get mi2s mclk\n"); + } + } + + priv->bclk = of_clk_get_by_name(node, "bclk"); + if (IS_ERR(priv->bclk)) { + int err = PTR_ERR(priv->bclk); + + priv->bclk = NULL; + if (err == -EPROBE_DEFER) { + q6apm_lpass_dai_put_clocks(data); + return dev_err_probe(dev, err, + "unable to get mi2s bclk\n"); + } + } + break; + default: + break; + } + } + + return 0; +} + +static void q6apm_lpass_dai_clocks_action(void *data) +{ + q6apm_lpass_dai_put_clocks(data); +} + static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev) { struct q6dsp_audio_port_dai_driver_config cfg; @@ -516,6 +530,10 @@ static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev) if (ret) return ret; + ret = devm_add_action_or_reset(dev, q6apm_lpass_dai_clocks_action, dai_data); + if (ret) + return ret; + memset(&cfg, 0, sizeof(cfg)); cfg.q6i2s_ops = &q6i2s_ops; cfg.q6dma_ops = &q6dma_ops; diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c index 5e98f8c428338..9ddbb65684c66 100644 --- a/sound/soc/qcom/sc8280xp.c +++ b/sound/soc/qcom/sc8280xp.c @@ -14,14 +14,22 @@ #include #include #include "qdsp6/q6afe.h" +#include "qdsp6/q6apm.h" #include "qdsp6/q6prm.h" +#include "qdsp6/q6dsp-common.h" #include "common.h" #include "sdw.h" -#define MCLK_FREQ 12288000 -#define MCLK_NATIVE_FREQ 11289600 +#define I2S_MCLKFS 256 -static const struct snd_soc_dapm_widget sc8280xp_dapm_widgets[] = { +#define I2S_MCLK_RATE(rate) \ + ((rate) * (I2S_MCLKFS)) +#define I2S_BIT_RATE(rate, channels, format) \ + ((rate) * (channels) * (format)) + +#define LRCLK_SYSCLK 1 + +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), @@ -48,7 +56,7 @@ static const struct snd_kcontrol_new shikra_controls[] = { SOC_DAPM_PIN_SWITCH("Speaker"), }; -struct snd_soc_common { +struct qcom_snd_soc_common { const char *driver_name; const struct snd_soc_dapm_widget *dapm_widgets; int num_dapm_widgets; @@ -56,10 +64,13 @@ struct snd_soc_common { int num_dapm_routes; const struct snd_kcontrol_new *controls; int num_controls; - bool mi2s_mclk_enable; - bool mi2s_bclk_enable; unsigned int codec_dai_fmt; + unsigned int codec_sysclk_src; bool codec_sysclk_set; + bool mi2s_mclk_enable; + bool mi2s_bclk_enable; + bool wcd_jack; + int (*snd_prepare)(struct snd_pcm_substream *substream); }; struct sc8280xp_snd_data { @@ -67,27 +78,39 @@ struct sc8280xp_snd_data { struct snd_soc_card *card; struct snd_soc_jack jack; struct snd_soc_jack dp_jack[8]; - const struct snd_soc_common *snd_soc_common_priv; + const struct qcom_snd_soc_common *priv; bool dsp_bypass_mode; bool jack_setup; }; -static int sc8280xp_tdm_set_dai_fmt(struct snd_soc_pcm_runtime *rtd, - struct snd_soc_dai *cpu_dai) +static inline int sc8280xp_get_mclk_freq(struct snd_pcm_hw_params *params) { - int ret; + int rate = params_rate(params); - ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); - if (ret && ret != -EOPNOTSUPP) - dev_err(rtd->dev, "%s: failed to set cpu fmt: %d\n", __func__, ret); + switch (rate) { + case 11025: + case 44100: + case 88200: + return I2S_MCLK_RATE(44100); + default: + break; + } + + return I2S_MCLK_RATE(rate); +} - return ret < 0 && ret != -EOPNOTSUPP ? ret : 0; +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_tdm_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 sc8280xp_snd_data *data = 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; struct qcom_snd_tdm_slot_cfg cpu_cfg; @@ -103,11 +126,11 @@ static int sc8280xp_tdm_hw_params(struct snd_pcm_substream *substream, if (!cpu_cfg.slots) return 0; - ret = sc8280xp_tdm_set_dai_fmt(rtd, cpu_dai); + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); if (ret) return ret; - ret = qcom_snd_apply_dai_tdm_slots(rtd); + ret = qcom_snd_apply_dai_tdm_slots_cfg(rtd, &cpu_cfg, &codec_cfg); if (ret) return ret; @@ -115,18 +138,25 @@ static int sc8280xp_tdm_hw_params(struct snd_pcm_substream *substream, if (!bclk_freq) return -EINVAL; - ret = snd_soc_dai_set_sysclk(cpu_dai, LPAIF_MI2S_TDM_BCLK, bclk_freq, SND_SOC_CLOCK_IN); - if (ret < 0 && ret != -EOPNOTSUPP) { - dev_err(rtd->dev, "%s: failed to set cpu sysclk: %d\n", __func__, ret); - return ret; + if (data->priv->mi2s_bclk_enable) { + ret = snd_soc_dai_set_sysclk(cpu_dai, LPAIF_MI2S_BCLK, bclk_freq, + SND_SOC_CLOCK_IN); + if (ret) { + dev_err(rtd->dev, "%s: failed to set cpu sysclk: %d\n", + __func__, ret); + return ret; + } } - for_each_rtd_codec_dais(rtd, i, codec_dai) { - ret = snd_soc_dai_set_sysclk(codec_dai, 0, bclk_freq, SND_SOC_CLOCK_IN); - if (ret < 0 && ret != -EOPNOTSUPP) { - dev_err(rtd->dev, "%s: failed to set codec sysclk on %s: %d\n", - __func__, codec_dai->name, ret); - return ret; + if (data->priv->codec_sysclk_set) { + for_each_rtd_codec_dais(rtd, i, codec_dai) { + ret = snd_soc_dai_set_sysclk(codec_dai, 0, bclk_freq, + SND_SOC_CLOCK_IN); + if (ret) { + dev_err(rtd->dev, "%s: failed to set codec sysclk on %s: %d\n", + __func__, codec_dai->name, ret); + return ret; + } } } @@ -145,10 +175,10 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) case WSA_CODEC_DMA_RX_0: case WSA_CODEC_DMA_RX_1: /* - * Set limit of -3 dB on Digital Volume and 0 dB on PA Volume - * to reduce the risk of speaker damage until we have active - * speaker protection in place. - */ + * Set limit of -3 dB on Digital Volume and 0 dB on PA Volume + * to reduce the risk of speaker damage until we have active + * speaker protection in place. + */ snd_soc_limit_volume(card, "WSA_RX0 Digital Volume", 81); snd_soc_limit_volume(card, "WSA_RX1 Digital Volume", 81); snd_soc_limit_volume(card, "SpkrLeft PA Volume", 17); @@ -170,24 +200,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->priv->wcd_jack) + return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); -static inline int sc8280xp_get_mclk_feq(unsigned int rate) -{ - 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; + return 0; } static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, @@ -213,6 +229,7 @@ static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, case LPASS_CDC_DMA_VA_TX1: channels->min = 1; break; + break; default: break; } @@ -221,24 +238,17 @@ static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } -static int sc8280xp_snd_prepare(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 *data = snd_soc_card_get_drvdata(rtd->card); - - return qcom_snd_sdw_prepare(substream, &data->stream_prepared[cpu_dai->id]); -} - 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); - unsigned 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); + int rate = params_rate(params); int ret; - struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); if (data->dsp_bypass_mode) return 0; @@ -246,24 +256,50 @@ static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream, 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 < 0 && ret != -EOPNOTSUPP) + if (ret && ret != -ENOTSUPP) return ret; - if (data->snd_soc_common_priv->codec_dai_fmt) - snd_soc_dai_set_fmt(codec_dai, - data->snd_soc_common_priv->codec_dai_fmt); + if (data->priv->codec_dai_fmt) { + ret = snd_soc_dai_set_fmt(codec_dai, + data->priv->codec_dai_fmt); + if (ret && ret != -ENOTSUPP) + return ret; + } - if (data->snd_soc_common_priv->codec_sysclk_set) - snd_soc_dai_set_sysclk(codec_dai, 0, mclk_freq, - SND_SOC_CLOCK_IN); + if (data->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) - return 0; + if (data->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; + } - ret = snd_soc_dai_set_sysclk(cpu_dai, LPAIF_MI2S_MCLK, - mclk_freq, SND_SOC_CLOCK_IN); - return ret < 0 && ret != -EOPNOTSUPP ? ret : 0; + if (data->priv->codec_sysclk_set) { + if (data->priv->codec_sysclk_src == LRCLK_SYSCLK) { + ret = snd_soc_component_set_sysclk(codec_dai->component, 0, + LRCLK_SYSCLK, + rate, SND_SOC_CLOCK_IN); + if (ret && ret != -ENOTSUPP) + return ret; + } else { + ret = snd_soc_dai_set_sysclk(codec_dai, + 0, mclk_freq, + SND_SOC_CLOCK_IN); + if (ret && ret != -ENOTSUPP) + return ret; + } + } + break; case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: return sc8280xp_tdm_hw_params(substream, params); default: @@ -273,6 +309,23 @@ static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream, return 0; } +static int sc8280xp_snd_prepare(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 *data = snd_soc_card_get_drvdata(rtd->card); + + if (data->priv->snd_prepare) { + int ret; + + ret = data->priv->snd_prepare(substream); + if (ret) + return ret; + } + + return qcom_snd_sdw_prepare(substream, &data->stream_prepared[cpu_dai->id]); +} + static int sc8280xp_snd_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); @@ -315,24 +368,25 @@ static int sc8280xp_platform_probe(struct platform_device *pdev) if (!card) return -ENOMEM; + /* Allocate the private data */ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - data->snd_soc_common_priv = of_device_get_match_data(dev); - if (!data->snd_soc_common_priv) - return -EINVAL; + data->priv = of_device_get_match_data(dev); + if (!data->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; + card->dapm_widgets = data->priv->dapm_widgets; + card->num_dapm_widgets = data->priv->num_dapm_widgets; + card->dapm_routes = data->priv->dapm_routes; + card->num_dapm_routes = data->priv->num_dapm_routes; + card->controls = data->priv->controls; + card->num_controls = data->priv->num_controls; ret = qcom_snd_parse_of(card); if (ret) @@ -341,55 +395,68 @@ static int sc8280xp_platform_probe(struct platform_device *pdev) data->dsp_bypass_mode = of_property_read_bool(dev->of_node, "qcom,adsp-bypass-mode"); - card->driver_name = data->snd_soc_common_priv->driver_name; + card->driver_name = data->priv->driver_name; sc8280xp_add_be_ops(card); return devm_snd_soc_register_card(dev, card); } -static const struct snd_soc_common kaanapali_priv_data = { +static const struct qcom_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 const struct qcom_snd_soc_common nord_ride_priv_data = { + .driver_name = "nord", + .mi2s_bclk_enable = true, + .codec_sysclk_set = true, + .codec_sysclk_src = LRCLK_SYSCLK, + .codec_dai_fmt = SND_SOC_DAIFMT_CBC_CFC | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_I2S, }; -static const struct snd_soc_common qcm6490_priv_data = { +static const struct qcom_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 const struct snd_soc_common qcs615_priv_data = { +static const struct qcom_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 const struct snd_soc_common qcs6490_priv_data = { +static const struct qcom_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 const struct snd_soc_common qcs8275_priv_data = { +static const struct qcom_snd_soc_common qcs8275_priv_data = { .driver_name = "qcs8300", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), }; -static const struct snd_soc_common qcs9100_priv_data = { +static const struct qcom_snd_soc_common qcs9100_priv_data = { .driver_name = "sa8775p", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), }; -static const struct snd_soc_common sc8280xp_priv_data = { +static const struct qcom_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 const struct snd_soc_common shikra_priv_data = { +static const struct qcom_snd_soc_common shikra_priv_data = { .driver_name = "shikra", .dapm_widgets = shikra_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(shikra_dapm_widgets), @@ -402,25 +469,28 @@ static const struct snd_soc_common shikra_priv_data = { .codec_sysclk_set = true, }; -static const struct snd_soc_common sm8450_priv_data = { +static const struct qcom_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 const struct snd_soc_common sm8550_priv_data = { +static const struct qcom_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 const struct snd_soc_common sm8650_priv_data = { +static const struct qcom_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 const struct snd_soc_common sm8750_priv_data = { +static const struct qcom_snd_soc_common sm8750_priv_data = { .driver_name = "sm8750", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), @@ -428,6 +498,7 @@ static const struct snd_soc_common sm8750_priv_data = { static const struct of_device_id snd_sc8280xp_dt_match[] = { { .compatible = "qcom,kaanapali-sndcard", .data = &kaanapali_priv_data }, + { .compatible = "qcom,nord-ride-sndcard", .data = &nord_ride_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 },