diff options
-rw-r--r-- | Documentation/devicetree/bindings/sound/fsl-asoc-card.txt | 117 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/sound/fsl-asoc-card.yaml | 195 | ||||
-rw-r--r-- | include/sound/control.h | 23 | ||||
-rw-r--r-- | sound/soc/Kconfig | 8 | ||||
-rw-r--r-- | sound/soc/Makefile | 4 | ||||
-rw-r--r-- | sound/soc/codecs/nau8325.c | 17 | ||||
-rw-r--r-- | sound/soc/soc-card-test.c | 184 | ||||
-rw-r--r-- | sound/soc/soc-card.c | 21 | ||||
-rw-r--r-- | sound/soc/sof/intel/hda-dai-ops.c | 41 | ||||
-rw-r--r-- | sound/soc/sof/intel/hda-dai.c | 124 | ||||
-rw-r--r-- | sound/soc/sof/intel/hda.c | 29 | ||||
-rw-r--r-- | sound/soc/sof/intel/hda.h | 11 | ||||
-rw-r--r-- | sound/soc/sof/ipc4-topology.c | 62 | ||||
-rw-r--r-- | sound/soc/sof/ipc4-topology.h | 3 | ||||
-rw-r--r-- | sound/soc/sof/pcm.c | 58 | ||||
-rw-r--r-- | sound/soc/sof/sof-audio.c | 29 | ||||
-rw-r--r-- | sound/soc/sof/sof-audio.h | 2 |
17 files changed, 662 insertions, 266 deletions
diff --git a/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt b/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt deleted file mode 100644 index 4e8dbc5abfd1..000000000000 --- a/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt +++ /dev/null @@ -1,117 +0,0 @@ -Freescale Generic ASoC Sound Card with ASRC support - -The Freescale Generic ASoC Sound Card can be used, ideally, for all Freescale -SoCs connecting with external CODECs. - -The idea of this generic sound card is a bit like ASoC Simple Card. However, -for Freescale SoCs (especially those released in recent years), most of them -have ASRC (Documentation/devicetree/bindings/sound/fsl,asrc.txt) inside. And -this is a specific feature that might be painstakingly controlled and merged -into the Simple Card. - -So having this generic sound card allows all Freescale SoC users to benefit -from the simplification of a new card support and the capability of the wide -sample rates support through ASRC. - -Note: The card is initially designed for those sound cards who use AC'97, I2S - and PCM DAI formats. However, it'll be also possible to support those non - AC'97/I2S/PCM type sound cards, such as S/PDIF audio and HDMI audio, as - long as the driver has been properly upgraded. - - -The compatible list for this generic sound card currently: - "fsl,imx-audio-ac97" - - "fsl,imx-audio-cs42888" - - "fsl,imx-audio-cs427x" - (compatible with CS4271 and CS4272) - - "fsl,imx-audio-wm8962" - - "fsl,imx-audio-sgtl5000" - (compatible with Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt) - - "fsl,imx-audio-wm8960" - - "fsl,imx-audio-mqs" - - "fsl,imx-audio-wm8524" - - "fsl,imx-audio-tlv320aic32x4" - - "fsl,imx-audio-tlv320aic31xx" - - "fsl,imx-audio-si476x" - - "fsl,imx-audio-wm8958" - - "fsl,imx-audio-nau8822" - -Required properties: - - - compatible : Contains one of entries in the compatible list. - - - model : The user-visible name of this sound complex - - - audio-cpu : The phandle of an CPU DAI controller - - - audio-codec : The phandle of an audio codec - -Optional properties: - - - audio-asrc : The phandle of ASRC. It can be absent if there's no - need to add ASRC support via DPCM. - - - audio-routing : A list of the connections between audio components. - Each entry is a pair of strings, the first being the - connection's sink, the second being the connection's - source. There're a few pre-designed board connectors: - * Line Out Jack - * Line In Jack - * Headphone Jack - * Mic Jack - * Ext Spk - * AMIC (stands for Analog Microphone Jack) - * DMIC (stands for Digital Microphone Jack) - - Note: The "Mic Jack" and "AMIC" are redundant while - coexisting in order to support the old bindings - of wm8962 and sgtl5000. - - - hp-det-gpio : The GPIO that detect headphones are plugged in - - mic-det-gpio : The GPIO that detect microphones are plugged in - - bitclock-master : Indicates dai-link bit clock master; for details see simple-card.yaml. - - frame-master : Indicates dai-link frame master; for details see simple-card.yaml. - - dai-format : audio format, for details see simple-card.yaml. - - frame-inversion : dai-link uses frame clock inversion, for details see simple-card.yaml. - - bitclock-inversion : dai-link uses bit clock inversion, for details see simple-card.yaml. - - mclk-id : main clock id, specific for each card configuration. - -Optional unless SSI is selected as a CPU DAI: - - - mux-int-port : The internal port of the i.MX audio muxer (AUDMUX) - - - mux-ext-port : The external port of the i.MX audio muxer - -Example: -sound-cs42888 { - compatible = "fsl,imx-audio-cs42888"; - model = "cs42888-audio"; - audio-cpu = <&esai>; - audio-asrc = <&asrc>; - audio-codec = <&cs42888>; - audio-routing = - "Line Out Jack", "AOUT1L", - "Line Out Jack", "AOUT1R", - "Line Out Jack", "AOUT2L", - "Line Out Jack", "AOUT2R", - "Line Out Jack", "AOUT3L", - "Line Out Jack", "AOUT3R", - "Line Out Jack", "AOUT4L", - "Line Out Jack", "AOUT4R", - "AIN1L", "Line In Jack", - "AIN1R", "Line In Jack", - "AIN2L", "Line In Jack", - "AIN2R", "Line In Jack"; -}; diff --git a/Documentation/devicetree/bindings/sound/fsl-asoc-card.yaml b/Documentation/devicetree/bindings/sound/fsl-asoc-card.yaml new file mode 100644 index 000000000000..42ca39eebd49 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/fsl-asoc-card.yaml @@ -0,0 +1,195 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/fsl-asoc-card.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale Generic ASoC Sound Card with ASRC support + +description: + The Freescale Generic ASoC Sound Card can be used, ideally, + for all Freescale SoCs connecting with external CODECs. + + The idea of this generic sound card is a bit like ASoC Simple Card. + However, for Freescale SoCs (especially those released in recent years), + most of them have ASRC inside. And this is a specific feature that might + be painstakingly controlled and merged into the Simple Card. + + So having this generic sound card allows all Freescale SoC users to + benefit from the simplification of a new card support and the capability + of the wide sample rates support through ASRC. + + Note, The card is initially designed for those sound cards who use AC'97, I2S + and PCM DAI formats. However, it'll be also possible to support those non + AC'97/I2S/PCM type sound cards, such as S/PDIF audio and HDMI audio, as + long as the driver has been properly upgraded. + +maintainers: + - Shengjiu Wang <shengjiu.wang@nxp.com> + +properties: + compatible: + oneOf: + - items: + - enum: + - fsl,imx-sgtl5000 + - fsl,imx53-cpuvo-sgtl5000 + - fsl,imx51-babbage-sgtl5000 + - fsl,imx53-m53evk-sgtl5000 + - fsl,imx53-qsb-sgtl5000 + - fsl,imx53-voipac-sgtl5000 + - fsl,imx6-armadeus-sgtl5000 + - fsl,imx6-rex-sgtl5000 + - fsl,imx6-sabreauto-cs42888 + - fsl,imx6-wandboard-sgtl5000 + - fsl,imx6dl-nit6xlite-sgtl5000 + - fsl,imx6q-ba16-sgtl5000 + - fsl,imx6q-nitrogen6_max-sgtl5000 + - fsl,imx6q-nitrogen6_som2-sgtl5000 + - fsl,imx6q-nitrogen6x-sgtl5000 + - fsl,imx6q-sabrelite-sgtl5000 + - fsl,imx6q-sabresd-wm8962 + - fsl,imx6q-udoo-ac97 + - fsl,imx6q-ventana-sgtl5000 + - fsl,imx6sl-evk-wm8962 + - fsl,imx6sx-sdb-mqs + - fsl,imx6sx-sdb-wm8962 + - fsl,imx7d-evk-wm8960 + - karo,tx53-audio-sgtl5000 + - tq,imx53-mba53-sgtl5000 + - enum: + - fsl,imx-audio-ac97 + - fsl,imx-audio-cs42888 + - fsl,imx-audio-mqs + - fsl,imx-audio-sgtl5000 + - fsl,imx-audio-wm8960 + - fsl,imx-audio-wm8962 + - items: + - enum: + - fsl,imx-audio-ac97 + - fsl,imx-audio-cs42888 + - fsl,imx-audio-cs427x + - fsl,imx-audio-mqs + - fsl,imx-audio-nau8822 + - fsl,imx-audio-sgtl5000 + - fsl,imx-audio-si476x + - fsl,imx-audio-tlv320aic31xx + - fsl,imx-audio-tlv320aic32x4 + - fsl,imx-audio-wm8524 + - fsl,imx-audio-wm8960 + - fsl,imx-audio-wm8962 + - fsl,imx-audio-wm8958 + + model: + $ref: /schemas/types.yaml#/definitions/string + description: The user-visible name of this sound complex + + audio-asrc: + $ref: /schemas/types.yaml#/definitions/phandle + description: + The phandle of ASRC. It can be absent if there's no + need to add ASRC support via DPCM. + + audio-codec: + $ref: /schemas/types.yaml#/definitions/phandle + description: The phandle of an audio codec + + audio-cpu: + $ref: /schemas/types.yaml#/definitions/phandle + description: The phandle of an CPU DAI controller + + audio-routing: + $ref: /schemas/types.yaml#/definitions/non-unique-string-array + description: + A list of the connections between audio components. Each entry is a + pair of strings, the first being the connection's sink, the second + being the connection's source. There're a few pre-designed board + connectors. "AMIC" stands for Analog Microphone Jack. + "DMIC" stands for Digital Microphone Jack. The "Mic Jack" and "AMIC" + are redundant while coexisting in order to support the old bindings + of wm8962 and sgtl5000. + + hp-det-gpio: + deprecated: true + maxItems: 1 + description: The GPIO that detect headphones are plugged in + + hp-det-gpios: + maxItems: 1 + description: The GPIO that detect headphones are plugged in + + mic-det-gpio: + deprecated: true + maxItems: 1 + description: The GPIO that detect microphones are plugged in + + mic-det-gpios: + maxItems: 1 + description: The GPIO that detect microphones are plugged in + + bitclock-master: + $ref: simple-card.yaml#/definitions/bitclock-master + description: Indicates dai-link bit clock master. + + frame-master: + $ref: simple-card.yaml#/definitions/frame-master + description: Indicates dai-link frame master. + + format: + $ref: simple-card.yaml#/definitions/format + description: audio format. + + frame-inversion: + $ref: simple-card.yaml#/definitions/frame-inversion + description: dai-link uses frame clock inversion. + + bitclock-inversion: + $ref: simple-card.yaml#/definitions/bitclock-inversion + description: dai-link uses bit clock inversion. + + mclk-id: + $ref: /schemas/types.yaml#/definitions/uint32 + description: main clock id, specific for each card configuration. + + mux-int-port: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [1, 2, 7] + description: The internal port of the i.MX audio muxer (AUDMUX) + + mux-ext-port: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [3, 4, 5, 6] + description: The external port of the i.MX audio muxer + + ssi-controller: + $ref: /schemas/types.yaml#/definitions/phandle + description: The phandle of an CPU DAI controller + +required: + - compatible + - model + +unevaluatedProperties: false + +examples: + - | + sound-cs42888 { + compatible = "fsl,imx-audio-cs42888"; + model = "cs42888-audio"; + audio-cpu = <&esai>; + audio-asrc = <&asrc>; + audio-codec = <&cs42888>; + audio-routing = + "Line Out Jack", "AOUT1L", + "Line Out Jack", "AOUT1R", + "Line Out Jack", "AOUT2L", + "Line Out Jack", "AOUT2R", + "Line Out Jack", "AOUT3L", + "Line Out Jack", "AOUT3R", + "Line Out Jack", "AOUT4L", + "Line Out Jack", "AOUT4R", + "AIN1L", "Line In Jack", + "AIN1R", "Line In Jack", + "AIN2L", "Line In Jack", + "AIN2R", "Line In Jack"; + }; diff --git a/include/sound/control.h b/include/sound/control.h index 9a4f4f7138da..c1659036c4a7 100644 --- a/include/sound/control.h +++ b/include/sound/control.h @@ -167,6 +167,29 @@ snd_ctl_find_id_mixer(struct snd_card *card, const char *name) return snd_ctl_find_id(card, &id); } +/** + * snd_ctl_find_id_mixer_locked - find the control instance with the given name string + * @card: the card instance + * @name: the name string + * + * Finds the control instance with the given name and + * @SNDRV_CTL_ELEM_IFACE_MIXER. Other fields are set to zero. + * + * This is merely a wrapper to snd_ctl_find_id_locked(). + * The caller must down card->controls_rwsem before calling this function. + * + * Return: The pointer of the instance if found, or %NULL if not. + */ +static inline struct snd_kcontrol * +snd_ctl_find_id_mixer_locked(struct snd_card *card, const char *name) +{ + struct snd_ctl_elem_id id = {}; + + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strscpy(id.name, name, sizeof(id.name)); + return snd_ctl_find_id_locked(card, &id); +} + int snd_ctl_create(struct snd_card *card); int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn); diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 439fa631c342..a52afb423b46 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -66,6 +66,14 @@ config SND_SOC_TOPOLOGY_KUNIT_TEST userspace applications such as pulseaudio, to prevent unnecessary problems. +config SND_SOC_CARD_KUNIT_TEST + tristate "KUnit tests for SoC card" + depends on KUNIT + default KUNIT_ALL_TESTS + help + If you want to perform tests on ALSA SoC card functions say Y here. + If unsure, say N. + config SND_SOC_UTILS_KUNIT_TEST tristate "KUnit tests for SoC utils" depends on KUNIT diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 8376fdb217ed..f90f5300b36e 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -12,6 +12,10 @@ ifneq ($(CONFIG_SND_SOC_TOPOLOGY_KUNIT_TEST),) obj-$(CONFIG_SND_SOC_TOPOLOGY_KUNIT_TEST) += soc-topology-test.o endif +ifneq ($(CONFIG_SND_SOC_CARD_KUNIT_TEST),) +obj-$(CONFIG_SND_SOC_CARD_KUNIT_TEST) += soc-card-test.o +endif + ifneq ($(CONFIG_SND_SOC_UTILS_KUNIT_TEST),) # snd-soc-test-objs := soc-utils-test.o obj-$(CONFIG_SND_SOC_UTILS_KUNIT_TEST) += soc-utils-test.o diff --git a/sound/soc/codecs/nau8325.c b/sound/soc/codecs/nau8325.c index a00a30364c89..d65f73144597 100644 --- a/sound/soc/codecs/nau8325.c +++ b/sound/soc/codecs/nau8325.c @@ -174,13 +174,20 @@ static bool nau8325_volatile_reg(struct device *dev, unsigned int reg) } } -static const char * const nau8325_dac_oversampl[] = { - "64", "256", "128", "", "32" }; +static const char * const nau8325_dac_oversampl_texts[] = { + "64", "256", "128", "32", +}; + +static const unsigned int nau8325_dac_oversampl_values[] = { + 0, 1, 2, 4, +}; static const struct soc_enum nau8325_dac_oversampl_enum = - SOC_ENUM_SINGLE(NAU8325_R29_DAC_CTRL1, NAU8325_DAC_OVERSAMPLE_SFT, - ARRAY_SIZE(nau8325_dac_oversampl), - nau8325_dac_oversampl); + SOC_VALUE_ENUM_SINGLE(NAU8325_R29_DAC_CTRL1, + NAU8325_DAC_OVERSAMPLE_SFT, 0x7, + ARRAY_SIZE(nau8325_dac_oversampl_texts), + nau8325_dac_oversampl_texts, + nau8325_dac_oversampl_values); static const DECLARE_TLV_DB_MINMAX_MUTE(dac_vol_tlv, -8000, 600); diff --git a/sound/soc/soc-card-test.c b/sound/soc/soc-card-test.c new file mode 100644 index 000000000000..075c52fe82e5 --- /dev/null +++ b/sound/soc/soc-card-test.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2024 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include <kunit/device.h> +#include <kunit/test.h> +#include <linux/module.h> +#include <sound/control.h> +#include <sound/soc.h> +#include <sound/soc-card.h> + +struct soc_card_test_priv { + struct device *card_dev; + struct snd_soc_card *card; +}; + +static const struct snd_kcontrol_new test_card_controls[] = { + SOC_SINGLE("Fee", SND_SOC_NOPM, 0, 1, 0), + SOC_SINGLE("Fi", SND_SOC_NOPM, 1, 1, 0), + SOC_SINGLE("Fo", SND_SOC_NOPM, 2, 1, 0), + SOC_SINGLE("Fum", SND_SOC_NOPM, 3, 1, 0), + SOC_SINGLE("Left Fee", SND_SOC_NOPM, 4, 1, 0), + SOC_SINGLE("Right Fee", SND_SOC_NOPM, 5, 1, 0), + SOC_SINGLE("Left Fi", SND_SOC_NOPM, 6, 1, 0), + SOC_SINGLE("Right Fi", SND_SOC_NOPM, 7, 1, 0), + SOC_SINGLE("Left Fo", SND_SOC_NOPM, 8, 1, 0), + SOC_SINGLE("Right Fo", SND_SOC_NOPM, 9, 1, 0), + SOC_SINGLE("Left Fum", SND_SOC_NOPM, 10, 1, 0), + SOC_SINGLE("Right Fum", SND_SOC_NOPM, 11, 1, 0), +}; + +static void test_snd_soc_card_get_kcontrol(struct kunit *test) +{ + struct soc_card_test_priv *priv = test->priv; + struct snd_soc_card *card = priv->card; + struct snd_kcontrol *kc; + struct soc_mixer_control *mc; + int i, ret; + + ret = snd_soc_add_card_controls(card, test_card_controls, ARRAY_SIZE(test_card_controls)); + KUNIT_ASSERT_EQ(test, ret, 0); + + /* Look up every control */ + for (i = 0; i < ARRAY_SIZE(test_card_controls); ++i) { + kc = snd_soc_card_get_kcontrol(card, test_card_controls[i].name); + KUNIT_EXPECT_NOT_ERR_OR_NULL_MSG(test, kc, "Failed to find '%s'\n", + test_card_controls[i].name); + if (!kc) + continue; + + /* Test that it is the correct control */ + mc = (struct soc_mixer_control *)kc->private_value; + KUNIT_EXPECT_EQ_MSG(test, mc->shift, i, "For '%s'\n", test_card_controls[i].name); + } + + /* Test some names that should not be found */ + kc = snd_soc_card_get_kcontrol(card, "None"); + KUNIT_EXPECT_NULL(test, kc); + + kc = snd_soc_card_get_kcontrol(card, "Left None"); + KUNIT_EXPECT_NULL(test, kc); + + kc = snd_soc_card_get_kcontrol(card, "Left"); + KUNIT_EXPECT_NULL(test, kc); + + kc = snd_soc_card_get_kcontrol(card, NULL); + KUNIT_EXPECT_NULL(test, kc); +} + +static void test_snd_soc_card_get_kcontrol_locked(struct kunit *test) +{ + struct soc_card_test_priv *priv = test->priv; + struct snd_soc_card *card = priv->card; + struct snd_kcontrol *kc, *kcw; + struct soc_mixer_control *mc; + int i, ret; + + ret = snd_soc_add_card_controls(card, test_card_controls, ARRAY_SIZE(test_card_controls)); + KUNIT_ASSERT_EQ(test, ret, 0); + + /* Look up every control */ + for (i = 0; i < ARRAY_SIZE(test_card_controls); ++i) { + down_read(&card->snd_card->controls_rwsem); + kc = snd_soc_card_get_kcontrol_locked(card, test_card_controls[i].name); + up_read(&card->snd_card->controls_rwsem); + KUNIT_EXPECT_NOT_ERR_OR_NULL_MSG(test, kc, "Failed to find '%s'\n", + test_card_controls[i].name); + if (!kc) + continue; + + /* Test that it is the correct control */ + mc = (struct soc_mixer_control *)kc->private_value; + KUNIT_EXPECT_EQ_MSG(test, mc->shift, i, "For '%s'\n", test_card_controls[i].name); + + down_write(&card->snd_card->controls_rwsem); + kcw = snd_soc_card_get_kcontrol_locked(card, test_card_controls[i].name); + up_write(&card->snd_card->controls_rwsem); + KUNIT_EXPECT_NOT_ERR_OR_NULL_MSG(test, kcw, "Failed to find '%s'\n", + test_card_controls[i].name); + + KUNIT_EXPECT_PTR_EQ(test, kc, kcw); + } + + /* Test some names that should not be found */ + down_read(&card->snd_card->controls_rwsem); + kc = snd_soc_card_get_kcontrol_locked(card, "None"); + up_read(&card->snd_card->controls_rwsem); + KUNIT_EXPECT_NULL(test, kc); + + down_read(&card->snd_card->controls_rwsem); + kc = snd_soc_card_get_kcontrol_locked(card, "Left None"); + up_read(&card->snd_card->controls_rwsem); + KUNIT_EXPECT_NULL(test, kc); + + down_read(&card->snd_card->controls_rwsem); + kc = snd_soc_card_get_kcontrol_locked(card, "Left"); + up_read(&card->snd_card->controls_rwsem); + KUNIT_EXPECT_NULL(test, kc); + + down_read(&card->snd_card->controls_rwsem); + kc = snd_soc_card_get_kcontrol_locked(card, NULL); + up_read(&card->snd_card->controls_rwsem); + KUNIT_EXPECT_NULL(test, kc); +} + +static int soc_card_test_case_init(struct kunit *test) +{ + struct soc_card_test_priv *priv; + int ret; + + priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + test->priv = priv; + + priv->card_dev = kunit_device_register(test, "sound-soc-card-test"); + priv->card_dev = get_device(priv->card_dev); + if (!priv->card_dev) + return -ENODEV; + + priv->card = kunit_kzalloc(test, sizeof(*priv->card), GFP_KERNEL); + if (!priv->card) + return -ENOMEM; + + priv->card->name = "soc-card-test"; + priv->card->dev = priv->card_dev; + priv->card->owner = THIS_MODULE; + + ret = snd_soc_register_card(priv->card); + if (!ret) + return ret; + + return 0; +} + +static void soc_card_test_case_exit(struct kunit *test) +{ + struct soc_card_test_priv *priv = test->priv; + + if (priv->card) + snd_soc_unregister_card(priv->card); + + if (priv->card_dev) + put_device(priv->card_dev); +} + +static struct kunit_case soc_card_test_cases[] = { + KUNIT_CASE(test_snd_soc_card_get_kcontrol), + KUNIT_CASE(test_snd_soc_card_get_kcontrol_locked), + {} +}; + +static struct kunit_suite soc_card_test_suite = { + .name = "soc-card", + .test_cases = soc_card_test_cases, + .init = soc_card_test_case_init, + .exit = soc_card_test_case_exit, +}; + +kunit_test_suites(&soc_card_test_suite); + +MODULE_DESCRIPTION("ASoC soc-card KUnit test"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/soc-card.c b/sound/soc/soc-card.c index 8a2f163da6bc..0a3104d4ad23 100644 --- a/sound/soc/soc-card.c +++ b/sound/soc/soc-card.c @@ -32,33 +32,20 @@ static inline int _soc_card_ret(struct snd_soc_card *card, struct snd_kcontrol *snd_soc_card_get_kcontrol_locked(struct snd_soc_card *soc_card, const char *name) { - struct snd_card *card = soc_card->snd_card; - struct snd_kcontrol *kctl; - - /* must be held read or write */ - lockdep_assert_held(&card->controls_rwsem); - if (unlikely(!name)) return NULL; - list_for_each_entry(kctl, &card->controls, list) - if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) - return kctl; - return NULL; + return snd_ctl_find_id_mixer_locked(soc_card->snd_card, name); } EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol_locked); struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card, const char *name) { - struct snd_card *card = soc_card->snd_card; - struct snd_kcontrol *kctl; - - down_read(&card->controls_rwsem); - kctl = snd_soc_card_get_kcontrol_locked(soc_card, name); - up_read(&card->controls_rwsem); + if (unlikely(!name)) + return NULL; - return kctl; + return snd_ctl_find_id_mixer(soc_card->snd_card, name); } EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol); diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index c50ca9e72d37..1afdb06499a3 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -145,17 +145,9 @@ static struct hdac_ext_stream *hda_assign_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_soc_dai *dai; struct hdac_ext_stream *hext_stream; - /* only allocate a stream_tag for the first DAI in the dailink */ - dai = snd_soc_rtd_to_cpu(rtd, 0); - if (dai == cpu_dai) - hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream); - else - hext_stream = snd_soc_dai_get_dma_data(dai, substream); - + hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream); if (!hext_stream) return NULL; @@ -168,14 +160,9 @@ static void hda_release_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai struct snd_pcm_substream *substream) { struct hdac_ext_stream *hext_stream = hda_get_hext_stream(sdev, cpu_dai, substream); - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_soc_dai *dai; - /* only release a stream_tag for the first DAI in the dailink */ - dai = snd_soc_rtd_to_cpu(rtd, 0); - if (dai == cpu_dai) - snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK); snd_soc_dai_set_dma_data(cpu_dai, substream, NULL); + snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK); } static void hda_setup_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream, @@ -435,28 +422,6 @@ out: return ret; } -static struct hdac_ext_stream *sdw_hda_ipc4_get_hext_stream(struct snd_sof_dev *sdev, - struct snd_soc_dai *cpu_dai, - struct snd_pcm_substream *substream) -{ - struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); - struct snd_sof_widget *swidget = w->dobj.private; - struct snd_sof_dai *dai = swidget->private; - struct sof_ipc4_copier *ipc4_copier = dai->private; - struct sof_ipc4_alh_configuration_blob *blob; - - blob = (struct sof_ipc4_alh_configuration_blob *)ipc4_copier->copier_config; - - /* - * Starting with ACE_2_0, re-setting the device_count is mandatory to avoid using - * the multi-gateway firmware configuration. The DMA hardware can take care of - * multiple links without needing any firmware assistance - */ - blob->alh_cfg.device_count = 1; - - return hda_ipc4_get_hext_stream(sdev, cpu_dai, substream); -} - static const struct hda_dai_widget_dma_ops hda_ipc4_dma_ops = { .get_hext_stream = hda_ipc4_get_hext_stream, .assign_hext_stream = hda_assign_hext_stream, @@ -498,7 +463,7 @@ static const struct hda_dai_widget_dma_ops dmic_ipc4_dma_ops = { }; static const struct hda_dai_widget_dma_ops sdw_ipc4_dma_ops = { - .get_hext_stream = sdw_hda_ipc4_get_hext_stream, + .get_hext_stream = hda_ipc4_get_hext_stream, .assign_hext_stream = hda_assign_hext_stream, .release_hext_stream = hda_release_hext_stream, .setup_hext_stream = hda_setup_hext_stream, diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index c1682bcdb5a6..3f2fd84907d2 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -29,14 +29,6 @@ static bool hda_use_tplg_nhlt; module_param_named(sof_use_tplg_nhlt, hda_use_tplg_nhlt, bool, 0444); MODULE_PARM_DESC(sof_use_tplg_nhlt, "SOF topology nhlt override"); -static struct snd_sof_dev *widget_to_sdev(struct snd_soc_dapm_widget *w) -{ - struct snd_sof_widget *swidget = w->dobj.private; - struct snd_soc_component *component = swidget->scomp; - - return snd_soc_component_get_drvdata(component); -} - int hda_dai_config(struct snd_soc_dapm_widget *w, unsigned int flags, struct snd_sof_dai_config_data *data) { @@ -221,15 +213,15 @@ static int __maybe_unused hda_dai_hw_free(struct snd_pcm_substream *substream, return hda_link_dma_cleanup(substream, hext_stream, cpu_dai); } -static int __maybe_unused hda_dai_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) +static int __maybe_unused hda_dai_hw_params_data(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai, + struct snd_sof_dai_config_data *data, + unsigned int flags) { struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream); const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai); struct hdac_ext_stream *hext_stream; - struct snd_sof_dai_config_data data = { 0 }; - unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS; struct snd_sof_dev *sdev = widget_to_sdev(w); int ret; @@ -249,9 +241,19 @@ static int __maybe_unused hda_dai_hw_params(struct snd_pcm_substream *substream, hext_stream = ops->get_hext_stream(sdev, dai, substream); flags |= SOF_DAI_CONFIG_FLAGS_2_STEP_STOP << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT; - data.dai_data = hdac_stream(hext_stream)->stream_tag - 1; + data->dai_data = hdac_stream(hext_stream)->stream_tag - 1; - return hda_dai_config(w, flags, &data); + return hda_dai_config(w, flags, data); +} + +static int __maybe_unused hda_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_sof_dai_config_data data = { 0 }; + unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS; + + return hda_dai_hw_params_data(substream, params, dai, &data, flags); } /* @@ -341,11 +343,14 @@ static struct sof_ipc4_copier *widget_to_copier(struct snd_soc_dapm_widget *w) return ipc4_copier; } -static int non_hda_dai_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *cpu_dai) +static int non_hda_dai_hw_params_data(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *cpu_dai, + struct snd_sof_dai_config_data *data, + unsigned int flags) { struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct sof_ipc4_dma_config_tlv *dma_config_tlv; const struct hda_dai_widget_dma_ops *ops; struct sof_ipc4_dma_config *dma_config; @@ -353,6 +358,8 @@ static int non_hda_dai_hw_params(struct snd_pcm_substream *substream, struct hdac_ext_stream *hext_stream; struct hdac_stream *hstream; struct snd_sof_dev *sdev; + struct snd_soc_dai *dai; + int cpu_dai_id; int stream_id; int ret; @@ -363,9 +370,9 @@ static int non_hda_dai_hw_params(struct snd_pcm_substream *substream, } /* use HDaudio stream handling */ - ret = hda_dai_hw_params(substream, params, cpu_dai); + ret = hda_dai_hw_params_data(substream, params, cpu_dai, data, flags); if (ret < 0) { - dev_err(cpu_dai->dev, "%s: hda_dai_hw_params failed: %d\n", __func__, ret); + dev_err(cpu_dai->dev, "%s: hda_dai_hw_params_data failed: %d\n", __func__, ret); return ret; } @@ -392,7 +399,12 @@ static int non_hda_dai_hw_params(struct snd_pcm_substream *substream, /* configure TLV */ ipc4_copier = widget_to_copier(w); - dma_config_tlv = &ipc4_copier->dma_config_tlv; + for_each_rtd_cpu_dais(rtd, cpu_dai_id, dai) { + if (dai == cpu_dai) + break; + } + + dma_config_tlv = &ipc4_copier->dma_config_tlv[cpu_dai_id]; dma_config_tlv->type = SOF_IPC4_GTW_DMA_CONFIG_ID; /* dma_config_priv_size is zero */ dma_config_tlv->length = sizeof(dma_config_tlv->dma_config); @@ -403,13 +415,27 @@ static int non_hda_dai_hw_params(struct snd_pcm_substream *substream, dma_config->pre_allocated_by_host = 1; dma_config->dma_channel_id = stream_id - 1; dma_config->stream_id = stream_id; - dma_config->dma_stream_channel_map.device_count = 0; /* mapping not used */ + /* + * Currently we use a DMA for each device in ALH blob. The device will + * be copied in sof_ipc4_prepare_copier_module. + */ + dma_config->dma_stream_channel_map.device_count = 1; dma_config->dma_priv_config_size = 0; skip_tlv: return 0; } +static int non_hda_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *cpu_dai) +{ + struct snd_sof_dai_config_data data = { 0 }; + unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS; + + return non_hda_dai_hw_params_data(substream, params, cpu_dai, &data, flags); +} + static int non_hda_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { @@ -436,15 +462,29 @@ static const struct snd_soc_dai_ops dmic_dai_ops = { int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai, - int link_id) + int link_id, + int intel_alh_id) { struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct sof_ipc4_dma_config_tlv *dma_config_tlv; + struct snd_sof_dai_config_data data = { 0 }; + unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS; const struct hda_dai_widget_dma_ops *ops; + struct sof_ipc4_dma_config *dma_config; + struct sof_ipc4_copier *ipc4_copier; struct hdac_ext_stream *hext_stream; + struct snd_soc_dai *dai; struct snd_sof_dev *sdev; + bool cpu_dai_found = false; + int cpu_dai_id; + int ch_mask; int ret; + int i; - ret = non_hda_dai_hw_params(substream, params, cpu_dai); + data.dai_index = (link_id << 8) | cpu_dai->id; + data.dai_node_id = intel_alh_id; + ret = non_hda_dai_hw_params_data(substream, params, cpu_dai, &data, flags); if (ret < 0) { dev_err(cpu_dai->dev, "%s: non_hda_dai_hw_params failed %d\n", __func__, ret); return ret; @@ -457,9 +497,25 @@ int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream, if (!hext_stream) return -ENODEV; - /* in the case of SoundWire we need to program the PCMSyCM registers */ + /* + * in the case of SoundWire we need to program the PCMSyCM registers. In case + * of aggregated devices, we need to define the channel mask for each sublink + * by reconstructing the split done in soc-pcm.c + */ + for_each_rtd_cpu_dais(rtd, cpu_dai_id, dai) { + if (dai == cpu_dai) { + cpu_dai_found = true; + break; + } + } + + if (!cpu_dai_found) + return -ENODEV; + + ch_mask = GENMASK(params_channels(params) - 1, 0); + ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id, cpu_dai->id, - GENMASK(params_channels(params) - 1, 0), + ch_mask, hdac_stream(hext_stream)->stream_tag, substream->stream); if (ret < 0) { @@ -468,6 +524,22 @@ int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream, return ret; } + ipc4_copier = widget_to_copier(w); + dma_config_tlv = &ipc4_copier->dma_config_tlv[cpu_dai_id]; + dma_config = &dma_config_tlv->dma_config; + dma_config->dma_stream_channel_map.mapping[0].device = data.dai_index; + dma_config->dma_stream_channel_map.mapping[0].channel_mask = ch_mask; + + /* + * copy the dma_config_tlv to all ipc4_copier in the same link. Because only one copier + * will be handled in sof_ipc4_prepare_copier_module. + */ + for_each_rtd_cpu_dais(rtd, i, dai) { + w = snd_soc_dai_get_widget(dai, substream->stream); + ipc4_copier = widget_to_copier(w); + memcpy(&ipc4_copier->dma_config_tlv[cpu_dai_id], dma_config_tlv, + sizeof(*dma_config_tlv)); + } return 0; } diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index e26b8fd682e5..d38dc43c2f1c 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -31,6 +31,7 @@ #include "../sof-audio.h" #include "../sof-pci-dev.h" #include "../ops.h" +#include "../ipc4-topology.h" #include "hda.h" #include "telemetry.h" @@ -145,12 +146,37 @@ static int sdw_params_stream(struct device *dev, data.dai_index = (params_data->link_id << 8) | d->id; data.dai_data = params_data->alh_stream_id; + data.dai_node_id = data.dai_data; return hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_HW_PARAMS, &data); } +static int sdw_params_free(struct device *dev, struct sdw_intel_stream_free_data *free_data) +{ + struct snd_soc_dai *d = free_data->dai; + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(d, free_data->substream->stream); + struct snd_sof_dev *sdev = widget_to_sdev(w); + + if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) { + struct snd_sof_widget *swidget = w->dobj.private; + struct snd_sof_dai *dai = swidget->private; + struct sof_ipc4_copier_data *copier_data; + struct sof_ipc4_copier *ipc4_copier; + + ipc4_copier = dai->private; + ipc4_copier->dai_index = 0; + copier_data = &ipc4_copier->data; + + /* clear the node ID */ + copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK; + } + + return 0; +} + struct sdw_intel_ops sdw_callback = { .params_stream = sdw_params_stream, + .free_stream = sdw_params_free, }; static int sdw_ace2x_params_stream(struct device *dev, @@ -159,7 +185,8 @@ static int sdw_ace2x_params_stream(struct device *dev, return sdw_hda_dai_hw_params(params_data->substream, params_data->hw_params, params_data->dai, - params_data->link_id); + params_data->link_id, + params_data->alh_stream_id); } static int sdw_ace2x_free_stream(struct device *dev, diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index b36eb7c78913..f530a05cfc92 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -844,7 +844,8 @@ static inline bool hda_common_check_sdw_irq(struct snd_sof_dev *sdev) int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai, - int link_id); + int link_id, + int intel_alh_id); int sdw_hda_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai, @@ -999,4 +1000,12 @@ int hda_dai_config(struct snd_soc_dapm_widget *w, unsigned int flags, int hda_link_dma_cleanup(struct snd_pcm_substream *substream, struct hdac_ext_stream *hext_stream, struct snd_soc_dai *cpu_dai); +static inline struct snd_sof_dev *widget_to_sdev(struct snd_soc_dapm_widget *w) +{ + struct snd_sof_widget *swidget = w->dobj.private; + struct snd_soc_component *component = swidget->scomp; + + return snd_soc_component_get_drvdata(component); +} + #endif diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 651bff2122a3..49eab1a093a2 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -1276,7 +1276,6 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget) } if (ipc4_copier->dai_type == SOF_DAI_INTEL_ALH) { - struct sof_ipc4_copier_data *copier_data = &ipc4_copier->data; struct sof_ipc4_alh_configuration_blob *blob; unsigned int group_id; @@ -1286,9 +1285,6 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget) ALH_MULTI_GTW_BASE; ida_free(&alh_group_ida, group_id); } - - /* clear the node ID */ - copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK; } } @@ -1453,6 +1449,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, u32 deep_buffer_dma_ms = 0; int output_fmt_index; bool single_output_format; + int i; dev_dbg(sdev->dev, "copier %s, type %d", swidget->widget->name, swidget->id); @@ -1670,6 +1667,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, */ if (ipc4_copier->dai_type == SOF_DAI_INTEL_ALH) { struct sof_ipc4_alh_configuration_blob *blob; + struct sof_ipc4_dma_config *dma_config; struct sof_ipc4_copier_data *alh_data; struct sof_ipc4_copier *alh_copier; struct snd_sof_widget *w; @@ -1678,7 +1676,6 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, u32 ch_map; u32 step; u32 mask; - int i; blob = (struct sof_ipc4_alh_configuration_blob *)ipc4_copier->copier_config; @@ -1702,6 +1699,8 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, */ i = 0; list_for_each_entry(w, &sdev->widget_list, list) { + u32 node_type; + if (w->widget->sname && strcmp(w->widget->sname, swidget->widget->sname)) continue; @@ -1709,7 +1708,22 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, dai = w->private; alh_copier = (struct sof_ipc4_copier *)dai->private; alh_data = &alh_copier->data; - blob->alh_cfg.mapping[i].device = alh_data->gtw_cfg.node_id; + node_type = SOF_IPC4_GET_NODE_TYPE(alh_data->gtw_cfg.node_id); + blob->alh_cfg.mapping[i].device = SOF_IPC4_NODE_TYPE(node_type); + blob->alh_cfg.mapping[i].device |= + SOF_IPC4_NODE_INDEX(alh_copier->dai_index); + + /* + * The mapping[i] device in ALH blob should be the same as the + * dma_config_tlv[i] mapping device if a dma_config_tlv is present. + * The device id will be used for DMA tlv mapping purposes. + */ + if (ipc4_copier->dma_config_tlv[i].length) { + dma_config = &ipc4_copier->dma_config_tlv[i].dma_config; + blob->alh_cfg.mapping[i].device = + dma_config->dma_stream_channel_map.mapping[0].device; + } + /* * Set the same channel mask for playback as the audio data is * duplicated for all speakers. For capture, split the channels @@ -1788,19 +1802,18 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, gtw_cfg_config_length = copier_data->gtw_cfg.config_length * 4; ipc_size = sizeof(*copier_data) + gtw_cfg_config_length; - if (ipc4_copier->dma_config_tlv.type == SOF_IPC4_GTW_DMA_CONFIG_ID && - ipc4_copier->dma_config_tlv.length) { - dma_config_tlv_size = sizeof(ipc4_copier->dma_config_tlv) + - ipc4_copier->dma_config_tlv.dma_config.dma_priv_config_size; - - /* paranoia check on TLV size/length */ - if (dma_config_tlv_size != ipc4_copier->dma_config_tlv.length + - sizeof(uint32_t) * 2) { - dev_err(sdev->dev, "Invalid configuration, TLV size %d length %d\n", - dma_config_tlv_size, ipc4_copier->dma_config_tlv.length); - return -EINVAL; - } + dma_config_tlv_size = 0; + for (i = 0; i < SOF_IPC4_DMA_DEVICE_MAX_COUNT; i++) { + if (ipc4_copier->dma_config_tlv[i].type != SOF_IPC4_GTW_DMA_CONFIG_ID) + continue; + dma_config_tlv_size += ipc4_copier->dma_config_tlv[i].length; + dma_config_tlv_size += + ipc4_copier->dma_config_tlv[i].dma_config.dma_priv_config_size; + dma_config_tlv_size += (sizeof(ipc4_copier->dma_config_tlv[i]) - + sizeof(ipc4_copier->dma_config_tlv[i].dma_config)); + } + if (dma_config_tlv_size) { ipc_size += dma_config_tlv_size; /* we also need to increase the size at the gtw level */ @@ -2812,17 +2825,24 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget * case SOF_DAI_INTEL_HDA: gtw_attr = ipc4_copier->gtw_attr; gtw_attr->lp_buffer_alloc = pipeline->lp_mode; - fallthrough; + if (flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS) { + copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK; + copier_data->gtw_cfg.node_id |= SOF_IPC4_NODE_INDEX(data->dai_data); + } + break; case SOF_DAI_INTEL_ALH: /* * Do not clear the node ID when this op is invoked with * SOF_DAI_CONFIG_FLAGS_HW_FREE. It is needed to free the group_ida during - * unprepare. + * unprepare. The node_id for multi-gateway DAI's will be overwritten with the + * group_id during copier's ipc_prepare op. */ if (flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS) { + ipc4_copier->dai_index = data->dai_node_id; copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK; - copier_data->gtw_cfg.node_id |= SOF_IPC4_NODE_INDEX(data->dai_data); + copier_data->gtw_cfg.node_id |= SOF_IPC4_NODE_INDEX(data->dai_node_id); } + break; case SOF_DAI_INTEL_DMIC: case SOF_DAI_INTEL_SSP: diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h index dce174a190dd..6e33208a357f 100644 --- a/sound/soc/sof/ipc4-topology.h +++ b/sound/soc/sof/ipc4-topology.h @@ -45,6 +45,7 @@ #define SOF_IPC4_NODE_INDEX_MASK 0xFF #define SOF_IPC4_NODE_INDEX(x) ((x) & SOF_IPC4_NODE_INDEX_MASK) #define SOF_IPC4_NODE_TYPE(x) ((x) << 8) +#define SOF_IPC4_GET_NODE_TYPE(node_id) ((node_id) >> 8) /* Node ID for SSP type DAI copiers */ #define SOF_IPC4_NODE_INDEX_INTEL_SSP(x) (((x) & 0xf) << 4) @@ -313,7 +314,7 @@ struct sof_ipc4_copier { struct sof_ipc4_gtw_attributes *gtw_attr; u32 dai_type; int dai_index; - struct sof_ipc4_dma_config_tlv dma_config_tlv; + struct sof_ipc4_dma_config_tlv dma_config_tlv[SOF_IPC4_DMA_DEVICE_MAX_COUNT]; }; /** diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 33d576b17647..7b88e24b7701 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -196,9 +196,8 @@ static int sof_pcm_hw_free(struct snd_soc_component *component, { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); struct snd_sof_pcm *spcm; - int ret, err = 0; + int ret; /* nothing to do for BE */ if (rtd->dai_link->no_pcm) @@ -211,42 +210,18 @@ static int sof_pcm_hw_free(struct snd_soc_component *component, dev_dbg(component->dev, "pcm: free stream %d dir %d\n", spcm->pcm.pcm_id, substream->stream); - if (spcm->prepared[substream->stream]) { - /* stop DMA first if needed */ - if (pcm_ops && pcm_ops->platform_stop_during_hw_free) - snd_sof_pcm_platform_trigger(sdev, substream, SNDRV_PCM_TRIGGER_STOP); - - /* free PCM in the DSP */ - if (pcm_ops && pcm_ops->hw_free) { - ret = pcm_ops->hw_free(component, substream); - if (ret < 0) - err = ret; - } - - spcm->prepared[substream->stream] = false; - } - - /* reset DMA */ - ret = snd_sof_pcm_platform_hw_free(sdev, substream); - if (ret < 0) { - dev_err(component->dev, "error: platform hw free failed\n"); - err = ret; - } - - /* free the DAPM widget list */ - ret = sof_widget_list_free(sdev, spcm, substream->stream); - if (ret < 0) - err = ret; + ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, true); cancel_work_sync(&spcm->stream[substream->stream].period_elapsed_work); - return err; + return ret; } static int sof_pcm_prepare(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct snd_sof_pcm *spcm; int ret; @@ -258,8 +233,18 @@ static int sof_pcm_prepare(struct snd_soc_component *component, if (!spcm) return -EINVAL; - if (spcm->prepared[substream->stream]) - return 0; + if (spcm->prepared[substream->stream]) { + if (!spcm->pending_stop[substream->stream]) + return 0; + + /* + * this case should be reached in case of xruns where we absolutely + * want to free-up and reset all PCM/DMA resources + */ + ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, true); + if (ret < 0) + return ret; + } dev_dbg(component->dev, "pcm: prepare stream %d dir %d\n", spcm->pcm.pcm_id, substream->stream); @@ -302,6 +287,8 @@ static int sof_pcm_trigger(struct snd_soc_component *component, dev_dbg(component->dev, "pcm: trigger stream %d dir %d cmd %d\n", spcm->pcm.pcm_id, substream->stream, cmd); + spcm->pending_stop[substream->stream] = false; + switch (cmd) { case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ipc_first = true; @@ -371,6 +358,15 @@ static int sof_pcm_trigger(struct snd_soc_component *component, /* invoke platform trigger to stop DMA even if pcm_ops isn't set or if it failed */ if (!pcm_ops || !pcm_ops->platform_stop_during_hw_free) snd_sof_pcm_platform_trigger(sdev, substream, cmd); + + /* + * set the pending_stop flag to indicate that pipeline stop has been delayed. + * This will be used later to stop the pipelines during prepare when recovering + * from xruns. + */ + if (pcm_ops && pcm_ops->platform_stop_during_hw_free && + cmd == SNDRV_PCM_TRIGGER_STOP) + spcm->pending_stop[substream->stream] = true; break; default: break; diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index e693dcb475e4..32fef64ef10d 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -834,35 +834,48 @@ int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *subs { const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); int ret; + int err = 0; if (spcm->prepared[substream->stream]) { /* stop DMA first if needed */ if (pcm_ops && pcm_ops->platform_stop_during_hw_free) snd_sof_pcm_platform_trigger(sdev, substream, SNDRV_PCM_TRIGGER_STOP); - /* Send PCM_FREE IPC to reset pipeline */ + /* free PCM in the DSP */ if (pcm_ops && pcm_ops->hw_free) { ret = pcm_ops->hw_free(sdev->component, substream); - if (ret < 0) - return ret; + if (ret < 0) { + dev_err(sdev->dev, "%s: pcm_ops hw_free failed %d\n", + __func__, ret); + err = ret; + } } spcm->prepared[substream->stream] = false; + spcm->pending_stop[substream->stream] = false; } /* reset the DMA */ ret = snd_sof_pcm_platform_hw_free(sdev, substream); - if (ret < 0) - return ret; + if (ret < 0) { + dev_err(sdev->dev, "%s: platform hw free failed %d\n", + __func__, ret); + if (!err) + err = ret; + } /* free widget list */ if (free_widget_list) { ret = sof_widget_list_free(sdev, spcm, dir); - if (ret < 0) - dev_err(sdev->dev, "failed to free widgets during suspend\n"); + if (ret < 0) { + dev_err(sdev->dev, "%s: sof_widget_list_free failed %d\n", + __func__, ret); + if (!err) + err = ret; + } } - return ret; + return err; } /* diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 9ea2ac5adac7..80a5bd69ef1c 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -91,6 +91,7 @@ struct snd_sof_pcm; struct snd_sof_dai_config_data { int dai_index; int dai_data; /* contains DAI-specific information */ + int dai_node_id; /* contains DAI-specific information for Gateway configuration */ }; /** @@ -341,6 +342,7 @@ struct snd_sof_pcm { struct list_head list; /* list in sdev pcm list */ struct snd_pcm_hw_params params[2]; bool prepared[2]; /* PCM_PARAMS set successfully */ + bool pending_stop[2]; /* only used if (!pcm_ops->platform_stop_during_hw_free) */ }; struct snd_sof_led_control { |