From 4f66a9ef37d3c09917a1edc065ff68b895e0b163 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 15 Nov 2021 08:59:44 +0100 Subject: ALSA: hda: intel: More comprehensive PM runtime setup for controller driver Currently we haven't explicitly enable and allow/forbid the runtime PM at the probe and the remove phases of HD-audio controller driver, and this was the reason of a GPF mentioned in the commit e81478bbe7a1 ("ALSA: hda: fix general protection fault in azx_runtime_idle"); namely, even after the resources are released, the runtime PM might be still invoked by the bound graphics driver during the remove of the controller driver. Although we've fixed it by clearing the drvdata reference, it'd be also better to cover the runtime PM issue more properly. This patch adds a few more pm_runtime_*() calls at the probe and the remove time for setting and cleaning up the runtime PM. Particularly, now more explicitly pm_runtime_enable() and _disable() get called as well as pm_runtime_forbid() call at the remove callback, so that a use-after-free should be avoided. Reported-by: Kai Vehmanen Reviewed-by: Kai Vehmanen Tested-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211110210307.1172004-1-kai.vehmanen@linux.intel.com Link: https://lore.kernel.org/r/20211115075944.6972-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index fe51163f2d82..45e85180048c 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1347,8 +1347,14 @@ static void azx_free(struct azx *chip) if (hda->freed) return; - if (azx_has_pm_runtime(chip) && chip->running) + if (azx_has_pm_runtime(chip) && chip->running) { pm_runtime_get_noresume(&pci->dev); + pm_runtime_disable(&pci->dev); + pm_runtime_set_suspended(&pci->dev); + pm_runtime_forbid(&pci->dev); + pm_runtime_dont_use_autosuspend(&pci->dev); + } + chip->running = 0; azx_del_card_list(chip); @@ -2322,6 +2328,8 @@ static int azx_probe_continue(struct azx *chip) if (azx_has_pm_runtime(chip)) { pm_runtime_use_autosuspend(&pci->dev); pm_runtime_allow(&pci->dev); + pm_runtime_set_active(&pci->dev); + pm_runtime_enable(&pci->dev); pm_runtime_put_autosuspend(&pci->dev); } -- cgit v1.2.3 From 77fffb83933ad9e514ea0c7fd93b28cabcdea311 Mon Sep 17 00:00:00 2001 From: Vincent Knecht Date: Sun, 31 Oct 2021 22:09:55 +0100 Subject: ASoC: dt-bindings: nxp, tfa989x: Add rcv-gpios property for tfa9897 Add optional rcv-gpios property specific to tfa9897 receiver mode. Signed-off-by: Vincent Knecht Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20211031210956.812101-2-vincent.knecht@mailoo.org Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/nxp,tfa989x.yaml | 41 ++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/nxp,tfa989x.yaml b/Documentation/devicetree/bindings/sound/nxp,tfa989x.yaml index 7667471be1e4..b9b1dba40856 100644 --- a/Documentation/devicetree/bindings/sound/nxp,tfa989x.yaml +++ b/Documentation/devicetree/bindings/sound/nxp,tfa989x.yaml @@ -24,11 +24,23 @@ properties: '#sound-dai-cells': const: 0 + rcv-gpios: + description: optional GPIO to be asserted when receiver mode is enabled. + sound-name-prefix: true vddd-supply: description: regulator phandle for the VDDD power supply. +if: + not: + properties: + compatible: + const: nxp,tfa9897 +then: + properties: + rcv-gpios: false + required: - compatible - reg @@ -55,3 +67,32 @@ examples: #sound-dai-cells = <0>; }; }; + + - | + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + + speaker_codec_top: audio-codec@34 { + compatible = "nxp,tfa9897"; + reg = <0x34>; + vddd-supply = <&pm8916_l6>; + rcv-gpios = <&msmgpio 50 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&speaker_top_default>; + sound-name-prefix = "Speaker Top"; + #sound-dai-cells = <0>; + }; + + speaker_codec_bottom: audio-codec@36 { + compatible = "nxp,tfa9897"; + reg = <0x36>; + vddd-supply = <&pm8916_l6>; + rcv-gpios = <&msmgpio 111 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&speaker_bottom_default>; + sound-name-prefix = "Speaker Bottom"; + #sound-dai-cells = <0>; + }; + }; -- cgit v1.2.3 From 9da52c39b33e7bd9c1f56175c0466fa468d7f145 Mon Sep 17 00:00:00 2001 From: Vincent Knecht Date: Sun, 31 Oct 2021 22:09:56 +0100 Subject: ASoC: codecs: tfa989x: Add support for tfa9897 optional rcv-gpios Some OEM use a GPIO in addition to the tfa9897 RCV bit to switch between loudspeaker and earpiece/receiver mode. Add support for the GPIO switching by specifying rcv-gpios in DT. Signed-off-by: Vincent Knecht Link: https://lore.kernel.org/r/20211031210956.812101-3-vincent.knecht@mailoo.org Signed-off-by: Mark Brown --- sound/soc/codecs/tfa989x.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/tfa989x.c b/sound/soc/codecs/tfa989x.c index eb2a7870148d..dc86852752c5 100644 --- a/sound/soc/codecs/tfa989x.c +++ b/sound/soc/codecs/tfa989x.c @@ -7,6 +7,7 @@ * Copyright (C) 2013 Sony Mobile Communications Inc. */ +#include #include #include #include @@ -56,6 +57,7 @@ struct tfa989x_rev { struct tfa989x { const struct tfa989x_rev *rev; struct regulator *vddd_supply; + struct gpio_desc *rcv_gpiod; }; static bool tfa989x_writeable_reg(struct device *dev, unsigned int reg) @@ -99,10 +101,20 @@ static const struct snd_soc_dapm_route tfa989x_dapm_routes[] = { {"Amp Input", "Right", "AIFINR"}, }; +static int tfa989x_put_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct tfa989x *tfa989x = snd_soc_component_get_drvdata(component); + + gpiod_set_value_cansleep(tfa989x->rcv_gpiod, ucontrol->value.enumerated.item[0]); + + return snd_soc_put_enum_double(kcontrol, ucontrol); +} + static const char * const mode_text[] = { "Speaker", "Receiver" }; static SOC_ENUM_SINGLE_DECL(mode_enum, TFA989X_I2SREG, TFA989X_I2SREG_RCV, mode_text); static const struct snd_kcontrol_new tfa989x_mode_controls[] = { - SOC_ENUM("Mode", mode_enum), + SOC_ENUM_EXT("Mode", mode_enum, snd_soc_get_enum_double, tfa989x_put_mode), }; static int tfa989x_probe(struct snd_soc_component *component) @@ -301,6 +313,12 @@ static int tfa989x_i2c_probe(struct i2c_client *i2c) return dev_err_probe(dev, PTR_ERR(tfa989x->vddd_supply), "Failed to get vddd regulator\n"); + if (tfa989x->rev->rev == TFA9897_REVISION) { + tfa989x->rcv_gpiod = devm_gpiod_get_optional(dev, "rcv", GPIOD_OUT_LOW); + if (IS_ERR(tfa989x->rcv_gpiod)) + return PTR_ERR(tfa989x->rcv_gpiod); + } + regmap = devm_regmap_init_i2c(i2c, &tfa989x_regmap); if (IS_ERR(regmap)) return PTR_ERR(regmap); -- cgit v1.2.3 From 168eed447129899611098219b70ef97b605bc6e1 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 2 Nov 2021 12:10:17 +0200 Subject: ASoC: SOF: IPC: Add new IPC command to free trace DMA Add a new SOF_IPC_TRACE_DMA_FREE IPC command to stop and free trace DMA in the FW. Signed-off-by: Ranjani Sridharan Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Signed-off-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20211102101019.14037-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/sof/header.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/sound/sof/header.h b/include/sound/sof/header.h index 4c747c52e01b..b97a76bcb655 100644 --- a/include/sound/sof/header.h +++ b/include/sound/sof/header.h @@ -119,6 +119,7 @@ #define SOF_IPC_TRACE_DMA_POSITION SOF_CMD_TYPE(0x002) #define SOF_IPC_TRACE_DMA_PARAMS_EXT SOF_CMD_TYPE(0x003) #define SOF_IPC_TRACE_FILTER_UPDATE SOF_CMD_TYPE(0x004) /**< ABI3.17 */ +#define SOF_IPC_TRACE_DMA_FREE SOF_CMD_TYPE(0x005) /**< ABI3.20 */ /* debug */ #define SOF_IPC_DEBUG_MEM_USAGE SOF_CMD_TYPE(0x001) -- cgit v1.2.3 From b4e2d7ce132bc4337916662f8e699420377132d9 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 2 Nov 2021 12:10:18 +0200 Subject: ASoC: SOF: IPC: update ipc_log_header() Parse all the trace DMA IPC commands in ipc_log_header(). Signed-off-by: Ranjani Sridharan Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Signed-off-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20211102101019.14037-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index e6c53c6c470e..a4036d0b3d3a 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -173,7 +173,22 @@ static void ipc_log_header(struct device *dev, u8 *text, u32 cmd) } break; case SOF_IPC_GLB_TRACE_MSG: - str = "GLB_TRACE_MSG"; break; + str = "GLB_TRACE_MSG"; + switch (type) { + case SOF_IPC_TRACE_DMA_PARAMS: + str2 = "DMA_PARAMS"; break; + case SOF_IPC_TRACE_DMA_POSITION: + str2 = "DMA_POSITION"; break; + case SOF_IPC_TRACE_DMA_PARAMS_EXT: + str2 = "DMA_PARAMS_EXT"; break; + case SOF_IPC_TRACE_FILTER_UPDATE: + str2 = "FILTER_UPDATE"; break; + case SOF_IPC_TRACE_DMA_FREE: + str2 = "DMA_FREE"; break; + default: + str2 = "unknown type"; break; + } + break; case SOF_IPC_GLB_TEST_MSG: str = "GLB_TEST_MSG"; switch (type) { -- cgit v1.2.3 From 48b5b6a56002569881d18be56deaddad045df918 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 2 Nov 2021 12:10:19 +0200 Subject: ASoC: SOF: trace: send DMA_TRACE_FREE IPC during release Send the DMA_TRACE_FREE IPC during release to stop and free the trace DMA in the DSP. Signed-off-by: Ranjani Sridharan Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Signed-off-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20211102101019.14037-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/core.c | 2 +- sound/soc/sof/trace.c | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 2c3de295f11f..9ec9ef8ed525 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -363,6 +363,7 @@ int snd_sof_device_remove(struct device *dev) cancel_work_sync(&sdev->probe_work); if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) { + snd_sof_free_trace(sdev); ret = snd_sof_dsp_power_down_notify(sdev); if (ret < 0) dev_warn(dev, "error: %d failed to prepare DSP for device removal", @@ -370,7 +371,6 @@ int snd_sof_device_remove(struct device *dev) snd_sof_ipc_free(sdev); snd_sof_free_debug(sdev); - snd_sof_free_trace(sdev); } /* diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c index e3afc3dac7d1..f13024c8ebf2 100644 --- a/sound/soc/sof/trace.c +++ b/sound/soc/sof/trace.c @@ -539,6 +539,10 @@ EXPORT_SYMBOL(snd_sof_trace_notify_for_error); void snd_sof_release_trace(struct snd_sof_dev *sdev) { + struct sof_ipc_fw_ready *ready = &sdev->fw_ready; + struct sof_ipc_fw_version *v = &ready->version; + struct sof_ipc_cmd_hdr hdr; + struct sof_ipc_reply ipc_reply; int ret; if (!sdev->dtrace_is_supported || !sdev->dtrace_is_enabled) @@ -549,6 +553,20 @@ void snd_sof_release_trace(struct snd_sof_dev *sdev) dev_err(sdev->dev, "error: snd_sof_dma_trace_trigger: stop: %d\n", ret); + /* + * stop and free trace DMA in the DSP. TRACE_DMA_FREE is only supported from + * ABI 3.20.0 onwards + */ + if (v->abi_version >= SOF_ABI_VER(3, 20, 0)) { + hdr.size = sizeof(hdr); + hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_DMA_FREE; + + ret = sof_ipc_tx_message(sdev->ipc, hdr.cmd, &hdr, hdr.size, + &ipc_reply, sizeof(ipc_reply)); + if (ret < 0) + dev_err(sdev->dev, "DMA_TRACE_FREE failed with error: %d\n", ret); + } + ret = snd_sof_dma_trace_release(sdev); if (ret < 0) dev_err(sdev->dev, -- cgit v1.2.3 From dd31ddd81904070d0a9cafd5499d3210a322f8af Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 27 Oct 2021 10:18:15 +0800 Subject: ASoC: intel: sof_sdw: return the original error number We don't want to convert create_sdw_dailink()'s return value to -ENOMEM. Signed-off-by: Bard Liao Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Reviewed-by: Rander Wang Link: https://lore.kernel.org/r/20211027021824.24776-2-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 77219c3f8766..6d59462880fb 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -1203,7 +1203,7 @@ static int sof_card_dai_links_create(struct device *dev, &ignore_pch_dmic); if (ret < 0) { dev_err(dev, "failed to create dai link %d", be_id); - return -ENOMEM; + return ret; } } -- cgit v1.2.3 From 0527b19fa4f390a6054612e1fa1dd4f8efc96739 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 27 Oct 2021 10:18:16 +0800 Subject: ASoC: Intel: sof_sdw: fix jack detection on HP Spectre x360 convertible Tests on device show the JD2 mode does not work at all, the 'Headphone Jack' and 'Headset Mic Jack' are shown as 'on' always. JD1 seems to be the better option, with at least a change between the two cases. Jack not plugged-in: [root@fedora ~]# amixer -Dhw:0 cget numid=12 numid=12,iface=CARD,name='Headphone Jack' ; type=BOOLEAN,access=r-------,values=1 : values=off [root@fedora ~]# amixer -Dhw:0 cget numid=13 numid=13,iface=CARD,name='Headset Mic Jack' ; type=BOOLEAN,access=r-------,values=1 : values=off Jack plugged-in: [root@fedora ~]# amixer -Dhw:0 cget numid=13 numid=13,iface=CARD,name='Headset Mic Jack' ; type=BOOLEAN,access=r-------,values=1 : values=on [root@fedora ~]# amixer -Dhw:0 cget numid=13 numid=13,iface=CARD,name='Headset Mic Jack' ; type=BOOLEAN,access=r-------,values=1 : values=on The 'Headset Mic Jack' is updated with a delay which seems normal with additional calibration needed. Fixes: d92e279dee56 ('ASoC: Intel: sof_sdw: add quirk for HP Spectre x360 convertible') Signed-off-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20211027021824.24776-3-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 6d59462880fb..c5b1a1621fb5 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -188,7 +188,7 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { }, .driver_data = (void *)(SOF_SDW_TGL_HDMI | SOF_SDW_PCH_DMIC | - RT711_JD2), + RT711_JD1), }, { /* NUC15 'Bishop County' LAPBC510 and LAPBC710 skews */ -- cgit v1.2.3 From 1071f2415b6b4ee653869acabfb26de1a27da9cd Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 27 Oct 2021 10:18:17 +0800 Subject: ASoC: Intel: sof_sdw: add SKU for Dell Latitude 9520 Different SKUs seem to be used for the same design. BugLink: https://github.com/thesofproject/linux/issues/3206 Signed-off-by: Pierre-Louis Bossart Reviewed-by: Kai Vehmanen Reviewed-by: Ranjani Sridharan Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20211027021824.24776-4-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index c5b1a1621fb5..1c6c22cb7cab 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -129,6 +129,17 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { RT711_JD2 | SOF_RT715_DAI_ID_FIX), }, + { + /* another SKU of Dell Latitude 9520 */ + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A3F") + }, + .driver_data = (void *)(SOF_SDW_TGL_HDMI | + RT711_JD2 | + SOF_RT715_DAI_ID_FIX), + }, { /* Dell XPS 9710 */ .callback = sof_sdw_quirk_cb, -- cgit v1.2.3 From b63137cf5167b73d9d68a2334b835996bfc3b941 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 27 Oct 2021 10:18:18 +0800 Subject: ASoC: intel: sof_sdw: rename be_index/link_id to link_index The link_id variable in sof_card_dai_links_create() and be_index argument in create_sdw_dailink() is actually links' index. Rename them to link_index to be consistent. Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20211027021824.24776-5-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 48 +++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 1c6c22cb7cab..2492cd3556a8 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -899,7 +899,7 @@ static int get_slave_info(const struct snd_soc_acpi_link_adr *adr_link, } static int create_sdw_dailink(struct snd_soc_card *card, - struct device *dev, int *be_index, + struct device *dev, int *link_index, struct snd_soc_dai_link *dai_links, int sdw_be_num, int sdw_cpu_dai_num, struct snd_soc_dai_link_component *cpus, @@ -1002,8 +1002,12 @@ static int create_sdw_dailink(struct snd_soc_card *card, cpus[cpu_dai_index++].dai_name = cpu_name; } - if (*be_index >= sdw_be_num) { - dev_err(dev, " invalid be dai index %d", *be_index); + /* + * We create sdw dai links at first stage, so link index should + * not be larger than sdw_be_num + */ + if (*link_index >= sdw_be_num) { + dev_err(dev, "invalid dai link index %d", *link_index); return -EINVAL; } @@ -1014,18 +1018,19 @@ static int create_sdw_dailink(struct snd_soc_card *card, playback = (stream == SNDRV_PCM_STREAM_PLAYBACK); capture = (stream == SNDRV_PCM_STREAM_CAPTURE); - init_dai_link(dev, dai_links + *be_index, *be_index, name, + init_dai_link(dev, dai_links + *link_index, *link_index, name, playback, capture, cpus + *cpu_id, cpu_dai_num, codecs, codec_num, NULL, &sdw_ops); + /* * SoundWire DAILINKs use 'stream' functions and Bank Switch operations * based on wait_for_completion(), tag them as 'nonatomic'. */ - dai_links[*be_index].nonatomic = true; + dai_links[*link_index].nonatomic = true; - ret = set_codec_init_func(card, link, dai_links + (*be_index)++, + ret = set_codec_init_func(card, link, dai_links + (*link_index)++, playback, group_id); if (ret < 0) { dev_err(dev, "failed to init codec %d", codec_index); @@ -1106,7 +1111,7 @@ static int sof_card_dai_links_create(struct device *dev, bool group_generated[SDW_MAX_GROUPS]; int ssp_codec_index, ssp_mask; struct snd_soc_dai_link *links; - int num_links, link_id = 0; + int num_links, link_index = 0; char *name, *cpu_name; int total_cpu_dai_num; int sdw_cpu_dai_num; @@ -1206,23 +1211,20 @@ static int sof_card_dai_links_create(struct device *dev, group_generated[endpoint->group_id]) continue; - ret = create_sdw_dailink(card, dev, &be_id, links, sdw_be_num, + ret = create_sdw_dailink(card, dev, &link_index, links, sdw_be_num, sdw_cpu_dai_num, cpus, adr_link, &cpu_id, group_generated, codec_conf, codec_conf_count, &codec_conf_index, &ignore_pch_dmic); if (ret < 0) { - dev_err(dev, "failed to create dai link %d", be_id); + dev_err(dev, "failed to create dai link %d", link_index); return ret; } } - /* non-sdw DAI follows sdw DAI */ - link_id = be_id; - /* get BE ID for non-sdw DAI */ - be_id = get_next_be_id(links, be_id); + be_id = get_next_be_id(links, link_index); SSP: /* SSP */ @@ -1263,17 +1265,17 @@ SSP: playback = info->direction[SNDRV_PCM_STREAM_PLAYBACK]; capture = info->direction[SNDRV_PCM_STREAM_CAPTURE]; - init_dai_link(dev, links + link_id, be_id, name, + init_dai_link(dev, links + link_index, be_id, name, playback, capture, cpus + cpu_id, 1, ssp_components, 1, NULL, info->ops); - ret = info->init(card, NULL, links + link_id, info, 0); + ret = info->init(card, NULL, links + link_index, info, 0); if (ret < 0) return ret; - INC_ID(be_id, cpu_id, link_id); + INC_ID(be_id, cpu_id, link_index); } DMIC: @@ -1284,21 +1286,21 @@ DMIC: goto HDMI; } cpus[cpu_id].dai_name = "DMIC01 Pin"; - init_dai_link(dev, links + link_id, be_id, "dmic01", + init_dai_link(dev, links + link_index, be_id, "dmic01", 0, 1, // DMIC only supports capture cpus + cpu_id, 1, dmic_component, 1, sof_sdw_dmic_init, NULL); - INC_ID(be_id, cpu_id, link_id); + INC_ID(be_id, cpu_id, link_index); cpus[cpu_id].dai_name = "DMIC16k Pin"; - init_dai_link(dev, links + link_id, be_id, "dmic16k", + init_dai_link(dev, links + link_index, be_id, "dmic16k", 0, 1, // DMIC only supports capture cpus + cpu_id, 1, dmic_component, 1, /* don't call sof_sdw_dmic_init() twice */ NULL, NULL); - INC_ID(be_id, cpu_id, link_id); + INC_ID(be_id, cpu_id, link_index); } HDMI: @@ -1336,12 +1338,12 @@ HDMI: return -ENOMEM; cpus[cpu_id].dai_name = cpu_name; - init_dai_link(dev, links + link_id, be_id, name, + init_dai_link(dev, links + link_index, be_id, name, 1, 0, // HDMI only supports playback cpus + cpu_id, 1, idisp_components + i, 1, sof_sdw_hdmi_init, NULL); - INC_ID(be_id, cpu_id, link_id); + INC_ID(be_id, cpu_id, link_index); } if (sof_sdw_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) { @@ -1365,7 +1367,7 @@ HDMI: return -ENOMEM; cpus[cpu_id].dai_name = cpu_name; - init_dai_link(dev, links + link_id, be_id, name, 1, 1, + init_dai_link(dev, links + link_index, be_id, name, 1, 1, cpus + cpu_id, 1, ssp_components, 1, NULL, NULL); } -- cgit v1.2.3 From d471c034f83201f84330e9ed46ad5139d32e77ce Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 27 Oct 2021 10:18:19 +0800 Subject: ASoC: intel: sof_sdw: Use a fixed DAI link id for AMP Currently, we assign SoundWire DAI link id according to the order in the link address table, with the assumption that the headset codec is listed first, then amplifiers and last capture devices. If the headset codec is not present in a platform, the dai link for amplifiers will be shifted, which can be handled in two ways a) modify the topology to renumber the dailink changes b) keep the dailink numbers constant in topology but also avoid the variations in the machine driver. This patch adds support for option b), the dailink index for amplifiers and capture devices becomes fixed. Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20211027021824.24776-6-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 26 +++++++++++++++++++++++--- sound/soc/intel/boards/sof_sdw_common.h | 6 ++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 2492cd3556a8..1be5c4754337 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -462,6 +462,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .direction = {true, true}, .dai_name = "rt700-aif1", .init = sof_sdw_rt700_init, + .codec_type = SOF_SDW_CODEC_TYPE_JACK, }, { .part_id = 0x711, @@ -470,6 +471,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "rt711-sdca-aif1", .init = sof_sdw_rt711_sdca_init, .exit = sof_sdw_rt711_sdca_exit, + .codec_type = SOF_SDW_CODEC_TYPE_JACK, }, { .part_id = 0x711, @@ -478,6 +480,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "rt711-aif1", .init = sof_sdw_rt711_init, .exit = sof_sdw_rt711_exit, + .codec_type = SOF_SDW_CODEC_TYPE_JACK, }, { .part_id = 0x1308, @@ -486,12 +489,14 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "rt1308-aif", .ops = &sof_sdw_rt1308_i2s_ops, .init = sof_sdw_rt1308_init, + .codec_type = SOF_SDW_CODEC_TYPE_AMP, }, { .part_id = 0x1316, .direction = {true, true}, .dai_name = "rt1316-aif", .init = sof_sdw_rt1316_init, + .codec_type = SOF_SDW_CODEC_TYPE_AMP, }, { .part_id = 0x714, @@ -500,6 +505,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .ignore_pch_dmic = true, .dai_name = "rt715-aif2", .init = sof_sdw_rt715_sdca_init, + .codec_type = SOF_SDW_CODEC_TYPE_MIC, }, { .part_id = 0x715, @@ -508,6 +514,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .ignore_pch_dmic = true, .dai_name = "rt715-aif2", .init = sof_sdw_rt715_sdca_init, + .codec_type = SOF_SDW_CODEC_TYPE_MIC, }, { .part_id = 0x714, @@ -516,6 +523,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .ignore_pch_dmic = true, .dai_name = "rt715-aif2", .init = sof_sdw_rt715_init, + .codec_type = SOF_SDW_CODEC_TYPE_MIC, }, { .part_id = 0x715, @@ -524,6 +532,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .ignore_pch_dmic = true, .dai_name = "rt715-aif2", .init = sof_sdw_rt715_init, + .codec_type = SOF_SDW_CODEC_TYPE_MIC, }, { .part_id = 0x8373, @@ -531,12 +540,14 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "max98373-aif1", .init = sof_sdw_mx8373_init, .codec_card_late_probe = sof_sdw_mx8373_late_probe, + .codec_type = SOF_SDW_CODEC_TYPE_AMP, }, { .part_id = 0x5682, .direction = {true, true}, .dai_name = "rt5682-sdw", .init = sof_sdw_rt5682_init, + .codec_type = SOF_SDW_CODEC_TYPE_JACK, }, { .part_id = 0xaaaa, /* generic codec mockup */ @@ -544,6 +555,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .direction = {true, true}, .dai_name = "sdw-mockup-aif1", .init = NULL, + .codec_type = SOF_SDW_CODEC_TYPE_JACK, }, { .part_id = 0xaa55, /* headset codec mockup */ @@ -551,6 +563,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .direction = {true, true}, .dai_name = "sdw-mockup-aif1", .init = NULL, + .codec_type = SOF_SDW_CODEC_TYPE_JACK, }, { .part_id = 0x55aa, /* amplifier mockup */ @@ -558,6 +571,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .direction = {true, false}, .dai_name = "sdw-mockup-aif1", .init = NULL, + .codec_type = SOF_SDW_CODEC_TYPE_AMP, }, { .part_id = 0x5555, @@ -565,6 +579,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .direction = {false, true}, .dai_name = "sdw-mockup-aif1", .init = sof_sdw_mic_codec_mockup_init, + .codec_type = SOF_SDW_CODEC_TYPE_MIC, }, }; @@ -906,7 +921,7 @@ static int create_sdw_dailink(struct snd_soc_card *card, const struct snd_soc_acpi_link_adr *link, int *cpu_id, bool *group_generated, struct snd_soc_codec_conf *codec_conf, - int codec_count, + int codec_count, int *link_id, int *codec_conf_index, bool *ignore_pch_dmic) { @@ -964,6 +979,11 @@ static int create_sdw_dailink(struct snd_soc_card *card, if (codec_info_list[codec_index].ignore_pch_dmic) *ignore_pch_dmic = true; + /* Shift the first amplifier's *link_id to SDW_AMP_DAI_ID */ + if (codec_info_list[codec_index].codec_type == SOF_SDW_CODEC_TYPE_AMP && + *link_id < SDW_AMP_DAI_ID) + *link_id = SDW_AMP_DAI_ID; + cpu_dai_index = *cpu_id; for_each_pcm_streams(stream) { char *name, *cpu_name; @@ -1018,7 +1038,7 @@ static int create_sdw_dailink(struct snd_soc_card *card, playback = (stream == SNDRV_PCM_STREAM_PLAYBACK); capture = (stream == SNDRV_PCM_STREAM_CAPTURE); - init_dai_link(dev, dai_links + *link_index, *link_index, name, + init_dai_link(dev, dai_links + *link_index, (*link_id)++, name, playback, capture, cpus + *cpu_id, cpu_dai_num, codecs, codec_num, @@ -1215,7 +1235,7 @@ static int sof_card_dai_links_create(struct device *dev, sdw_cpu_dai_num, cpus, adr_link, &cpu_id, group_generated, codec_conf, codec_conf_count, - &codec_conf_index, + &be_id, &codec_conf_index, &ignore_pch_dmic); if (ret < 0) { dev_err(dev, "failed to create dai link %d", link_index); diff --git a/sound/soc/intel/boards/sof_sdw_common.h b/sound/soc/intel/boards/sof_sdw_common.h index b35f5a9b96f5..c6200aa14089 100644 --- a/sound/soc/intel/boards/sof_sdw_common.h +++ b/sound/soc/intel/boards/sof_sdw_common.h @@ -15,6 +15,7 @@ #define MAX_NO_PROPS 2 #define MAX_HDMI_NUM 4 +#define SDW_AMP_DAI_ID 2 #define SDW_DMIC_DAI_ID 4 #define SDW_MAX_CPU_DAIS 16 #define SDW_INTEL_BIDIR_PDI_BASE 2 @@ -52,9 +53,14 @@ enum { (((quirk) << SOF_BT_OFFLOAD_SSP_SHIFT) & SOF_BT_OFFLOAD_SSP_MASK) #define SOF_SSP_BT_OFFLOAD_PRESENT BIT(18) +#define SOF_SDW_CODEC_TYPE_JACK 0 +#define SOF_SDW_CODEC_TYPE_AMP 1 +#define SOF_SDW_CODEC_TYPE_MIC 2 + struct sof_sdw_codec_info { const int part_id; const int version_id; + const int codec_type; int amp_num; const u8 acpi_id[ACPI_ID_LEN]; const bool direction[2]; // playback & capture support -- cgit v1.2.3 From bf605cb042307d162fbcb094738bab5d767f1261 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 27 Oct 2021 10:18:20 +0800 Subject: ASoC: intel: sof_sdw: move DMIC link id overwrite to create_sdw_dailink We can apply the fixed dai link id for DMICs in create_sdw_dailink(). No need to set it in each DMIC's callback. The fixed dai link id is not only for rt715 and rt715-sdca, but for all DMICs, therefore we remove the SOF_RT715_DAI_ID_FIX check as well. Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20211027021824.24776-7-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 8 ++++++++ sound/soc/intel/boards/sof_sdw_rt715.c | 7 ------- sound/soc/intel/boards/sof_sdw_rt715_sdca.c | 7 ------- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 1be5c4754337..b00c1731c2c3 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -984,6 +984,14 @@ static int create_sdw_dailink(struct snd_soc_card *card, *link_id < SDW_AMP_DAI_ID) *link_id = SDW_AMP_DAI_ID; + /* + * DAI ID is fixed at SDW_DMIC_DAI_ID for MICs to + * keep sdw DMIC and HDMI setting static in UCM + */ + if (codec_info_list[codec_index].codec_type == SOF_SDW_CODEC_TYPE_MIC && + *link_id < SDW_DMIC_DAI_ID) + *link_id = SDW_DMIC_DAI_ID; + cpu_dai_index = *cpu_id; for_each_pcm_streams(stream) { char *name, *cpu_name; diff --git a/sound/soc/intel/boards/sof_sdw_rt715.c b/sound/soc/intel/boards/sof_sdw_rt715.c index c8af3780cbc3..7c068dc6b9cf 100644 --- a/sound/soc/intel/boards/sof_sdw_rt715.c +++ b/sound/soc/intel/boards/sof_sdw_rt715.c @@ -30,13 +30,6 @@ int sof_sdw_rt715_init(struct snd_soc_card *card, struct sof_sdw_codec_info *info, bool playback) { - /* - * DAI ID is fixed at SDW_DMIC_DAI_ID for 715 to - * keep sdw DMIC and HDMI setting static in UCM - */ - if (sof_sdw_quirk & SOF_RT715_DAI_ID_FIX) - dai_links->id = SDW_DMIC_DAI_ID; - dai_links->init = rt715_rtd_init; return 0; diff --git a/sound/soc/intel/boards/sof_sdw_rt715_sdca.c b/sound/soc/intel/boards/sof_sdw_rt715_sdca.c index 85d3d8c355cc..ca0cf3db2e4d 100644 --- a/sound/soc/intel/boards/sof_sdw_rt715_sdca.c +++ b/sound/soc/intel/boards/sof_sdw_rt715_sdca.c @@ -30,13 +30,6 @@ int sof_sdw_rt715_sdca_init(struct snd_soc_card *card, struct sof_sdw_codec_info *info, bool playback) { - /* - * DAI ID is fixed at SDW_DMIC_DAI_ID for 715-SDCA to - * keep sdw DMIC and HDMI setting static in UCM - */ - if (sof_sdw_quirk & SOF_RT715_DAI_ID_FIX) - dai_links->id = SDW_DMIC_DAI_ID; - dai_links->init = rt715_sdca_rtd_init; return 0; -- cgit v1.2.3 From f8f8312263e2d0c38dd0330a4503dcdcc94d0cd5 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 27 Oct 2021 10:18:21 +0800 Subject: ASoC: intel: sof_sdw: remove SOF_RT715_DAI_ID_FIX quirk SOF_RT715_DAI_ID_FIX is not used anywhere. Remove it. Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20211027021824.24776-8-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 23 +++++------------------ sound/soc/intel/boards/sof_sdw_common.h | 1 - 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index b00c1731c2c3..72bc7da2d21e 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -36,8 +36,6 @@ static void log_quirks(struct device *dev) if (SOF_SSP_GET_PORT(sof_sdw_quirk)) dev_dbg(dev, "SSP port %ld\n", SOF_SSP_GET_PORT(sof_sdw_quirk)); - if (sof_sdw_quirk & SOF_RT715_DAI_ID_FIX) - dev_dbg(dev, "quirk SOF_RT715_DAI_ID_FIX enabled\n"); if (sof_sdw_quirk & SOF_SDW_NO_AGGREGATION) dev_dbg(dev, "quirk SOF_SDW_NO_AGGREGATION enabled\n"); } @@ -64,8 +62,7 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "09C6") }, - .driver_data = (void *)(RT711_JD2 | - SOF_RT715_DAI_ID_FIX), + .driver_data = (void *)RT711_JD2, }, { /* early version of SKU 09C6 */ @@ -74,8 +71,7 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0983") }, - .driver_data = (void *)(RT711_JD2 | - SOF_RT715_DAI_ID_FIX), + .driver_data = (void *)RT711_JD2, }, { .callback = sof_sdw_quirk_cb, @@ -84,7 +80,6 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F"), }, .driver_data = (void *)(RT711_JD2 | - SOF_RT715_DAI_ID_FIX | SOF_SDW_FOUR_SPK), }, { @@ -94,7 +89,6 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990"), }, .driver_data = (void *)(RT711_JD2 | - SOF_RT715_DAI_ID_FIX | SOF_SDW_FOUR_SPK), }, /* IceLake devices */ @@ -126,8 +120,7 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A3E") }, .driver_data = (void *)(SOF_SDW_TGL_HDMI | - RT711_JD2 | - SOF_RT715_DAI_ID_FIX), + RT711_JD2), }, { /* another SKU of Dell Latitude 9520 */ @@ -137,8 +130,7 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A3F") }, .driver_data = (void *)(SOF_SDW_TGL_HDMI | - RT711_JD2 | - SOF_RT715_DAI_ID_FIX), + RT711_JD2), }, { /* Dell XPS 9710 */ @@ -149,7 +141,6 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { }, .driver_data = (void *)(SOF_SDW_TGL_HDMI | RT711_JD2 | - SOF_RT715_DAI_ID_FIX | SOF_SDW_FOUR_SPK), }, { @@ -160,7 +151,6 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { }, .driver_data = (void *)(SOF_SDW_TGL_HDMI | RT711_JD2 | - SOF_RT715_DAI_ID_FIX | SOF_SDW_FOUR_SPK), }, { @@ -221,7 +211,6 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { }, .driver_data = (void *)(SOF_SDW_TGL_HDMI | RT711_JD2 | - SOF_RT715_DAI_ID_FIX | SOF_SDW_FOUR_SPK), }, { @@ -231,8 +220,7 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A45") }, .driver_data = (void *)(SOF_SDW_TGL_HDMI | - RT711_JD2 | - SOF_RT715_DAI_ID_FIX), + RT711_JD2), }, /* AlderLake devices */ { @@ -243,7 +231,6 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { }, .driver_data = (void *)(RT711_JD2_100K | SOF_SDW_TGL_HDMI | - SOF_RT715_DAI_ID_FIX | SOF_BT_OFFLOAD_SSP(2) | SOF_SSP_BT_OFFLOAD_PRESENT), }, diff --git a/sound/soc/intel/boards/sof_sdw_common.h b/sound/soc/intel/boards/sof_sdw_common.h index c6200aa14089..e2457738a332 100644 --- a/sound/soc/intel/boards/sof_sdw_common.h +++ b/sound/soc/intel/boards/sof_sdw_common.h @@ -43,7 +43,6 @@ enum { #define SOF_SDW_PCH_DMIC BIT(6) #define SOF_SSP_PORT(x) (((x) & GENMASK(5, 0)) << 7) #define SOF_SSP_GET_PORT(quirk) (((quirk) >> 7) & GENMASK(5, 0)) -#define SOF_RT715_DAI_ID_FIX BIT(13) #define SOF_SDW_NO_AGGREGATION BIT(14) /* BT audio offload: reserve 3 bits for future */ -- cgit v1.2.3 From bd98394a811c7dc39bcd189cf5f33925f0c30ae2 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 27 Oct 2021 10:18:22 +0800 Subject: ASoC: intel: sof_sdw: remove sof_sdw_mic_codec_mockup_init Now, we set DAI link id as SDW_DMIC_DAI_ID for all DMICs. No need to set it in sof_sdw_mic_codec_mockup_init. Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20211027021824.24776-9-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 72bc7da2d21e..aac493aea002 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -429,20 +429,6 @@ static const struct snd_soc_ops sdw_ops = { .shutdown = sdw_shutdown, }; -static int sof_sdw_mic_codec_mockup_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback) -{ - /* - * force DAI link to use same ID as RT715 and DMIC - * to reuse topologies - */ - dai_links->id = SDW_DMIC_DAI_ID; - return 0; -} - static struct sof_sdw_codec_info codec_info_list[] = { { .part_id = 0x700, @@ -565,7 +551,6 @@ static struct sof_sdw_codec_info codec_info_list[] = { .version_id = 0, .direction = {false, true}, .dai_name = "sdw-mockup-aif1", - .init = sof_sdw_mic_codec_mockup_init, .codec_type = SOF_SDW_CODEC_TYPE_MIC, }, }; -- cgit v1.2.3 From 4ed65d6ead29a992c4803e1138a6042caa6ec2a3 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 27 Oct 2021 10:18:23 +0800 Subject: ASoC: intel: sof_sdw: remove get_next_be_id DAI link id will not be set from sdw codec init feedback function, and be_id is changed by create_sdw_dailink() now. So we don't need get_next_be_id() anymore. Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20211027021824.24776-10-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index aac493aea002..df29f7b478a4 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -1044,17 +1044,6 @@ static int create_sdw_dailink(struct snd_soc_card *card, return 0; } -/* - * DAI link ID of SSP & DMIC & HDMI are based on last - * link ID used by sdw link. Since be_id may be changed - * in init func of sdw codec, it is not equal to be_id - */ -static inline int get_next_be_id(struct snd_soc_dai_link *links, - int be_id) -{ - return links[be_id - 1].id + 1; -} - #define IDISP_CODEC_MASK 0x4 static int sof_card_codec_conf_alloc(struct device *dev, @@ -1223,9 +1212,6 @@ static int sof_card_dai_links_create(struct device *dev, } } - /* get BE ID for non-sdw DAI */ - be_id = get_next_be_id(links, link_index); - SSP: /* SSP */ if (!ssp_num) -- cgit v1.2.3 From 296c789ce1e501be8b46ebb24da4ba53063cc10a Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 27 Oct 2021 10:18:24 +0800 Subject: ASoC: intel: sof_sdw: add link adr order check We assume the adr order described in a snd_soc_acpi_link_adr array is jack -> amp -> mic. We follow the same order to implement the topology. We will need a special topology if we configure a snd_soc_acpi_link_adr array with different order. Adding a check and a warning message can remind people to keep the order when adding a new snd_soc_acpi_link_adr array. Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20211027021824.24776-11-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index df29f7b478a4..de303a980879 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -599,10 +599,11 @@ static inline int find_codec_info_acpi(const u8 *acpi_id) * Since some sdw slaves may be aggregated, the CPU DAI number * may be larger than the number of BE dailinks. */ -static int get_sdw_dailink_info(const struct snd_soc_acpi_link_adr *links, +static int get_sdw_dailink_info(struct device *dev, const struct snd_soc_acpi_link_adr *links, int *sdw_be_num, int *sdw_cpu_dai_num) { const struct snd_soc_acpi_link_adr *link; + int _codec_type = SOF_SDW_CODEC_TYPE_JACK; bool group_visited[SDW_MAX_GROUPS]; bool no_aggregation; int i; @@ -628,6 +629,12 @@ static int get_sdw_dailink_info(const struct snd_soc_acpi_link_adr *links, if (codec_index < 0) return codec_index; + if (codec_info_list[codec_index].codec_type < _codec_type) + dev_warn(dev, + "Unexpected address table ordering. Expected order: jack -> amp -> mic\n"); + + _codec_type = codec_info_list[codec_index].codec_type; + endpoint = link->adr_d->endpoints; /* count DAI number for playback and capture */ @@ -1136,7 +1143,7 @@ static int sof_card_dai_links_create(struct device *dev, ssp_num = ssp_codec_index >= 0 ? hweight_long(ssp_mask) : 0; comp_num = hdmi_num + ssp_num; - ret = get_sdw_dailink_info(mach_params->links, + ret = get_sdw_dailink_info(dev, mach_params->links, &sdw_be_num, &sdw_cpu_dai_num); if (ret < 0) { dev_err(dev, "failed to get sdw link info %d", ret); -- cgit v1.2.3 From 0f9710603e803ae9b64ed3b54019170b323968d7 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Thu, 28 Oct 2021 15:09:02 +0100 Subject: ASoC: dt-bindings: cs42l42: Convert binding to yaml Replace the old .txt binding with a new schema binding. At the same time, some of the descriptions are updated to make them clearer, fix errors, or just make them fit better into the style of schema binding. The cirrus,hs-bias-ramp-rate property was missing from the old .txt binding and has been added to the yaml. Signed-off-by: Richard Fitzgerald Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20211028140902.11786-4-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/cirrus,cs42l42.yaml | 225 +++++++++++++++++++++ .../devicetree/bindings/sound/cs42l42.txt | 115 ----------- MAINTAINERS | 1 + 3 files changed, 226 insertions(+), 115 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml delete mode 100644 Documentation/devicetree/bindings/sound/cs42l42.txt diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml new file mode 100644 index 000000000000..31800f70e9d9 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml @@ -0,0 +1,225 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/cirrus,cs42l42.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cirrus Logic CS42L42 audio CODEC + +maintainers: + - patches@opensource.cirrus.com + +description: + The CS42L42 is a low-power audio codec designed for portable applications. + It provides a high-dynamic range, stereo DAC for audio playback and a mono + high-dynamic-range ADC for audio capture. There is an integrated headset + detection block. + +properties: + compatible: + enum: + - cirrus,cs42l42 + + reg: + description: + The I2C address of the CS42L42. + maxItems: 1 + + VP-supply: + description: + VP power supply. + + VCP-supply: + description: + Charge pump power supply. + + VD_FILT-supply: + description: + FILT+ power supply. + + VL-supply: + description: + Logic power supply. + + VA-supply: + description: + Analog power supply. + + reset-gpios: + description: + This pin will be asserted and then deasserted to reset the + CS42L42 before communication starts. + maxItems: 1 + + interrupts: + description: + Interrupt for CS42L42 IRQ line. + maxItems: 1 + + cirrus,ts-inv: + description: | + Sets the behaviour of the jack plug detect switch. + + 0 - (Default) Shorted to tip when unplugged, open when plugged. + This is "inverted tip sense (ITS)" in the datasheet. + + 1 - Open when unplugged, shorted to tip when plugged. + This is "normal tip sense (TS)" in the datasheet. + + The CS42L42_TS_INV_* defines are available for this. + $ref: "/schemas/types.yaml#/definitions/uint32" + minimum: 0 + maximum: 1 + + cirrus,ts-dbnc-rise: + description: | + Debounce the rising edge of TIP_SENSE_PLUG. With no + debounce, the tip sense pin might be noisy on a plug event. + + 0 - 0ms + 1 - 125ms + 2 - 250ms + 3 - 500ms + 4 - 750ms + 5 - 1s (Default) + 6 - 1.25s + 7 - 1.5s + + The CS42L42_TS_DBNCE_* defines are available for this. + $ref: "/schemas/types.yaml#/definitions/uint32" + minimum: 0 + maximum: 7 + + cirrus,ts-dbnc-fall: + description: | + Debounce the falling edge of TIP_SENSE_UNPLUG. With no + debounce, the tip sense pin might be noisy on an unplug event. + + 0 - 0ms + 1 - 125ms + 2 - 250ms + 3 - 500ms + 4 - 750ms + 5 - 1s (Default) + 6 - 1.25s + 7 - 1.5s + + The CS42L42_TS_DBNCE_* defines are available for this. + $ref: "/schemas/types.yaml#/definitions/uint32" + minimum: 0 + maximum: 7 + + cirrus,btn-det-init-dbnce: + description: | + This sets how long to wait after enabling button detection + interrupts before servicing button interrupts, to allow the + HS bias time to settle. Value is in milliseconds. + There may be erroneous button interrupts if this debounce time + is too short. + + 0ms - 200ms, + Default = 100ms + $ref: "/schemas/types.yaml#/definitions/uint32" + minimum: 0 + maximum: 200 + + cirrus,btn-det-event-dbnce: + description: | + This sets how long to wait after receiving a button press + interrupt before processing it. Allows time for the button + press to make a clean connection with the bias resistors. + Value is in milliseconds. + + 0ms - 20ms, + Default = 10ms + $ref: "/schemas/types.yaml#/definitions/uint32" + minimum: 0 + maximum: 20 + + cirrus,bias-lvls: + description: | + For a level-detect headset button scheme, each button will bias + the mic pin to a certain voltage. To determine which button was + pressed, the voltage is compared to sequential, decreasing + voltages, until the compared voltage < bias voltage. + For different hardware setups, a designer might want to tweak this. + This is an array of descending values for the comparator voltage, + given as percent of the HSBIAS voltage. + + Array of 4 values, each 0-63 + < x1 x2 x3 x4 > + Default = < 15 8 4 1 > + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 4 + maxItems: 4 + items: + minimum: 0 + maximum: 63 + + cirrus,hs-bias-ramp-rate: + description: | + If present this sets the rate that the HS bias should rise and fall. + The actual rise and fall times depend on external hardware (the + datasheet gives several rise and fall time examples). + + 0 - Fast rise time; slow, load-dependent fall time + 1 - Fast + 2 - Slow (default) + 3 - Slowest + + The CS42L42_HSBIAS_RAMP_* defines are available for this. + $ref: "/schemas/types.yaml#/definitions/uint32" + minimum: 0 + maximum: 3 + + cirrus,hs-bias-sense-disable: + description: | + If present the HSBIAS sense is disabled. Configures HSBIAS output + current sense through the external 2.21-k resistor. HSBIAS_SENSE + is a hardware feature to reduce the potential pop noise when the + headset plug is removed slowly. But on some platforms ESD voltage + will affect it causing plug detection to fail, especially with CTIA + headset type. For different hardware setups, a designer might want + to tweak default behavior. + type: boolean + +required: + - compatible + - reg + - VP-supply + - VCP-supply + - VD_FILT-supply + - VL-supply + - VA-supply + +additionalProperties: false + +examples: + - | + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + + cs42l42: cs42l42@48 { + compatible = "cirrus,cs42l42"; + reg = <0x48>; + VA-supply = <&dummy_vreg>; + VP-supply = <&dummy_vreg>; + VCP-supply = <&dummy_vreg>; + VD_FILT-supply = <&dummy_vreg>; + VL-supply = <&dummy_vreg>; + + reset-gpios = <&axi_gpio_0 1 0>; + interrupt-parent = <&gpio0>; + interrupts = <55 8>; + + cirrus,ts-inv = ; + cirrus,ts-dbnc-rise = ; + cirrus,ts-dbnc-fall = ; + cirrus,btn-det-init-dbnce = <100>; + cirrus,btn-det-event-dbnce = <10>; + cirrus,bias-lvls = <0x0F 0x08 0x04 0x01>; + cirrus,hs-bias-ramp-rate = ; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/cs42l42.txt b/Documentation/devicetree/bindings/sound/cs42l42.txt deleted file mode 100644 index 3b7705623980..000000000000 --- a/Documentation/devicetree/bindings/sound/cs42l42.txt +++ /dev/null @@ -1,115 +0,0 @@ -CS42L42 audio CODEC - -Required properties: - - - compatible : "cirrus,cs42l42" - - - reg : the I2C address of the device for I2C. - - - VP-supply, VCP-supply, VD_FILT-supply, VL-supply, VA-supply : - power supplies for the device, as covered in - Documentation/devicetree/bindings/regulator/regulator.txt. - -Optional properties: - - - reset-gpios : a GPIO spec for the reset pin. If specified, it will be - deasserted before communication to the codec starts. - - - interrupts : IRQ line info CS42L42. - (See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt - for further information relating to interrupt properties) - - - cirrus,ts-inv : Boolean property. Sets the behaviour of the jack plug - detect switch. - - 0 = (Default) Shorted to tip when unplugged, open when plugged. - This is "inverted tip sense (ITS)" in the datasheet. - - 1 = Open when unplugged, shorted to tip when plugged. - This is "normal tip sense (TS)" in the datasheet. - - - cirrus,ts-dbnc-rise : Debounce the rising edge of TIP_SENSE_PLUG. With no - debounce, the tip sense pin might be noisy on a plug event. - - 0 - 0ms, - 1 - 125ms, - 2 - 250ms, - 3 - 500ms, - 4 - 750ms, - 5 - (Default) 1s, - 6 - 1.25s, - 7 - 1.5s, - - - cirrus,ts-dbnc-fall : Debounce the falling edge of TIP_SENSE_UNPLUG. - With no debounce, the tip sense pin might be noisy on an unplug event. - - 0 - 0ms, - 1 - 125ms, - 2 - 250ms, - 3 - 500ms, - 4 - 750ms, - 5 - (Default) 1s, - 6 - 1.25s, - 7 - 1.5s, - - - cirrus,btn-det-init-dbnce : This sets how long the driver sleeps after - enabling button detection interrupts. After auto-detection and before - servicing button interrupts, the HS bias needs time to settle. If you - don't wait, there is possibility for erroneous button interrupt. - - 0ms - 200ms, - Default = 100ms - - - cirrus,btn-det-event-dbnce : This sets how long the driver delays after - receiving a button press interrupt. With level detect interrupts, you want - to wait a small amount of time to make sure the button press is making a - clean connection with the bias resistors. - - 0ms - 20ms, - Default = 10ms - - - cirrus,bias-lvls : For a level-detect headset button scheme, each button - will bias the mic pin to a certain voltage. To determine which button was - pressed, the driver will compare this biased voltage to sequential, - decreasing voltages and will stop when a comparator is tripped, - indicating a comparator voltage < bias voltage. This value represents a - percentage of the internally generated HS bias voltage. For different - hardware setups, a designer might want to tweak this. This is an array of - descending values for the comparator voltage. - - Array of 4 values - Each 0-63 - < x1 x2 x3 x4 > - Default = < 15 8 4 1> - - - cirrus,hs-bias-sense-disable: This is boolean property. If present the - HSBIAS sense is disabled. Configures HSBIAS output current sense through - the external 2.21-k resistor. HSBIAS_SENSE is hardware feature to reduce - the potential pop noise during the headset plug out slowly. But on some - platforms ESD voltage will affect it causing test to fail, especially - with CTIA headset type. For different hardware setups, a designer might - want to tweak default behavior. - -Example: - -cs42l42: cs42l42@48 { - compatible = "cirrus,cs42l42"; - reg = <0x48>; - VA-supply = <&dummy_vreg>; - VP-supply = <&dummy_vreg>; - VCP-supply = <&dummy_vreg>; - VD_FILT-supply = <&dummy_vreg>; - VL-supply = <&dummy_vreg>; - - reset-gpios = <&axi_gpio_0 1 0>; - interrupt-parent = <&gpio0>; - interrupts = <55 8> - - cirrus,ts-inv = <0x00>; - cirrus,ts-dbnc-rise = <0x05>; - cirrus,ts-dbnc-fall = <0x00>; - cirrus,btn-det-init-dbnce = <100>; - cirrus,btn-det-event-dbnce = <10>; - cirrus,bias-lvls = <0x0F 0x08 0x04 0x01>; - cirrus,hs-bias-ramp-rate = <0x02>; -}; diff --git a/MAINTAINERS b/MAINTAINERS index 7a2345ce8521..09734251e1de 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4514,6 +4514,7 @@ M: David Rhodes L: alsa-devel@alsa-project.org (moderated for non-subscribers) L: patches@opensource.cirrus.com S: Maintained +F: Documentation/devicetree/bindings/sound/cirrus,cs* F: sound/soc/codecs/cs* CIRRUS LOGIC DSP FIRMWARE DRIVER -- cgit v1.2.3 From bae9e13fc55cbc5ae25409385b2f1ba9187082d0 Mon Sep 17 00:00:00 2001 From: David Rhodes Date: Fri, 29 Oct 2021 16:40:28 -0500 Subject: ASoC: cs35l41: DSP Support Support for HALO DSP and firmware Signed-off-by: David Rhodes Reviewed-by: Charles Keepax Link: https://lore.kernel.org/r/20211029214028.401284-2-drhodes@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/cs35l41-tables.c | 96 ++++++++++++ sound/soc/codecs/cs35l41.c | 312 +++++++++++++++++++++++++++++++++++++- sound/soc/codecs/cs35l41.h | 20 +++ 4 files changed, 426 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 326f2d611ad4..3fe62df32238 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -343,11 +343,15 @@ config SND_SOC_WM_ADSP default y if SND_SOC_WM5102=y default y if SND_SOC_WM5110=y default y if SND_SOC_WM2200=y + default y if SND_SOC_CS35L41_SPI=y + default y if SND_SOC_CS35L41_I2C=y default m if SND_SOC_MADERA=m default m if SND_SOC_CS47L24=m default m if SND_SOC_WM5102=m default m if SND_SOC_WM5110=m default m if SND_SOC_WM2200=m + default m if SND_SOC_CS35L41_SPI=m + default m if SND_SOC_CS35L41_I2C=m config SND_SOC_AB8500_CODEC tristate diff --git a/sound/soc/codecs/cs35l41-tables.c b/sound/soc/codecs/cs35l41-tables.c index 964e530afa27..9d1a7d7dd24d 100644 --- a/sound/soc/codecs/cs35l41-tables.c +++ b/sound/soc/codecs/cs35l41-tables.c @@ -200,6 +200,83 @@ bool cs35l41_readable_reg(struct device *dev, unsigned int reg) case CS35L41_DIE_STS2: case CS35L41_TEMP_CAL1: case CS35L41_TEMP_CAL2: + case CS35L41_DSP1_TIMESTAMP_COUNT: + case CS35L41_DSP1_SYS_ID: + case CS35L41_DSP1_SYS_VERSION: + case CS35L41_DSP1_SYS_CORE_ID: + case CS35L41_DSP1_SYS_AHB_ADDR: + case CS35L41_DSP1_SYS_XSRAM_SIZE: + case CS35L41_DSP1_SYS_YSRAM_SIZE: + case CS35L41_DSP1_SYS_PSRAM_SIZE: + case CS35L41_DSP1_SYS_PM_BOOT_SIZE: + case CS35L41_DSP1_SYS_FEATURES: + case CS35L41_DSP1_SYS_FIR_FILTERS: + case CS35L41_DSP1_SYS_LMS_FILTERS: + case CS35L41_DSP1_SYS_XM_BANK_SIZE: + case CS35L41_DSP1_SYS_YM_BANK_SIZE: + case CS35L41_DSP1_SYS_PM_BANK_SIZE: + case CS35L41_DSP1_RX1_RATE: + case CS35L41_DSP1_RX2_RATE: + case CS35L41_DSP1_RX3_RATE: + case CS35L41_DSP1_RX4_RATE: + case CS35L41_DSP1_RX5_RATE: + case CS35L41_DSP1_RX6_RATE: + case CS35L41_DSP1_RX7_RATE: + case CS35L41_DSP1_RX8_RATE: + case CS35L41_DSP1_TX1_RATE: + case CS35L41_DSP1_TX2_RATE: + case CS35L41_DSP1_TX3_RATE: + case CS35L41_DSP1_TX4_RATE: + case CS35L41_DSP1_TX5_RATE: + case CS35L41_DSP1_TX6_RATE: + case CS35L41_DSP1_TX7_RATE: + case CS35L41_DSP1_TX8_RATE: + case CS35L41_DSP1_SCRATCH1: + case CS35L41_DSP1_SCRATCH2: + case CS35L41_DSP1_SCRATCH3: + case CS35L41_DSP1_SCRATCH4: + case CS35L41_DSP1_CCM_CORE_CTRL: + case CS35L41_DSP1_CCM_CLK_OVERRIDE: + case CS35L41_DSP1_XM_MSTR_EN: + case CS35L41_DSP1_XM_CORE_PRI: + case CS35L41_DSP1_XM_AHB_PACK_PL_PRI: + case CS35L41_DSP1_XM_AHB_UP_PL_PRI: + case CS35L41_DSP1_XM_ACCEL_PL0_PRI: + case CS35L41_DSP1_XM_NPL0_PRI: + case CS35L41_DSP1_YM_MSTR_EN: + case CS35L41_DSP1_YM_CORE_PRI: + case CS35L41_DSP1_YM_AHB_PACK_PL_PRI: + case CS35L41_DSP1_YM_AHB_UP_PL_PRI: + case CS35L41_DSP1_YM_ACCEL_PL0_PRI: + case CS35L41_DSP1_YM_NPL0_PRI: + case CS35L41_DSP1_MPU_XM_ACCESS0: + case CS35L41_DSP1_MPU_YM_ACCESS0: + case CS35L41_DSP1_MPU_WNDW_ACCESS0: + case CS35L41_DSP1_MPU_XREG_ACCESS0: + case CS35L41_DSP1_MPU_YREG_ACCESS0: + case CS35L41_DSP1_MPU_XM_ACCESS1: + case CS35L41_DSP1_MPU_YM_ACCESS1: + case CS35L41_DSP1_MPU_WNDW_ACCESS1: + case CS35L41_DSP1_MPU_XREG_ACCESS1: + case CS35L41_DSP1_MPU_YREG_ACCESS1: + case CS35L41_DSP1_MPU_XM_ACCESS2: + case CS35L41_DSP1_MPU_YM_ACCESS2: + case CS35L41_DSP1_MPU_WNDW_ACCESS2: + case CS35L41_DSP1_MPU_XREG_ACCESS2: + case CS35L41_DSP1_MPU_YREG_ACCESS2: + case CS35L41_DSP1_MPU_XM_ACCESS3: + case CS35L41_DSP1_MPU_YM_ACCESS3: + case CS35L41_DSP1_MPU_WNDW_ACCESS3: + case CS35L41_DSP1_MPU_XREG_ACCESS3: + case CS35L41_DSP1_MPU_YREG_ACCESS3: + case CS35L41_DSP1_MPU_XM_VIO_ADDR: + case CS35L41_DSP1_MPU_XM_VIO_STATUS: + case CS35L41_DSP1_MPU_YM_VIO_ADDR: + case CS35L41_DSP1_MPU_YM_VIO_STATUS: + case CS35L41_DSP1_MPU_PM_VIO_ADDR: + case CS35L41_DSP1_MPU_PM_VIO_STATUS: + case CS35L41_DSP1_MPU_LOCK_CONFIG: + case CS35L41_DSP1_MPU_WDT_RST_CTRL: case CS35L41_OTP_TRIM_1: case CS35L41_OTP_TRIM_2: case CS35L41_OTP_TRIM_3: @@ -237,6 +314,13 @@ bool cs35l41_readable_reg(struct device *dev, unsigned int reg) case CS35L41_OTP_TRIM_35: case CS35L41_OTP_TRIM_36: case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31: + case CS35L41_DSP1_XMEM_PACK_0 ... CS35L41_DSP1_XMEM_PACK_3068: + case CS35L41_DSP1_XMEM_UNPACK32_0 ... CS35L41_DSP1_XMEM_UNPACK32_2046: + case CS35L41_DSP1_XMEM_UNPACK24_0 ... CS35L41_DSP1_XMEM_UNPACK24_4093: + case CS35L41_DSP1_YMEM_PACK_0 ... CS35L41_DSP1_YMEM_PACK_1532: + case CS35L41_DSP1_YMEM_UNPACK32_0 ... CS35L41_DSP1_YMEM_UNPACK32_1022: + case CS35L41_DSP1_YMEM_UNPACK24_0 ... CS35L41_DSP1_YMEM_UNPACK24_2045: + case CS35L41_DSP1_PMEM_0 ... CS35L41_DSP1_PMEM_5114: /*test regs*/ case CS35L41_PLL_OVR: case CS35L41_BST_TEST_DUTY: @@ -251,6 +335,9 @@ bool cs35l41_precious_reg(struct device *dev, unsigned int reg) { switch (reg) { case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31: + case CS35L41_DSP1_XMEM_PACK_0 ... CS35L41_DSP1_XMEM_PACK_3068: + case CS35L41_DSP1_YMEM_PACK_0 ... CS35L41_DSP1_YMEM_PACK_1532: + case CS35L41_DSP1_PMEM_0 ... CS35L41_DSP1_PMEM_5114: return true; default: return false; @@ -342,6 +429,15 @@ bool cs35l41_volatile_reg(struct device *dev, unsigned int reg) case CS35L41_OTP_TRIM_34: case CS35L41_OTP_TRIM_35: case CS35L41_OTP_TRIM_36: + case CS35L41_DSP_MBOX_1 ... CS35L41_DSP_VIRT2_MBOX_8: + case CS35L41_DSP1_XMEM_PACK_0 ... CS35L41_DSP1_XMEM_PACK_3068: + case CS35L41_DSP1_XMEM_UNPACK32_0 ... CS35L41_DSP1_XMEM_UNPACK32_2046: + case CS35L41_DSP1_XMEM_UNPACK24_0 ... CS35L41_DSP1_XMEM_UNPACK24_4093: + case CS35L41_DSP1_YMEM_PACK_0 ... CS35L41_DSP1_YMEM_PACK_1532: + case CS35L41_DSP1_YMEM_UNPACK32_0 ... CS35L41_DSP1_YMEM_UNPACK32_1022: + case CS35L41_DSP1_YMEM_UNPACK24_0 ... CS35L41_DSP1_YMEM_UNPACK24_2045: + case CS35L41_DSP1_PMEM_0 ... CS35L41_DSP1_PMEM_5114: + case CS35L41_DSP1_CCM_CORE_CTRL ... CS35L41_DSP1_WDT_STATUS: case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31: return true; default: diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index 9d0530dde996..afb07d2991ba 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -197,6 +197,122 @@ static SOC_ENUM_SINGLE_DECL(pcm_sft_ramp, CS35L41_AMP_DIG_VOL_CTRL, 0, cs35l41_pcm_sftramp_text); +static int cs35l41_dsp_preload_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int ret; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return wm_adsp_early_event(w, kcontrol, event); + case SND_SOC_DAPM_PRE_PMD: + ret = wm_adsp_early_event(w, kcontrol, event); + if (ret) + return ret; + + return wm_adsp_event(w, kcontrol, event); + default: + return 0; + } +} + +static bool cs35l41_check_cspl_mbox_sts(enum cs35l41_cspl_mbox_cmd cmd, + enum cs35l41_cspl_mbox_status sts) +{ + switch (cmd) { + case CSPL_MBOX_CMD_NONE: + case CSPL_MBOX_CMD_UNKNOWN_CMD: + return true; + case CSPL_MBOX_CMD_PAUSE: + return (sts == CSPL_MBOX_STS_PAUSED); + case CSPL_MBOX_CMD_RESUME: + return (sts == CSPL_MBOX_STS_RUNNING); + case CSPL_MBOX_CMD_REINIT: + return (sts == CSPL_MBOX_STS_RUNNING); + case CSPL_MBOX_CMD_STOP_PRE_REINIT: + return (sts == CSPL_MBOX_STS_RDY_FOR_REINIT); + default: + return false; + } +} + +static int cs35l41_set_cspl_mbox_cmd(struct cs35l41_private *cs35l41, + enum cs35l41_cspl_mbox_cmd cmd) +{ + unsigned int sts = 0, i; + int ret; + + // Set mailbox cmd + ret = regmap_write(cs35l41->regmap, CS35L41_DSP_VIRT1_MBOX_1, cmd); + if (ret < 0) { + dev_err(cs35l41->dev, "Failed to write MBOX: %d\n", ret); + return ret; + } + + // Read mailbox status and verify it is appropriate for the given cmd + for (i = 0; i < 5; i++) { + usleep_range(1000, 1100); + + ret = regmap_read(cs35l41->regmap, CS35L41_DSP_MBOX_2, &sts); + if (ret < 0) { + dev_err(cs35l41->dev, "Failed to read MBOX STS: %d\n", ret); + continue; + } + + if (!cs35l41_check_cspl_mbox_sts(cmd, sts)) { + dev_dbg(cs35l41->dev, + "[%u] cmd %u returned invalid sts %u", + i, cmd, sts); + } else { + return 0; + } + } + + dev_err(cs35l41->dev, + "Failed to set mailbox cmd %u (status %u)\n", + cmd, sts); + + return -ENOMSG; +} + +static int cs35l41_dsp_audio_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component); + unsigned int fw_status; + int ret; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (!cs35l41->dsp.cs_dsp.running) + return wm_adsp_event(w, kcontrol, event); + + ret = regmap_read(cs35l41->regmap, CS35L41_DSP_MBOX_2, &fw_status); + if (ret < 0) { + dev_err(cs35l41->dev, + "Failed to read firmware status: %d\n", ret); + return ret; + } + + switch (fw_status) { + case CSPL_MBOX_STS_RUNNING: + case CSPL_MBOX_STS_PAUSED: + break; + default: + dev_err(cs35l41->dev, "Firmware status is invalid: %u\n", + fw_status); + return -EINVAL; + } + + return cs35l41_set_cspl_mbox_cmd(cs35l41, CSPL_MBOX_CMD_RESUME); + case SND_SOC_DAPM_PRE_PMD: + return cs35l41_set_cspl_mbox_cmd(cs35l41, CSPL_MBOX_CMD_PAUSE); + default: + return 0; + } +} + static const char * const cs35l41_pcm_source_texts[] = {"ASP", "DSP"}; static const unsigned int cs35l41_pcm_source_values[] = {0x08, 0x32}; static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_pcm_source_enum, @@ -255,6 +371,24 @@ static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_asptx4_enum, static const struct snd_kcontrol_new asp_tx4_mux = SOC_DAPM_ENUM("ASPTX4 SRC", cs35l41_asptx4_enum); +static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_dsprx1_enum, + CS35L41_DSP1_RX1_SRC, + 0, CS35L41_ASP_SOURCE_MASK, + cs35l41_tx_input_texts, + cs35l41_tx_input_values); + +static const struct snd_kcontrol_new dsp_rx1_mux = + SOC_DAPM_ENUM("DSPRX1 SRC", cs35l41_dsprx1_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_dsprx2_enum, + CS35L41_DSP1_RX2_SRC, + 0, CS35L41_ASP_SOURCE_MASK, + cs35l41_tx_input_texts, + cs35l41_tx_input_values); + +static const struct snd_kcontrol_new dsp_rx2_mux = + SOC_DAPM_ENUM("DSPRX2 SRC", cs35l41_dsprx2_enum); + static const struct snd_kcontrol_new cs35l41_aud_controls[] = { SOC_SINGLE_SX_TLV("Digital PCM Volume", CS35L41_AMP_DIG_VOL_CTRL, 3, 0x4CF, 0x391, dig_vol_tlv), @@ -282,6 +416,8 @@ static const struct snd_kcontrol_new cs35l41_aud_controls[] = { CS35L41_AMP_INV_PCM_SHIFT, 1, 0), SOC_SINGLE("Amp Gain ZC", CS35L41_AMP_GAIN_CTRL, CS35L41_AMP_GAIN_ZC_SHIFT, 1, 0), + WM_ADSP2_PRELOAD_SWITCH("DSP1", 1), + WM_ADSP_FW_CONTROL("DSP1", 0), }; static const struct cs35l41_otp_map_element_t *cs35l41_find_otp_map(u32 otp_id) @@ -603,6 +739,14 @@ static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w, } static const struct snd_soc_dapm_widget cs35l41_dapm_widgets[] = { + SND_SOC_DAPM_SPK("DSP1 Preload", NULL), + SND_SOC_DAPM_SUPPLY_S("DSP1 Preloader", 100, SND_SOC_NOPM, 0, 0, + cs35l41_dsp_preload_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_OUT_DRV_E("DSP1", SND_SOC_NOPM, 0, 0, NULL, 0, + cs35l41_dsp_audio_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_OUTPUT("SPK"), SND_SOC_DAPM_AIF_IN("ASPRX1", NULL, 0, CS35L41_SP_ENABLES, 16, 0), @@ -618,11 +762,18 @@ static const struct snd_soc_dapm_widget cs35l41_dapm_widgets[] = { SND_SOC_DAPM_SIGGEN("VBST"), SND_SOC_DAPM_SIGGEN("TEMP"), - SND_SOC_DAPM_ADC("VMON ADC", NULL, CS35L41_PWR_CTRL2, 12, 0), - SND_SOC_DAPM_ADC("IMON ADC", NULL, CS35L41_PWR_CTRL2, 13, 0), - SND_SOC_DAPM_ADC("VPMON ADC", NULL, CS35L41_PWR_CTRL2, 8, 0), - SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, CS35L41_PWR_CTRL2, 9, 0), - SND_SOC_DAPM_ADC("TEMPMON ADC", NULL, CS35L41_PWR_CTRL2, 10, 0), + SND_SOC_DAPM_SUPPLY("VMON", CS35L41_PWR_CTRL2, 12, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("IMON", CS35L41_PWR_CTRL2, 13, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("VPMON", CS35L41_PWR_CTRL2, 8, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("VBSTMON", CS35L41_PWR_CTRL2, 9, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("TEMPMON", CS35L41_PWR_CTRL2, 10, 0, NULL, 0), + + SND_SOC_DAPM_ADC("VMON ADC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("IMON ADC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("VPMON ADC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("TEMPMON ADC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("CLASS H", NULL, CS35L41_PWR_CTRL3, 4, 0), SND_SOC_DAPM_OUT_DRV_E("Main AMP", CS35L41_PWR_CTRL2, 0, 0, NULL, 0, @@ -633,33 +784,51 @@ static const struct snd_soc_dapm_widget cs35l41_dapm_widgets[] = { SND_SOC_DAPM_MUX("ASP TX2 Source", SND_SOC_NOPM, 0, 0, &asp_tx2_mux), SND_SOC_DAPM_MUX("ASP TX3 Source", SND_SOC_NOPM, 0, 0, &asp_tx3_mux), SND_SOC_DAPM_MUX("ASP TX4 Source", SND_SOC_NOPM, 0, 0, &asp_tx4_mux), + SND_SOC_DAPM_MUX("DSP RX1 Source", SND_SOC_NOPM, 0, 0, &dsp_rx1_mux), + SND_SOC_DAPM_MUX("DSP RX2 Source", SND_SOC_NOPM, 0, 0, &dsp_rx2_mux), SND_SOC_DAPM_MUX("PCM Source", SND_SOC_NOPM, 0, 0, &pcm_source_mux), SND_SOC_DAPM_SWITCH("DRE", SND_SOC_NOPM, 0, 0, &dre_ctrl), }; static const struct snd_soc_dapm_route cs35l41_audio_map[] = { + {"DSP RX1 Source", "ASPRX1", "ASPRX1"}, + {"DSP RX1 Source", "ASPRX2", "ASPRX2"}, + {"DSP RX2 Source", "ASPRX1", "ASPRX1"}, + {"DSP RX2 Source", "ASPRX2", "ASPRX2"}, + + {"DSP1", NULL, "DSP RX1 Source"}, + {"DSP1", NULL, "DSP RX2 Source"}, + {"ASP TX1 Source", "VMON", "VMON ADC"}, {"ASP TX1 Source", "IMON", "IMON ADC"}, {"ASP TX1 Source", "VPMON", "VPMON ADC"}, {"ASP TX1 Source", "VBSTMON", "VBSTMON ADC"}, + {"ASP TX1 Source", "DSPTX1", "DSP1"}, + {"ASP TX1 Source", "DSPTX2", "DSP1"}, {"ASP TX1 Source", "ASPRX1", "ASPRX1" }, {"ASP TX1 Source", "ASPRX2", "ASPRX2" }, {"ASP TX2 Source", "VMON", "VMON ADC"}, {"ASP TX2 Source", "IMON", "IMON ADC"}, {"ASP TX2 Source", "VPMON", "VPMON ADC"}, {"ASP TX2 Source", "VBSTMON", "VBSTMON ADC"}, + {"ASP TX2 Source", "DSPTX1", "DSP1"}, + {"ASP TX2 Source", "DSPTX2", "DSP1"}, {"ASP TX2 Source", "ASPRX1", "ASPRX1" }, {"ASP TX2 Source", "ASPRX2", "ASPRX2" }, {"ASP TX3 Source", "VMON", "VMON ADC"}, {"ASP TX3 Source", "IMON", "IMON ADC"}, {"ASP TX3 Source", "VPMON", "VPMON ADC"}, {"ASP TX3 Source", "VBSTMON", "VBSTMON ADC"}, + {"ASP TX3 Source", "DSPTX1", "DSP1"}, + {"ASP TX3 Source", "DSPTX2", "DSP1"}, {"ASP TX3 Source", "ASPRX1", "ASPRX1" }, {"ASP TX3 Source", "ASPRX2", "ASPRX2" }, {"ASP TX4 Source", "VMON", "VMON ADC"}, {"ASP TX4 Source", "IMON", "IMON ADC"}, {"ASP TX4 Source", "VPMON", "VPMON ADC"}, {"ASP TX4 Source", "VBSTMON", "VBSTMON ADC"}, + {"ASP TX4 Source", "DSPTX1", "DSP1"}, + {"ASP TX4 Source", "DSPTX2", "DSP1"}, {"ASP TX4 Source", "ASPRX1", "ASPRX1" }, {"ASP TX4 Source", "ASPRX2", "ASPRX2" }, {"ASPTX1", NULL, "ASP TX1 Source"}, @@ -671,12 +840,27 @@ static const struct snd_soc_dapm_route cs35l41_audio_map[] = { {"AMP Capture", NULL, "ASPTX3"}, {"AMP Capture", NULL, "ASPTX4"}, + {"DSP1", NULL, "VMON"}, + {"DSP1", NULL, "IMON"}, + {"DSP1", NULL, "VPMON"}, + {"DSP1", NULL, "VBSTMON"}, + {"DSP1", NULL, "TEMPMON"}, + + {"VMON ADC", NULL, "VMON"}, + {"IMON ADC", NULL, "IMON"}, + {"VPMON ADC", NULL, "VPMON"}, + {"VBSTMON ADC", NULL, "VBSTMON"}, + {"TEMPMON ADC", NULL, "TEMPMON"}, + {"VMON ADC", NULL, "VSENSE"}, {"IMON ADC", NULL, "ISENSE"}, {"VPMON ADC", NULL, "VP"}, {"VBSTMON ADC", NULL, "VBST"}, {"TEMPMON ADC", NULL, "TEMP"}, + {"DSP1 Preload", NULL, "DSP1 Preloader"}, + {"DSP1", NULL, "DSP1 Preloader"}, + {"ASPRX1", NULL, "AMP Playback"}, {"ASPRX2", NULL, "AMP Playback"}, {"DRE", "Switch", "CLASS H"}, @@ -685,9 +869,18 @@ static const struct snd_soc_dapm_route cs35l41_audio_map[] = { {"SPK", NULL, "Main AMP"}, {"PCM Source", "ASP", "ASPRX1"}, + {"PCM Source", "DSP", "DSP1"}, {"CLASS H", NULL, "PCM Source"}, }; +static const struct cs_dsp_region cs35l41_dsp1_regions[] = { + { .type = WMFW_HALO_PM_PACKED, .base = CS35L41_DSP1_PMEM_0 }, + { .type = WMFW_HALO_XM_PACKED, .base = CS35L41_DSP1_XMEM_PACK_0 }, + { .type = WMFW_HALO_YM_PACKED, .base = CS35L41_DSP1_YMEM_PACK_0 }, + {. type = WMFW_ADSP2_XM, .base = CS35L41_DSP1_XMEM_UNPACK24_0}, + {. type = WMFW_ADSP2_YM, .base = CS35L41_DSP1_YMEM_UNPACK24_0}, +}; + static int cs35l41_set_channel_map(struct snd_soc_dai *dai, unsigned int tx_num, unsigned int *tx_slot, unsigned int rx_num, unsigned int *rx_slot) @@ -1098,6 +1291,20 @@ static int cs35l41_irq_gpio_config(struct cs35l41_private *cs35l41) return irq_pol; } +static int cs35l41_component_probe(struct snd_soc_component *component) +{ + struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component); + + return wm_adsp2_component_probe(&cs35l41->dsp, component); +} + +static void cs35l41_component_remove(struct snd_soc_component *component) +{ + struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component); + + wm_adsp2_component_remove(&cs35l41->dsp, component); +} + static const struct snd_soc_dai_ops cs35l41_ops = { .startup = cs35l41_pcm_startup, .set_fmt = cs35l41_set_dai_fmt, @@ -1131,6 +1338,8 @@ static struct snd_soc_dai_driver cs35l41_dai[] = { static const struct snd_soc_component_driver soc_component_dev_cs35l41 = { .name = "cs35l41-codec", + .probe = cs35l41_component_probe, + .remove = cs35l41_component_remove, .dapm_widgets = cs35l41_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(cs35l41_dapm_widgets), @@ -1237,6 +1446,90 @@ static const struct reg_sequence cs35l41_revb2_errata_patch[] = { { 0x00000040, 0x00003333 }, }; +static const struct reg_sequence cs35l41_fs_errata_patch[] = { + { CS35L41_DSP1_RX1_RATE, 0x00000001 }, + { CS35L41_DSP1_RX2_RATE, 0x00000001 }, + { CS35L41_DSP1_RX3_RATE, 0x00000001 }, + { CS35L41_DSP1_RX4_RATE, 0x00000001 }, + { CS35L41_DSP1_RX5_RATE, 0x00000001 }, + { CS35L41_DSP1_RX6_RATE, 0x00000001 }, + { CS35L41_DSP1_RX7_RATE, 0x00000001 }, + { CS35L41_DSP1_RX8_RATE, 0x00000001 }, + { CS35L41_DSP1_TX1_RATE, 0x00000001 }, + { CS35L41_DSP1_TX2_RATE, 0x00000001 }, + { CS35L41_DSP1_TX3_RATE, 0x00000001 }, + { CS35L41_DSP1_TX4_RATE, 0x00000001 }, + { CS35L41_DSP1_TX5_RATE, 0x00000001 }, + { CS35L41_DSP1_TX6_RATE, 0x00000001 }, + { CS35L41_DSP1_TX7_RATE, 0x00000001 }, + { CS35L41_DSP1_TX8_RATE, 0x00000001 }, +}; + +static int cs35l41_dsp_init(struct cs35l41_private *cs35l41) +{ + struct wm_adsp *dsp; + int ret; + + dsp = &cs35l41->dsp; + dsp->part = "cs35l41"; + dsp->cs_dsp.num = 1; + dsp->cs_dsp.type = WMFW_HALO; + dsp->cs_dsp.rev = 0; + dsp->fw = 9; /* 9 is WM_ADSP_FW_SPK_PROT in wm_adsp.c */ + dsp->cs_dsp.dev = cs35l41->dev; + dsp->cs_dsp.regmap = cs35l41->regmap; + dsp->cs_dsp.base = CS35L41_DSP1_CTRL_BASE; + dsp->cs_dsp.base_sysinfo = CS35L41_DSP1_SYS_ID; + dsp->cs_dsp.mem = cs35l41_dsp1_regions; + dsp->cs_dsp.num_mems = ARRAY_SIZE(cs35l41_dsp1_regions); + dsp->cs_dsp.lock_regions = 0xFFFFFFFF; + + ret = regmap_multi_reg_write(cs35l41->regmap, cs35l41_fs_errata_patch, + ARRAY_SIZE(cs35l41_fs_errata_patch)); + if (ret < 0) { + dev_err(cs35l41->dev, "Failed to write fs errata: %d\n", ret); + return ret; + } + + ret = wm_halo_init(dsp); + if (ret) { + dev_err(cs35l41->dev, "wm_halo_init failed: %d\n", ret); + return ret; + } + + ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX5_SRC, + CS35L41_INPUT_SRC_VPMON); + if (ret < 0) { + dev_err(cs35l41->dev, "Write INPUT_SRC_VPMON failed: %d\n", ret); + goto err_dsp; + } + ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX6_SRC, + CS35L41_INPUT_SRC_CLASSH); + if (ret < 0) { + dev_err(cs35l41->dev, "Write INPUT_SRC_CLASSH failed: %d\n", ret); + goto err_dsp; + } + ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX7_SRC, + CS35L41_INPUT_SRC_TEMPMON); + if (ret < 0) { + dev_err(cs35l41->dev, "Write INPUT_SRC_TEMPMON failed: %d\n", ret); + goto err_dsp; + } + ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX8_SRC, + CS35L41_INPUT_SRC_RSVD); + if (ret < 0) { + dev_err(cs35l41->dev, "Write INPUT_SRC_RSVD failed: %d\n", ret); + goto err_dsp; + } + + return 0; + +err_dsp: + wm_adsp2_remove(dsp); + + return ret; +} + int cs35l41_probe(struct cs35l41_private *cs35l41, struct cs35l41_platform_data *pdata) { @@ -1413,12 +1706,16 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, goto err; } + ret = cs35l41_dsp_init(cs35l41); + if (ret < 0) + goto err; + ret = devm_snd_soc_register_component(cs35l41->dev, &soc_component_dev_cs35l41, cs35l41_dai, ARRAY_SIZE(cs35l41_dai)); if (ret < 0) { dev_err(cs35l41->dev, "Register codec failed: %d\n", ret); - goto err; + goto err_dsp; } dev_info(cs35l41->dev, "Cirrus Logic CS35L41 (%x), Revision: %02X\n", @@ -1426,6 +1723,8 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, return 0; +err_dsp: + wm_adsp2_remove(&cs35l41->dsp); err: regulator_bulk_disable(CS35L41_NUM_SUPPLIES, cs35l41->supplies); gpiod_set_value_cansleep(cs35l41->reset_gpio, 0); @@ -1436,6 +1735,7 @@ err: void cs35l41_remove(struct cs35l41_private *cs35l41) { regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1, 0xFFFFFFFF); + wm_adsp2_remove(&cs35l41->dsp); regulator_bulk_disable(CS35L41_NUM_SUPPLIES, cs35l41->supplies); gpiod_set_value_cansleep(cs35l41->reset_gpio, 0); } diff --git a/sound/soc/codecs/cs35l41.h b/sound/soc/codecs/cs35l41.h index 6cffe8a55beb..eea3b14acb0b 100644 --- a/sound/soc/codecs/cs35l41.h +++ b/sound/soc/codecs/cs35l41.h @@ -13,9 +13,12 @@ #include #include #include +#include #include #include +#include "wm_adsp.h" + #define CS35L41_FIRSTREG 0x00000000 #define CS35L41_LASTREG 0x03804FE8 #define CS35L41_DEVID 0x00000000 @@ -755,7 +758,24 @@ extern const struct cs35l41_otp_map_element_t #define CS35L41_REGSTRIDE 4 +enum cs35l41_cspl_mbox_status { + CSPL_MBOX_STS_RUNNING = 0, + CSPL_MBOX_STS_PAUSED = 1, + CSPL_MBOX_STS_RDY_FOR_REINIT = 2, +}; + +enum cs35l41_cspl_mbox_cmd { + CSPL_MBOX_CMD_NONE = 0, + CSPL_MBOX_CMD_PAUSE = 1, + CSPL_MBOX_CMD_RESUME = 2, + CSPL_MBOX_CMD_REINIT = 3, + CSPL_MBOX_CMD_STOP_PRE_REINIT = 4, + CSPL_MBOX_CMD_UNKNOWN_CMD = -1, + CSPL_MBOX_CMD_INVALID_SEQUENCE = -2, +}; + struct cs35l41_private { + struct wm_adsp dsp; /* needs to be first member */ struct snd_soc_codec *codec; struct cs35l41_platform_data pdata; struct device *dev; -- cgit v1.2.3 From 95cead06866a95baf0f8355bba81a8142d5908cf Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Wed, 3 Nov 2021 20:19:27 +0100 Subject: ASoC: codecs: Axe some dead code in 'wcd_mbhc_adc_hs_rem_irq()' 'hphpa_on' is know to be false, so this is just dead code that should be removed. Suggested-by: Srinivas Kandagatla Signed-off-by: Christophe JAILLET Reviewed-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/57a89cc31eb2312addd3c77896d7df8206aef138.1635967035.git.christophe.jaillet@wanadoo.fr Signed-off-by: Mark Brown --- sound/soc/codecs/wcd-mbhc-v2.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c index 405128ccb4b0..b905eb8f3c67 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.c +++ b/sound/soc/codecs/wcd-mbhc-v2.c @@ -1176,7 +1176,6 @@ static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data) struct wcd_mbhc *mbhc = data; unsigned long timeout; int adc_threshold, output_mv, retry = 0; - bool hphpa_on = false; mutex_lock(&mbhc->lock); timeout = jiffies + msecs_to_jiffies(WCD_FAKE_REMOVAL_MIN_PERIOD_MS); @@ -1210,10 +1209,6 @@ static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data) wcd_mbhc_elec_hs_report_unplug(mbhc); wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0); - if (hphpa_on) { - hphpa_on = false; - wcd_mbhc_write_field(mbhc, WCD_MBHC_HPH_PA_EN, 3); - } exit: mutex_unlock(&mbhc->lock); return IRQ_HANDLED; -- cgit v1.2.3 From 8d0872f6239f9d067d538d8368bdec643bb0d255 Mon Sep 17 00:00:00 2001 From: Mac Chiang Date: Tue, 9 Nov 2021 08:38:08 -0500 Subject: ASoC: Intel: add sof-nau8825 machine driver The machine driver is a generic machine driver for SOF with nau8825 codec w or w/o speaker additionally. Depending on the SOC HDMI, DMIC, Bluetooth offload support are added dynamically. Only add information related to SOF since the machine driver was only tested with SOF. There are currently 4 i2s machine variants of ADL. This supports the headphone NUA8825(SSP0) alone or with smart or dumb speakers. Board 2,3,4 use SSP2 for Bluetooth offload support except board 1. Board 1 : NAU8825 + RT1019P(SSP2) Board 2 : NAU8825 + MAX98373(SSP1) Board 3 : NAU8825 + MAX98360A(SSP1) Board 4 : NAU8825 Signed-off-by: David Lin Co-developed-by: Mac Chiang Signed-off-by: Mac Chiang Acked-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20211109133808.8729-1-mac.chiang@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/Kconfig | 19 + sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/sof_nau8825.c | 651 ++++++++++++++++++++++ sound/soc/intel/common/soc-acpi-intel-adl-match.c | 35 ++ 4 files changed, 707 insertions(+) create mode 100644 sound/soc/intel/boards/sof_nau8825.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 2dd5ff7e35ce..849445fcc05d 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -527,6 +527,25 @@ config SND_SOC_INTEL_SOF_ES8336_MACH Say Y if you have such a device. If unsure select "N". +config SND_SOC_INTEL_SOF_NAU8825_MACH + tristate "SOF with nau8825 codec in I2S Mode" + depends on I2C && ACPI && GPIOLIB + depends on ((SND_HDA_CODEC_HDMI && SND_SOC_SOF_HDA_AUDIO_CODEC) &&\ + (MFD_INTEL_LPSS || COMPILE_TEST)) + select SND_SOC_NAU8825 + select SND_SOC_RT1015P + select SND_SOC_MAX98373_I2C + select SND_SOC_MAX98357A + select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI + select SND_SOC_INTEL_HDA_DSP_COMMON + select SND_SOC_INTEL_SOF_MAXIM_COMMON + help + This adds support for ASoC machine driver for SOF platforms + with nau8825 codec. + Say Y if you have such a device. + If unsure select "N". + endif ## SND_SOC_SOF_HDA_LINK || SND_SOC_SOF_BAYTRAIL if (SND_SOC_SOF_COMETLAKE && SND_SOC_SOF_HDA_LINK) diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 9ee8ed864f5d..3ea273d27168 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -22,6 +22,7 @@ snd-soc-sst-byt-cht-nocodec-objs := bytcht_nocodec.o snd-soc-sof_rt5682-objs := sof_rt5682.o sof_realtek_common.o snd-soc-sof_cs42l42-objs := sof_cs42l42.o snd-soc-sof_es8336-objs := sof_es8336.o +snd-soc-sof_nau8825-objs := sof_nau8825.o sof_realtek_common.o snd-soc-cml_rt1011_rt5682-objs := cml_rt1011_rt5682.o snd-soc-kbl_da7219_max98357a-objs := kbl_da7219_max98357a.o snd-soc-kbl_da7219_max98927-objs := kbl_da7219_max98927.o @@ -44,6 +45,7 @@ snd-soc-sof-sdw-objs += sof_sdw.o \ obj-$(CONFIG_SND_SOC_INTEL_SOF_RT5682_MACH) += snd-soc-sof_rt5682.o obj-$(CONFIG_SND_SOC_INTEL_SOF_CS42L42_MACH) += snd-soc-sof_cs42l42.o obj-$(CONFIG_SND_SOC_INTEL_SOF_ES8336_MACH) += snd-soc-sof_es8336.o +obj-$(CONFIG_SND_SOC_INTEL_SOF_NAU8825_MACH) += snd-soc-sof_nau8825.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_COMMON) += snd-soc-sst-bxt-da7219_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.o diff --git a/sound/soc/intel/boards/sof_nau8825.c b/sound/soc/intel/boards/sof_nau8825.c new file mode 100644 index 000000000000..33de043b66c6 --- /dev/null +++ b/sound/soc/intel/boards/sof_nau8825.c @@ -0,0 +1,651 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright(c) 2021 Intel Corporation. +// Copyright(c) 2021 Nuvoton Corporation. + +/* + * Intel SOF Machine Driver with Nuvoton headphone codec NAU8825 + * and speaker codec RT1019P MAX98360a or MAX98373 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../codecs/nau8825.h" +#include "../common/soc-intel-quirks.h" +#include "hda_dsp_common.h" +#include "sof_realtek_common.h" +#include "sof_maxim_common.h" + +#define NAME_SIZE 32 + +#define SOF_NAU8825_SSP_CODEC(quirk) ((quirk) & GENMASK(2, 0)) +#define SOF_NAU8825_SSP_CODEC_MASK (GENMASK(2, 0)) +#define SOF_SPEAKER_AMP_PRESENT BIT(3) +#define SOF_NAU8825_SSP_AMP_SHIFT 4 +#define SOF_NAU8825_SSP_AMP_MASK (GENMASK(6, 4)) +#define SOF_NAU8825_SSP_AMP(quirk) \ + (((quirk) << SOF_NAU8825_SSP_AMP_SHIFT) & SOF_NAU8825_SSP_AMP_MASK) +#define SOF_NAU8825_NUM_HDMIDEV_SHIFT 7 +#define SOF_NAU8825_NUM_HDMIDEV_MASK (GENMASK(9, 7)) +#define SOF_NAU8825_NUM_HDMIDEV(quirk) \ + (((quirk) << SOF_NAU8825_NUM_HDMIDEV_SHIFT) & SOF_NAU8825_NUM_HDMIDEV_MASK) + +/* BT audio offload: reserve 3 bits for future */ +#define SOF_BT_OFFLOAD_SSP_SHIFT 10 +#define SOF_BT_OFFLOAD_SSP_MASK (GENMASK(12, 10)) +#define SOF_BT_OFFLOAD_SSP(quirk) \ + (((quirk) << SOF_BT_OFFLOAD_SSP_SHIFT) & SOF_BT_OFFLOAD_SSP_MASK) +#define SOF_SSP_BT_OFFLOAD_PRESENT BIT(13) +#define SOF_RT1019P_SPEAKER_AMP_PRESENT BIT(14) +#define SOF_MAX98373_SPEAKER_AMP_PRESENT BIT(15) +#define SOF_MAX98360A_SPEAKER_AMP_PRESENT BIT(16) + +static unsigned long sof_nau8825_quirk = SOF_NAU8825_SSP_CODEC(0); + +struct sof_hdmi_pcm { + struct list_head head; + struct snd_soc_dai *codec_dai; + int device; +}; + +struct sof_card_private { + struct clk *mclk; + struct snd_soc_jack sof_headset; + struct list_head hdmi_pcm_list; +}; + +static int sof_hdmi_init(struct snd_soc_pcm_runtime *rtd) +{ + struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0); + struct sof_hdmi_pcm *pcm; + + pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + /* dai_link id is 1:1 mapped to the PCM device */ + pcm->device = rtd->dai_link->id; + pcm->codec_dai = dai; + + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; +} + +static int sof_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; + + struct snd_soc_jack *jack; + int ret; + + /* + * Headset buttons map to the google Reference headset. + * These can be configured by userspace. + */ + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2 | + SND_JACK_BTN_3, + &ctx->sof_headset, NULL, 0); + if (ret) { + dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret); + return ret; + } + + jack = &ctx->sof_headset; + + snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); + snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP); + snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); + ret = snd_soc_component_set_jack(component, jack, NULL); + + if (ret) { + dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret); + return ret; + } + + return ret; +}; + +static void sof_nau8825_codec_exit(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; + + snd_soc_component_set_jack(component, NULL, NULL); +} + +static int sof_nau8825_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + int clk_freq, ret; + + clk_freq = sof_dai_get_bclk(rtd); /* BCLK freq */ + + if (clk_freq <= 0) { + dev_err(rtd->dev, "get bclk freq failed: %d\n", clk_freq); + return -EINVAL; + } + + /* Configure clock for codec */ + ret = snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_FLL_BLK, 0, + SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(codec_dai->dev, "can't set BCLK clock %d\n", ret); + return ret; + } + + /* Configure pll for codec */ + ret = snd_soc_dai_set_pll(codec_dai, 0, 0, clk_freq, + params_rate(params) * 256); + if (ret < 0) { + dev_err(codec_dai->dev, "can't set BCLK: %d\n", ret); + return ret; + } + + return ret; +} + +static struct snd_soc_ops sof_nau8825_ops = { + .hw_params = sof_nau8825_hw_params, +}; + +static struct snd_soc_dai_link_component platform_component[] = { + { + /* name might be overridden during probe */ + .name = "0000:00:1f.3" + } +}; + +static int sof_card_late_probe(struct snd_soc_card *card) +{ + struct sof_card_private *ctx = snd_soc_card_get_drvdata(card); + struct snd_soc_dapm_context *dapm = &card->dapm; + struct sof_hdmi_pcm *pcm; + int err; + + if (list_empty(&ctx->hdmi_pcm_list)) + return -EINVAL; + + pcm = list_first_entry(&ctx->hdmi_pcm_list, struct sof_hdmi_pcm, head); + + if (sof_nau8825_quirk & SOF_MAX98373_SPEAKER_AMP_PRESENT) { + /* Disable Left and Right Spk pin after boot */ + snd_soc_dapm_disable_pin(dapm, "Left Spk"); + snd_soc_dapm_disable_pin(dapm, "Right Spk"); + err = snd_soc_dapm_sync(dapm); + if (err < 0) + return err; + } + + return hda_dsp_hdmi_build_controls(card, pcm->codec_dai->component); +} + +static const struct snd_kcontrol_new sof_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Left Spk"), + SOC_DAPM_PIN_SWITCH("Right Spk"), +}; + +static const struct snd_kcontrol_new speaker_controls[] = { + SOC_DAPM_PIN_SWITCH("Spk"), +}; + +static const struct snd_soc_dapm_widget sof_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_SPK("Left Spk", NULL), + SND_SOC_DAPM_SPK("Right Spk", NULL), +}; + +static const struct snd_soc_dapm_widget speaker_widgets[] = { + SND_SOC_DAPM_SPK("Spk", NULL), +}; + +static const struct snd_soc_dapm_widget dmic_widgets[] = { + SND_SOC_DAPM_MIC("SoC DMIC", NULL), +}; + +static const struct snd_soc_dapm_route sof_map[] = { + /* HP jack connectors - unknown if we have jack detection */ + { "Headphone Jack", NULL, "HPOL" }, + { "Headphone Jack", NULL, "HPOR" }, + + /* other jacks */ + { "MIC", NULL, "Headset Mic" }, +}; + +static const struct snd_soc_dapm_route speaker_map[] = { + /* speaker */ + { "Spk", NULL, "Speaker" }, +}; + +static const struct snd_soc_dapm_route dmic_map[] = { + /* digital mics */ + {"DMic", NULL, "SoC DMIC"}, +}; + +static int speaker_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + int ret; + + ret = snd_soc_dapm_new_controls(&card->dapm, speaker_widgets, + ARRAY_SIZE(speaker_widgets)); + if (ret) { + dev_err(rtd->dev, "unable to add dapm controls, ret %d\n", ret); + /* Don't need to add routes if widget addition failed */ + return ret; + } + + ret = snd_soc_add_card_controls(card, speaker_controls, + ARRAY_SIZE(speaker_controls)); + if (ret) { + dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_add_routes(&card->dapm, speaker_map, + ARRAY_SIZE(speaker_map)); + + if (ret) + dev_err(rtd->dev, "Speaker map addition failed: %d\n", ret); + return ret; +} + +static int dmic_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + int ret; + + ret = snd_soc_dapm_new_controls(&card->dapm, dmic_widgets, + ARRAY_SIZE(dmic_widgets)); + if (ret) { + dev_err(card->dev, "DMic widget addition failed: %d\n", ret); + /* Don't need to add routes if widget addition failed */ + return ret; + } + + ret = snd_soc_dapm_add_routes(&card->dapm, dmic_map, + ARRAY_SIZE(dmic_map)); + + if (ret) + dev_err(card->dev, "DMic map addition failed: %d\n", ret); + + return ret; +} + +/* sof audio machine driver for nau8825 codec */ +static struct snd_soc_card sof_audio_card_nau8825 = { + .name = "nau8825", /* the sof- prefix is added by the core */ + .owner = THIS_MODULE, + .controls = sof_controls, + .num_controls = ARRAY_SIZE(sof_controls), + .dapm_widgets = sof_widgets, + .num_dapm_widgets = ARRAY_SIZE(sof_widgets), + .dapm_routes = sof_map, + .num_dapm_routes = ARRAY_SIZE(sof_map), + .fully_routed = true, + .late_probe = sof_card_late_probe, +}; + +static struct snd_soc_dai_link_component nau8825_component[] = { + { + .name = "i2c-10508825:00", + .dai_name = "nau8825-hifi", + } +}; + +static struct snd_soc_dai_link_component dmic_component[] = { + { + .name = "dmic-codec", + .dai_name = "dmic-hifi", + } +}; + +static struct snd_soc_dai_link_component rt1019p_component[] = { + { + .name = "RTL1019:00", + .dai_name = "HiFi", + } +}; + +static struct snd_soc_dai_link_component dummy_component[] = { + { + .name = "snd-soc-dummy", + .dai_name = "snd-soc-dummy-dai", + } +}; + +static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, + int ssp_codec, + int ssp_amp, + int dmic_be_num, + int hdmi_num) +{ + struct snd_soc_dai_link_component *idisp_components; + struct snd_soc_dai_link_component *cpus; + struct snd_soc_dai_link *links; + int i, id = 0; + + links = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link) * + sof_audio_card_nau8825.num_links, GFP_KERNEL); + cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component) * + sof_audio_card_nau8825.num_links, GFP_KERNEL); + if (!links || !cpus) + goto devm_err; + + /* codec SSP */ + links[id].name = devm_kasprintf(dev, GFP_KERNEL, + "SSP%d-Codec", ssp_codec); + if (!links[id].name) + goto devm_err; + + links[id].id = id; + links[id].codecs = nau8825_component; + links[id].num_codecs = ARRAY_SIZE(nau8825_component); + links[id].platforms = platform_component; + links[id].num_platforms = ARRAY_SIZE(platform_component); + links[id].init = sof_nau8825_codec_init; + links[id].exit = sof_nau8825_codec_exit; + links[id].ops = &sof_nau8825_ops; + links[id].dpcm_playback = 1; + links[id].dpcm_capture = 1; + links[id].no_pcm = 1; + links[id].cpus = &cpus[id]; + links[id].num_cpus = 1; + + links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, + "SSP%d Pin", + ssp_codec); + if (!links[id].cpus->dai_name) + goto devm_err; + + id++; + + /* dmic */ + if (dmic_be_num > 0) { + /* at least we have dmic01 */ + links[id].name = "dmic01"; + links[id].cpus = &cpus[id]; + links[id].cpus->dai_name = "DMIC01 Pin"; + links[id].init = dmic_init; + if (dmic_be_num > 1) { + /* set up 2 BE links at most */ + links[id + 1].name = "dmic16k"; + links[id + 1].cpus = &cpus[id + 1]; + links[id + 1].cpus->dai_name = "DMIC16k Pin"; + dmic_be_num = 2; + } + } + + for (i = 0; i < dmic_be_num; i++) { + links[id].id = id; + links[id].num_cpus = 1; + links[id].codecs = dmic_component; + links[id].num_codecs = ARRAY_SIZE(dmic_component); + links[id].platforms = platform_component; + links[id].num_platforms = ARRAY_SIZE(platform_component); + links[id].ignore_suspend = 1; + links[id].dpcm_capture = 1; + links[id].no_pcm = 1; + id++; + } + + /* HDMI */ + if (hdmi_num > 0) { + idisp_components = devm_kzalloc(dev, + sizeof(struct snd_soc_dai_link_component) * + hdmi_num, GFP_KERNEL); + if (!idisp_components) + goto devm_err; + } + for (i = 1; i <= hdmi_num; i++) { + links[id].name = devm_kasprintf(dev, GFP_KERNEL, + "iDisp%d", i); + if (!links[id].name) + goto devm_err; + + links[id].id = id; + links[id].cpus = &cpus[id]; + links[id].num_cpus = 1; + links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, + "iDisp%d Pin", i); + if (!links[id].cpus->dai_name) + goto devm_err; + + idisp_components[i - 1].name = "ehdaudio0D2"; + idisp_components[i - 1].dai_name = devm_kasprintf(dev, + GFP_KERNEL, + "intel-hdmi-hifi%d", + i); + if (!idisp_components[i - 1].dai_name) + goto devm_err; + + links[id].codecs = &idisp_components[i - 1]; + links[id].num_codecs = 1; + links[id].platforms = platform_component; + links[id].num_platforms = ARRAY_SIZE(platform_component); + links[id].init = sof_hdmi_init; + links[id].dpcm_playback = 1; + links[id].no_pcm = 1; + id++; + } + + /* speaker amp */ + if (sof_nau8825_quirk & SOF_SPEAKER_AMP_PRESENT) { + links[id].name = devm_kasprintf(dev, GFP_KERNEL, + "SSP%d-Codec", ssp_amp); + if (!links[id].name) + goto devm_err; + + links[id].id = id; + if (sof_nau8825_quirk & SOF_RT1019P_SPEAKER_AMP_PRESENT) { + links[id].codecs = rt1019p_component; + links[id].num_codecs = ARRAY_SIZE(rt1019p_component); + links[id].init = speaker_codec_init; + } else if (sof_nau8825_quirk & + SOF_MAX98373_SPEAKER_AMP_PRESENT) { + links[id].codecs = max_98373_components; + links[id].num_codecs = ARRAY_SIZE(max_98373_components); + links[id].init = max_98373_spk_codec_init; + links[id].ops = &max_98373_ops; + /* feedback stream */ + links[id].dpcm_capture = 1; + } else if (sof_nau8825_quirk & + SOF_MAX98360A_SPEAKER_AMP_PRESENT) { + max_98360a_dai_link(&links[id]); + } else { + goto devm_err; + } + + links[id].platforms = platform_component; + links[id].num_platforms = ARRAY_SIZE(platform_component); + links[id].dpcm_playback = 1; + links[id].no_pcm = 1; + links[id].cpus = &cpus[id]; + links[id].num_cpus = 1; + links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, + "SSP%d Pin", + ssp_amp); + if (!links[id].cpus->dai_name) + goto devm_err; + id++; + } + + /* BT audio offload */ + if (sof_nau8825_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) { + int port = (sof_nau8825_quirk & SOF_BT_OFFLOAD_SSP_MASK) >> + SOF_BT_OFFLOAD_SSP_SHIFT; + + links[id].id = id; + links[id].cpus = &cpus[id]; + links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, + "SSP%d Pin", port); + if (!links[id].cpus->dai_name) + goto devm_err; + links[id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT", port); + if (!links[id].name) + goto devm_err; + links[id].codecs = dummy_component; + links[id].num_codecs = ARRAY_SIZE(dummy_component); + links[id].platforms = platform_component; + links[id].num_platforms = ARRAY_SIZE(platform_component); + links[id].dpcm_playback = 1; + links[id].dpcm_capture = 1; + links[id].no_pcm = 1; + links[id].num_cpus = 1; + } + + return links; +devm_err: + return NULL; +} + +static int sof_audio_probe(struct platform_device *pdev) +{ + struct snd_soc_dai_link *dai_links; + struct snd_soc_acpi_mach *mach; + struct sof_card_private *ctx; + int dmic_be_num, hdmi_num; + int ret, ssp_amp, ssp_codec; + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + if (pdev->id_entry && pdev->id_entry->driver_data) + sof_nau8825_quirk = (unsigned long)pdev->id_entry->driver_data; + + mach = pdev->dev.platform_data; + + /* A speaker amp might not be present when the quirk claims one is. + * Detect this via whether the machine driver match includes quirk_data. + */ + if ((sof_nau8825_quirk & SOF_SPEAKER_AMP_PRESENT) && !mach->quirk_data) + sof_nau8825_quirk &= ~SOF_SPEAKER_AMP_PRESENT; + + dev_dbg(&pdev->dev, "sof_nau8825_quirk = %lx\n", sof_nau8825_quirk); + + /* default number of DMIC DAI's */ + dmic_be_num = 2; + hdmi_num = (sof_nau8825_quirk & SOF_NAU8825_NUM_HDMIDEV_MASK) >> + SOF_NAU8825_NUM_HDMIDEV_SHIFT; + /* default number of HDMI DAI's */ + if (!hdmi_num) + hdmi_num = 3; + + ssp_amp = (sof_nau8825_quirk & SOF_NAU8825_SSP_AMP_MASK) >> + SOF_NAU8825_SSP_AMP_SHIFT; + + ssp_codec = sof_nau8825_quirk & SOF_NAU8825_SSP_CODEC_MASK; + + /* compute number of dai links */ + sof_audio_card_nau8825.num_links = 1 + dmic_be_num + hdmi_num; + + if (sof_nau8825_quirk & SOF_SPEAKER_AMP_PRESENT) + sof_audio_card_nau8825.num_links++; + + if (sof_nau8825_quirk & SOF_MAX98373_SPEAKER_AMP_PRESENT) + max_98373_set_codec_conf(&sof_audio_card_nau8825); + + if (sof_nau8825_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) + sof_audio_card_nau8825.num_links++; + + dai_links = sof_card_dai_links_create(&pdev->dev, ssp_codec, ssp_amp, + dmic_be_num, hdmi_num); + if (!dai_links) + return -ENOMEM; + + sof_audio_card_nau8825.dai_link = dai_links; + + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); + + sof_audio_card_nau8825.dev = &pdev->dev; + + /* set platform name for each dailink */ + ret = snd_soc_fixup_dai_links_platform_name(&sof_audio_card_nau8825, + mach->mach_params.platform); + if (ret) + return ret; + + snd_soc_card_set_drvdata(&sof_audio_card_nau8825, ctx); + + return devm_snd_soc_register_card(&pdev->dev, + &sof_audio_card_nau8825); +} + +static const struct platform_device_id board_ids[] = { + { + .name = "sof_nau8825", + .driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) | + SOF_NAU8825_NUM_HDMIDEV(4) | + SOF_BT_OFFLOAD_SSP(2) | + SOF_SSP_BT_OFFLOAD_PRESENT), + + }, + { + .name = "adl_rt1019p_nau8825", + .driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) | + SOF_SPEAKER_AMP_PRESENT | + SOF_RT1019P_SPEAKER_AMP_PRESENT | + SOF_NAU8825_SSP_AMP(2) | + SOF_NAU8825_NUM_HDMIDEV(4)), + }, + { + .name = "adl_max98373_nau8825", + .driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) | + SOF_SPEAKER_AMP_PRESENT | + SOF_MAX98373_SPEAKER_AMP_PRESENT | + SOF_NAU8825_SSP_AMP(1) | + SOF_NAU8825_NUM_HDMIDEV(4) | + SOF_BT_OFFLOAD_SSP(2) | + SOF_SSP_BT_OFFLOAD_PRESENT), + }, + { + /* The limitation of length of char array, shorten the name */ + .name = "adl_mx98360a_nau8825", + .driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) | + SOF_SPEAKER_AMP_PRESENT | + SOF_MAX98360A_SPEAKER_AMP_PRESENT | + SOF_NAU8825_SSP_AMP(1) | + SOF_NAU8825_NUM_HDMIDEV(4) | + SOF_BT_OFFLOAD_SSP(2) | + SOF_SSP_BT_OFFLOAD_PRESENT), + + }, + { } +}; +MODULE_DEVICE_TABLE(platform, board_ids); + +static struct platform_driver sof_audio = { + .probe = sof_audio_probe, + .driver = { + .name = "sof_nau8825", + .pm = &snd_soc_pm_ops, + }, + .id_table = board_ids, +}; +module_platform_driver(sof_audio) + +/* Module information */ +MODULE_DESCRIPTION("SOF Audio Machine driver for NAU8825"); +MODULE_AUTHOR("David Lin "); +MODULE_AUTHOR("Mac Chiang "); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON); +MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_MAXIM_COMMON); diff --git a/sound/soc/intel/common/soc-acpi-intel-adl-match.c b/sound/soc/intel/common/soc-acpi-intel-adl-match.c index b61a778a9d26..fde310e5724b 100644 --- a/sound/soc/intel/common/soc-acpi-intel-adl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-adl-match.c @@ -374,6 +374,11 @@ static const struct snd_soc_acpi_codecs adl_rt5682_rt5682s_hp = { .codecs = {"10EC5682", "RTL5682"}, }; +static const struct snd_soc_acpi_codecs adl_rt1019p_amp = { + .num_codecs = 1, + .codecs = {"RTL1019"} +}; + struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { { .comp_ids = &adl_rt5682_rt5682s_hp, @@ -399,6 +404,36 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { .sof_fw_filename = "sof-adl.ri", .sof_tplg_filename = "sof-adl-max98360a-rt5682.tplg", }, + { + .id = "10508825", + .drv_name = "adl_rt1019p_nau8825", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &adl_rt1019p_amp, + .sof_fw_filename = "sof-adl.ri", + .sof_tplg_filename = "sof-adl-rt1019-nau8825.tplg", + }, + { + .id = "10508825", + .drv_name = "adl_max98373_nau8825", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &adl_max98373_amp, + .sof_fw_filename = "sof-adl.ri", + .sof_tplg_filename = "sof-adl-max98373-nau8825.tplg", + }, + { + .id = "10508825", + .drv_name = "adl_mx98360a_nau8825", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &adl_max98360a_amp, + .sof_fw_filename = "sof-adl.ri", + .sof_tplg_filename = "sof-adl-mx98360a-nau8825.tplg", + }, + { + .id = "10508825", + .drv_name = "sof_nau8825", + .sof_fw_filename = "sof-adl.ri", + .sof_tplg_filename = "sof-adl-nau8825.tplg", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_adl_machines); -- cgit v1.2.3 From 7ec4a058c16f3da9c2c0c66506f45c083198ed30 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 1 Nov 2021 10:10:06 +0000 Subject: ASoC: cs42l42: Add control for audio slow-start switch This adds an ALSA control so that the slow-start audio ramp feature can be disabled. This is useful for high-definition audio applications. The register field is unusual in that it is a 3-bit field with only two valid values, 000=off and 111=on. Signed-off-by: Richard Fitzgerald Link: https://lore.kernel.org/r/20211101101006.13092-1-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l42.c | 30 +++++++++++++++++++++++++++++- sound/soc/codecs/cs42l42.h | 3 +++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 27a1c4c73074..56804a3f285e 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -42,6 +42,7 @@ static const struct reg_default cs42l42_reg_defaults[] = { { CS42L42_SRC_CTL, 0x10 }, { CS42L42_MCLK_CTL, 0x02 }, { CS42L42_SFTRAMP_RATE, 0xA4 }, + { CS42L42_SLOW_START_ENABLE, 0x70 }, { CS42L42_I2C_DEBOUNCE, 0x88 }, { CS42L42_I2C_STRETCH, 0x03 }, { CS42L42_I2C_TIMEOUT, 0xB7 }, @@ -177,6 +178,7 @@ static bool cs42l42_readable_register(struct device *dev, unsigned int reg) case CS42L42_MCLK_STATUS: case CS42L42_MCLK_CTL: case CS42L42_SFTRAMP_RATE: + case CS42L42_SLOW_START_ENABLE: case CS42L42_I2C_DEBOUNCE: case CS42L42_I2C_STRETCH: case CS42L42_I2C_TIMEOUT: @@ -387,6 +389,28 @@ static const struct regmap_config cs42l42_regmap = { static DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 100, true); static DECLARE_TLV_DB_SCALE(mixer_tlv, -6300, 100, true); +static int cs42l42_slow_start_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + u8 val; + + /* all bits of SLOW_START_EN much change together */ + switch (ucontrol->value.integer.value[0]) { + case 0: + val = 0; + break; + case 1: + val = CS42L42_SLOW_START_EN_MASK; + break; + default: + return -EINVAL; + } + + return snd_soc_component_update_bits(component, CS42L42_SLOW_START_ENABLE, + CS42L42_SLOW_START_EN_MASK, val); +} + static const char * const cs42l42_hpf_freq_text[] = { "1.86Hz", "120Hz", "235Hz", "466Hz" }; @@ -431,7 +455,11 @@ static const struct snd_kcontrol_new cs42l42_snd_controls[] = { CS42L42_DAC_HPF_EN_SHIFT, true, false), SOC_DOUBLE_R_TLV("Mixer Volume", CS42L42_MIXER_CHA_VOL, CS42L42_MIXER_CHB_VOL, CS42L42_MIXER_CH_VOL_SHIFT, - 0x3f, 1, mixer_tlv) + 0x3f, 1, mixer_tlv), + + SOC_SINGLE_EXT("Slow Start Switch", CS42L42_SLOW_START_ENABLE, + CS42L42_SLOW_START_EN_SHIFT, true, false, + snd_soc_get_volsw, cs42l42_slow_start_put), }; static int cs42l42_hp_adc_ev(struct snd_soc_dapm_widget *w, diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h index f45bcc9a3a62..c8b3267a318b 100644 --- a/sound/soc/codecs/cs42l42.h +++ b/sound/soc/codecs/cs42l42.h @@ -62,6 +62,9 @@ #define CS42L42_INTERNAL_FS_MASK (1 << CS42L42_INTERNAL_FS_SHIFT) #define CS42L42_SFTRAMP_RATE (CS42L42_PAGE_10 + 0x0A) +#define CS42L42_SLOW_START_ENABLE (CS42L42_PAGE_10 + 0x0B) +#define CS42L42_SLOW_START_EN_MASK GENMASK(6, 4) +#define CS42L42_SLOW_START_EN_SHIFT 4 #define CS42L42_I2C_DEBOUNCE (CS42L42_PAGE_10 + 0x0E) #define CS42L42_I2C_STRETCH (CS42L42_PAGE_10 + 0x0F) #define CS42L42_I2C_TIMEOUT (CS42L42_PAGE_10 + 0x10) -- cgit v1.2.3 From 749303055b78bc38ec0790ccc596cae235446367 Mon Sep 17 00:00:00 2001 From: Simon Trimmer Date: Mon, 15 Nov 2021 12:02:15 +0000 Subject: firmware: cs_dsp: tidy includes in cs_dsp.c and cs_dsp.h This patch removes unused included header files and moves others into cs_dsp.h to ensure that types referenced in the header file are properly described to prevent compiler warnings. Signed-off-by: Simon Trimmer Acked-by: Charles Keepax Link: https://lore.kernel.org/r/20211115120215.56824-1-simont@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/firmware/cirrus/cs_dsp.c | 6 ------ include/linux/firmware/cirrus/cs_dsp.h | 5 +++++ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index 948dd8382686..1a0c6c793f6a 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -12,16 +12,10 @@ #include #include #include -#include -#include -#include -#include #include #include -#include #include #include -#include #include #include diff --git a/include/linux/firmware/cirrus/cs_dsp.h b/include/linux/firmware/cirrus/cs_dsp.h index 9ad9eaaaa552..3a54b1afc48f 100644 --- a/include/linux/firmware/cirrus/cs_dsp.h +++ b/include/linux/firmware/cirrus/cs_dsp.h @@ -11,6 +11,11 @@ #ifndef __CS_DSP_H #define __CS_DSP_H +#include +#include +#include +#include + #define CS_ADSP2_REGION_0 BIT(0) #define CS_ADSP2_REGION_1 BIT(1) #define CS_ADSP2_REGION_2 BIT(2) -- cgit v1.2.3 From 5dbec393cd23ecfdeddced217f8a1c11228139c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ADra=20Canal?= Date: Sun, 24 Oct 2021 15:42:07 -0300 Subject: ASoC: adau1701: Replace legacy gpio interface for gpiod MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Considering the current transition of the GPIO subsystem, remove all dependencies of the legacy GPIO interface (linux/gpio.h and linux /of_gpio.h) and replace it with the descriptor-based GPIO approach. Signed-off-by: Maíra Canal Link: https://lore.kernel.org/r/YXWo/9o7ye9a11aR@fedora Signed-off-by: Mark Brown --- sound/soc/codecs/adau1701.c | 94 +++++++++++++++------------------------------ 1 file changed, 31 insertions(+), 63 deletions(-) diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c index c5bf461c0b7e..dba9af753188 100644 --- a/sound/soc/codecs/adau1701.c +++ b/sound/soc/codecs/adau1701.c @@ -13,8 +13,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -106,8 +106,8 @@ static const char * const supply_names[] = { }; struct adau1701 { - int gpio_nreset; - int gpio_pll_mode[2]; + struct gpio_desc *gpio_nreset; + struct gpio_descs *gpio_pll_mode; unsigned int dai_fmt; unsigned int pll_clkdiv; unsigned int sysclk; @@ -303,39 +303,41 @@ static int adau1701_reset(struct snd_soc_component *component, unsigned int clkd struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component); int ret; + DECLARE_BITMAP(values, 2); sigmadsp_reset(adau1701->sigmadsp); - if (clkdiv != ADAU1707_CLKDIV_UNSET && - gpio_is_valid(adau1701->gpio_pll_mode[0]) && - gpio_is_valid(adau1701->gpio_pll_mode[1])) { + if (clkdiv != ADAU1707_CLKDIV_UNSET && adau1701->gpio_pll_mode) { switch (clkdiv) { case 64: - gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 0); - gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 0); + __assign_bit(0, values, 0); + __assign_bit(1, values, 0); break; case 256: - gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 0); - gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 1); + __assign_bit(0, values, 0); + __assign_bit(1, values, 1); break; case 384: - gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 1); - gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 0); + __assign_bit(0, values, 1); + __assign_bit(1, values, 0); break; - case 0: /* fallback */ + case 0: /* fallback */ case 512: - gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 1); - gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 1); + __assign_bit(0, values, 1); + __assign_bit(1, values, 1); break; } + gpiod_set_array_value_cansleep(adau1701->gpio_pll_mode->ndescs, + adau1701->gpio_pll_mode->desc, adau1701->gpio_pll_mode->info, + values); } adau1701->pll_clkdiv = clkdiv; - if (gpio_is_valid(adau1701->gpio_nreset)) { - gpio_set_value_cansleep(adau1701->gpio_nreset, 0); + if (adau1701->gpio_nreset) { + gpiod_set_value_cansleep(adau1701->gpio_nreset, 0); /* minimum reset time is 20ns */ udelay(1); - gpio_set_value_cansleep(adau1701->gpio_nreset, 1); + gpiod_set_value_cansleep(adau1701->gpio_nreset, 1); /* power-up time may be as long as 85ms */ mdelay(85); } @@ -719,8 +721,8 @@ static void adau1701_remove(struct snd_soc_component *component) { struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component); - if (gpio_is_valid(adau1701->gpio_nreset)) - gpio_set_value_cansleep(adau1701->gpio_nreset, 0); + if (adau1701->gpio_nreset) + gpiod_set_value_cansleep(adau1701->gpio_nreset, 0); regulator_bulk_disable(ARRAY_SIZE(adau1701->supplies), adau1701->supplies); } @@ -788,8 +790,6 @@ static int adau1701_i2c_probe(struct i2c_client *client, { struct adau1701 *adau1701; struct device *dev = &client->dev; - int gpio_nreset = -EINVAL; - int gpio_pll_mode[2] = { -EINVAL, -EINVAL }; int ret, i; adau1701 = devm_kzalloc(dev, sizeof(*adau1701), GFP_KERNEL); @@ -823,26 +823,6 @@ static int adau1701_i2c_probe(struct i2c_client *client, if (dev->of_node) { - gpio_nreset = of_get_named_gpio(dev->of_node, "reset-gpio", 0); - if (gpio_nreset < 0 && gpio_nreset != -ENOENT) { - ret = gpio_nreset; - goto exit_regulators_disable; - } - - gpio_pll_mode[0] = of_get_named_gpio(dev->of_node, - "adi,pll-mode-gpios", 0); - if (gpio_pll_mode[0] < 0 && gpio_pll_mode[0] != -ENOENT) { - ret = gpio_pll_mode[0]; - goto exit_regulators_disable; - } - - gpio_pll_mode[1] = of_get_named_gpio(dev->of_node, - "adi,pll-mode-gpios", 1); - if (gpio_pll_mode[1] < 0 && gpio_pll_mode[1] != -ENOENT) { - ret = gpio_pll_mode[1]; - goto exit_regulators_disable; - } - of_property_read_u32(dev->of_node, "adi,pll-clkdiv", &adau1701->pll_clkdiv); @@ -851,32 +831,20 @@ static int adau1701_i2c_probe(struct i2c_client *client, ARRAY_SIZE(adau1701->pin_config)); } - if (gpio_is_valid(gpio_nreset)) { - ret = devm_gpio_request_one(dev, gpio_nreset, GPIOF_OUT_INIT_LOW, - "ADAU1701 Reset"); - if (ret < 0) - goto exit_regulators_disable; + adau1701->gpio_nreset = devm_gpiod_get_optional(dev, "reset", GPIOD_IN); + + if (IS_ERR(adau1701->gpio_nreset)) { + ret = PTR_ERR(adau1701->gpio_nreset); + goto exit_regulators_disable; } - if (gpio_is_valid(gpio_pll_mode[0]) && - gpio_is_valid(gpio_pll_mode[1])) { - ret = devm_gpio_request_one(dev, gpio_pll_mode[0], - GPIOF_OUT_INIT_LOW, - "ADAU1701 PLL mode 0"); - if (ret < 0) - goto exit_regulators_disable; + adau1701->gpio_pll_mode = devm_gpiod_get_array_optional(dev, "adi,pll-mode", GPIOD_OUT_LOW); - ret = devm_gpio_request_one(dev, gpio_pll_mode[1], - GPIOF_OUT_INIT_LOW, - "ADAU1701 PLL mode 1"); - if (ret < 0) - goto exit_regulators_disable; + if (IS_ERR(adau1701->gpio_pll_mode)) { + ret = PTR_ERR(adau1701->gpio_pll_mode); + goto exit_regulators_disable; } - adau1701->gpio_nreset = gpio_nreset; - adau1701->gpio_pll_mode[0] = gpio_pll_mode[0]; - adau1701->gpio_pll_mode[1] = gpio_pll_mode[1]; - i2c_set_clientdata(client, adau1701); adau1701->sigmadsp = devm_sigmadsp_init_i2c(client, -- cgit v1.2.3 From 5b59289bfdbe287d0756e5ccadf039329147de67 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 2 Nov 2021 11:47:56 +0200 Subject: ASoC: SOF: core: Unregister machine driver before IPC and debugfs To ensure clean unload of the machine driver, components and topology, do the unregister before we free IPC and debugfs. It is a possibility that part of the unregister we would have IPC communication with the firmware. Suggested-by: Pierre-Louis Bossart Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20211102094756.9317-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/core.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 9ec9ef8ed525..40549cdd6d58 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -362,6 +362,13 @@ int snd_sof_device_remove(struct device *dev) if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) cancel_work_sync(&sdev->probe_work); + /* + * Unregister machine driver. This will unbind the snd_card which + * will remove the component driver and unload the topology + * before freeing the snd_card. + */ + snd_sof_machine_unregister(sdev, pdata); + if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) { snd_sof_free_trace(sdev); ret = snd_sof_dsp_power_down_notify(sdev); @@ -373,13 +380,6 @@ int snd_sof_device_remove(struct device *dev) snd_sof_free_debug(sdev); } - /* - * Unregister machine driver. This will unbind the snd_card which - * will remove the component driver and unload the topology - * before freeing the snd_card. - */ - snd_sof_machine_unregister(sdev, pdata); - /* * Unregistering the machine driver results in unloading the topology. * Some widgets, ex: scheduler, attempt to power down the core they are -- cgit v1.2.3 From 3c8a3ad4019126f06016ab0128dde11817502f52 Mon Sep 17 00:00:00 2001 From: Srinivasa Rao Mandadapu Date: Mon, 15 Nov 2021 12:41:28 +0530 Subject: ASoC: codecs: MBHC: Add support for special headset Update MBHC driver to support special headset such as apple and huwawei headsets. Signed-off-by: Srinivasa Rao Mandadapu Co-developed-by: Venkata Prasad Potturu Signed-off-by: Venkata Prasad Potturu Reviewed-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/1636960288-27537-1-git-send-email-srivasam@codeaurora.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd-mbhc-v2.c | 75 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 71 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c index b905eb8f3c67..934194b155d5 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.c +++ b/sound/soc/codecs/wcd-mbhc-v2.c @@ -1022,6 +1022,56 @@ static int wcd_mbhc_get_plug_from_adc(struct wcd_mbhc *mbhc, int adc_result) return plug_type; } +static int wcd_mbhc_get_spl_hs_thres(struct wcd_mbhc *mbhc) +{ + int hs_threshold, micbias_mv; + + micbias_mv = wcd_mbhc_get_micbias(mbhc); + if (mbhc->cfg->hs_thr && mbhc->cfg->micb_mv != WCD_MBHC_ADC_MICBIAS_MV) { + if (mbhc->cfg->micb_mv == micbias_mv) + hs_threshold = mbhc->cfg->hs_thr; + else + hs_threshold = (mbhc->cfg->hs_thr * micbias_mv) / mbhc->cfg->micb_mv; + } else { + hs_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV * micbias_mv) / + WCD_MBHC_ADC_MICBIAS_MV); + } + return hs_threshold; +} + +static bool wcd_mbhc_check_for_spl_headset(struct wcd_mbhc *mbhc) +{ + bool is_spl_hs = false; + int output_mv, hs_threshold, hph_threshold; + + if (!mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) + return false; + + /* Bump up MIC_BIAS2 to 2.7V */ + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->component, MIC_BIAS_2, true); + usleep_range(10000, 10100); + + output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P); + hs_threshold = wcd_mbhc_get_spl_hs_thres(mbhc); + hph_threshold = wcd_mbhc_adc_get_hph_thres(mbhc); + + if (output_mv > hs_threshold || output_mv < hph_threshold) { + if (mbhc->force_linein == true) + is_spl_hs = false; + } else { + is_spl_hs = true; + } + + /* Back MIC_BIAS2 to 1.8v if the type is not special headset */ + if (!is_spl_hs) { + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->component, MIC_BIAS_2, false); + /* Add 10ms delay for micbias to settle */ + usleep_range(10000, 10100); + } + + return is_spl_hs; +} + static void wcd_correct_swch_plug(struct work_struct *work) { struct wcd_mbhc *mbhc; @@ -1029,12 +1079,14 @@ static void wcd_correct_swch_plug(struct work_struct *work) enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_INVALID; unsigned long timeout; int pt_gnd_mic_swap_cnt = 0; - int output_mv, cross_conn, hs_threshold, try = 0; + int output_mv, cross_conn, hs_threshold, try = 0, micbias_mv; + bool is_spl_hs = false; bool is_pa_on; mbhc = container_of(work, struct wcd_mbhc, correct_plug_swch); component = mbhc->component; + micbias_mv = wcd_mbhc_get_micbias(mbhc); hs_threshold = wcd_mbhc_adc_get_hs_thres(mbhc); /* Mask ADC COMPLETE interrupt */ @@ -1097,6 +1149,16 @@ correct_plug_type: plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv); is_pa_on = wcd_mbhc_read_field(mbhc, WCD_MBHC_HPH_PA_EN); + if ((output_mv > hs_threshold) && (!is_spl_hs)) { + is_spl_hs = wcd_mbhc_check_for_spl_headset(mbhc); + output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P); + + if (is_spl_hs) { + hs_threshold = (hs_threshold * wcd_mbhc_get_micbias(mbhc)) / + micbias_mv; + } + } + if ((output_mv <= hs_threshold) && !is_pa_on) { /* Check for cross connection*/ cross_conn = wcd_check_cross_conn(mbhc); @@ -1122,14 +1184,19 @@ correct_plug_type: } } - if (output_mv > hs_threshold) /* cable is extension cable */ + /* cable is extension cable */ + if (output_mv > hs_threshold || mbhc->force_linein == true) plug_type = MBHC_PLUG_TYPE_HIGH_HPH; } wcd_mbhc_bcs_enable(mbhc, plug_type, true); - if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH) - wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_ISRC_EN, 1); + if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH) { + if (is_spl_hs) + plug_type = MBHC_PLUG_TYPE_HEADSET; + else + wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_ISRC_EN, 1); + } wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0); wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0); -- cgit v1.2.3 From 5ecc573d0c542c0f95497ba4586a6226814e4e18 Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Thu, 28 Oct 2021 14:46:38 +0200 Subject: ASoC: wm8903: Convert txt bindings to yaml Convert the Wolfson WM8903 Ultra-Low Power Stereo CODEC Device Tree binding documentation to json-schema. Signed-off-by: David Heidelberg Acked-by: Charles Keepax Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20211028124639.38420-1-david@ixit.cz Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/wlf,wm8903.yaml | 116 +++++++++++++++++++++ Documentation/devicetree/bindings/sound/wm8903.txt | 82 --------------- 2 files changed, 116 insertions(+), 82 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/wlf,wm8903.yaml delete mode 100644 Documentation/devicetree/bindings/sound/wm8903.txt diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8903.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8903.yaml new file mode 100644 index 000000000000..7105ed5fd6c7 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/wlf,wm8903.yaml @@ -0,0 +1,116 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/sound/wlf,wm8903.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: WM8903 audio codec + +description: | + This device supports I2C only. + Pins on the device (for linking into audio routes): + * IN1L + * IN1R + * IN2L + * IN2R + * IN3L + * IN3R + * DMICDAT + * HPOUTL + * HPOUTR + * LINEOUTL + * LINEOUTR + * LOP + * LON + * ROP + * RON + * MICBIAS + +maintainers: + - patches@opensource.cirrus.com + +properties: + compatible: + const: wlf,wm8903 + + reg: + maxItems: 1 + + gpio-controller: true + '#gpio-cells': + const: 2 + + interrupts: + maxItems: 1 + + micdet-cfg: + $ref: /schemas/types.yaml#/definitions/uint32 + default: 0 + description: Default register value for R6 (Mic Bias). + + micdet-delay: + $ref: /schemas/types.yaml#/definitions/uint32 + default: 100 + description: The debounce delay for microphone detection in mS. + + gpio-cfg: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: | + minItems: 5 + maxItems: 5 + A list of GPIO configuration register values. + If absent, no configuration of these registers is performed. + If any entry has the value 0xffffffff, that GPIO's + configuration will not be modified. + + AVDD-supply: + description: Analog power supply regulator on the AVDD pin. + + CPVDD-supply: + description: Charge pump supply regulator on the CPVDD pin. + + DBVDD-supply: + description: Digital buffer supply regulator for the DBVDD pin. + + DCVDD-supply: + description: Digital core supply regulator for the DCVDD pin. + + +required: + - compatible + - reg + - gpio-controller + - '#gpio-cells' + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + wm8903: codec@1a { + compatible = "wlf,wm8903"; + reg = <0x1a>; + interrupts = <347>; + + AVDD-supply = <&fooreg_a>; + CPVDD-supply = <&fooreg_b>; + DBVDD-supply = <&fooreg_c>; + DCVDD-supply = <&fooreg_d>; + + gpio-controller; + #gpio-cells = <2>; + + micdet-cfg = <0>; + micdet-delay = <100>; + gpio-cfg = < + 0x0600 /* DMIC_LR, output */ + 0x0680 /* DMIC_DAT, input */ + 0x0000 /* GPIO, output, low */ + 0x0200 /* Interrupt, output */ + 0x01a0 /* BCLK, input, active high */ + >; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/wm8903.txt b/Documentation/devicetree/bindings/sound/wm8903.txt deleted file mode 100644 index 6371c2434afe..000000000000 --- a/Documentation/devicetree/bindings/sound/wm8903.txt +++ /dev/null @@ -1,82 +0,0 @@ -WM8903 audio CODEC - -This device supports I2C only. - -Required properties: - - - compatible : "wlf,wm8903" - - - reg : the I2C address of the device. - - - gpio-controller : Indicates this device is a GPIO controller. - - - #gpio-cells : Should be two. The first cell is the pin number and the - second cell is used to specify optional parameters (currently unused). - -Optional properties: - - - interrupts : The interrupt line the codec is connected to. - - - micdet-cfg : Default register value for R6 (Mic Bias). If absent, the - default is 0. - - - micdet-delay : The debounce delay for microphone detection in mS. If - absent, the default is 100. - - - gpio-cfg : A list of GPIO configuration register values. The list must - be 5 entries long. If absent, no configuration of these registers is - performed. If any entry has the value 0xffffffff, that GPIO's - configuration will not be modified. - - - AVDD-supply : Analog power supply regulator on the AVDD pin. - - - CPVDD-supply : Charge pump supply regulator on the CPVDD pin. - - - DBVDD-supply : Digital buffer supply regulator for the DBVDD pin. - - - DCVDD-supply : Digital core supply regulator for the DCVDD pin. - -Pins on the device (for linking into audio routes): - - * IN1L - * IN1R - * IN2L - * IN2R - * IN3L - * IN3R - * DMICDAT - * HPOUTL - * HPOUTR - * LINEOUTL - * LINEOUTR - * LOP - * LON - * ROP - * RON - * MICBIAS - -Example: - -wm8903: codec@1a { - compatible = "wlf,wm8903"; - reg = <0x1a>; - interrupts = < 347 >; - - AVDD-supply = <&fooreg_a>; - CPVDD-supply = <&fooreg_b>; - DBVDD-supply = <&fooreg_c>; - DCVDC-supply = <&fooreg_d>; - - gpio-controller; - #gpio-cells = <2>; - - micdet-cfg = <0>; - micdet-delay = <100>; - gpio-cfg = < - 0x0600 /* DMIC_LR, output */ - 0x0680 /* DMIC_DAT, input */ - 0x0000 /* GPIO, output, low */ - 0x0200 /* Interrupt, output */ - 0x01a0 /* BCLK, input, active high */ - >; -}; -- cgit v1.2.3 From fd23116d7b8dffa05f42a857eee6ee9cce238d24 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 16 Nov 2021 07:54:13 +0100 Subject: ALSA: usb-audio: Use int for dB map values The values in usbmix_dB_map should be rather signed while we're using u32. As the copied target (usb_mixer_elem_info.dBmin and dBmax) is int, let's make them also int. Link: https://lore.kernel.org/r/20211116065415.11159-2-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/mixer_maps.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c index 55eea90ee993..92c06b1bb979 100644 --- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -6,8 +6,8 @@ */ struct usbmix_dB_map { - u32 min; - u32 max; + int min; + int max; }; struct usbmix_name_map { -- cgit v1.2.3 From 85b741c1cb6854478fd1aa13ac231e2c1baf4c4b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 16 Nov 2021 07:54:14 +0100 Subject: ALSA: usb-audio: Add minimal-mute notion in dB mapping table Some devices do mute the volume at the minimal volume, and for such devices, we need to set SNDRV_CTL_TLVT_DB_MINMAX_MUTE to the TLV information. It corresponds to setting usb_mixer_elem_info.min_mute flag in the USB-audio driver. This patch adds a new field min_mute in usbmix_dB_map so that the mixer map entry can pass the flag. Link: https://lore.kernel.org/r/20211116065415.11159-3-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 1 + sound/usb/mixer_maps.c | 1 + 2 files changed, 2 insertions(+) diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 6e7bac8203ba..5b9fd07ce2a2 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -145,6 +145,7 @@ static inline void check_mapped_dB(const struct usbmix_name_map *p, if (p && p->dB) { cval->dBmin = p->dB->min; cval->dBmax = p->dB->max; + cval->min_mute = p->dB->min_mute; cval->initialized = 1; } } diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c index 92c06b1bb979..9d71c569b148 100644 --- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -8,6 +8,7 @@ struct usbmix_dB_map { int min; int max; + bool min_mute; }; struct usbmix_name_map { -- cgit v1.2.3 From 02eb1d098e26f34c8f047b0b1cee6f4433a34bd1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 16 Nov 2021 07:54:15 +0100 Subject: ALSA: usb-audio: Fix dB level of Bose Revolve+ SoundLink Bose Revolve+ SoundLink (0a57:40fa) advertises invalid dB level for the speaker volume. This patch provides the correction in the mixer map quirk table entry. Note that this requires the prerequisite change to add min_mute flag to the dB map table. BugLink: https://bugzilla.suse.com/show_bug.cgi?id=1192375 Link: https://lore.kernel.org/r/20211116065415.11159-4-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/mixer_maps.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c index 9d71c569b148..5d391f62351b 100644 --- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -337,6 +337,13 @@ static const struct usbmix_name_map bose_companion5_map[] = { { 0 } /* terminator */ }; +/* Bose Revolve+ SoundLink, correction of dB maps */ +static const struct usbmix_dB_map bose_soundlink_dB = {-8283, -0, true}; +static const struct usbmix_name_map bose_soundlink_map[] = { + { 2, NULL, .dB = &bose_soundlink_dB }, + { 0 } /* terminator */ +}; + /* Sennheiser Communications Headset [PC 8], the dB value is reported as -6 negative maximum */ static const struct usbmix_dB_map sennheiser_pc8_dB = {-9500, 0}; static const struct usbmix_name_map sennheiser_pc8_map[] = { @@ -522,6 +529,11 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = { .id = USB_ID(0x05a7, 0x1020), .map = bose_companion5_map, }, + { + /* Bose Revolve+ SoundLink */ + .id = USB_ID(0x05a7, 0x40fa), + .map = bose_soundlink_map, + }, { /* Corsair Virtuoso SE (wired mode) */ .id = USB_ID(0x1b1c, 0x0a3d), -- cgit v1.2.3 From 06764dc931848c3a9bc01a63bbf76a605408bb54 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 16 Nov 2021 08:13:12 +0100 Subject: ALSA: jack: Add missing rwsem around snd_ctl_remove() calls snd_ctl_remove() has to be called with card->controls_rwsem held (when called after the card instantiation). This patch add the missing rwsem calls around it. Fixes: 9058cbe1eed2 ("ALSA: jack: implement kctl creating for jack devices") Link: https://lore.kernel.org/r/20211116071314.15065-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/core/jack.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/core/jack.c b/sound/core/jack.c index 32350c6aba84..f50a1e920e1d 100644 --- a/sound/core/jack.c +++ b/sound/core/jack.c @@ -62,10 +62,13 @@ static int snd_jack_dev_free(struct snd_device *device) struct snd_card *card = device->card; struct snd_jack_kctl *jack_kctl, *tmp_jack_kctl; + down_write(&card->controls_rwsem); list_for_each_entry_safe(jack_kctl, tmp_jack_kctl, &jack->kctl_list, list) { list_del_init(&jack_kctl->list); snd_ctl_remove(card, jack_kctl->kctl); } + up_write(&card->controls_rwsem); + if (jack->private_free) jack->private_free(jack); -- cgit v1.2.3 From 5471e9762e1af4b7df057a96bfd46cc250979b88 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 16 Nov 2021 08:13:13 +0100 Subject: ALSA: PCM: Add missing rwsem around snd_ctl_remove() calls snd_ctl_remove() has to be called with card->controls_rwsem held (when called after the card instantiation). This patch add the missing rwsem calls around it. Fixes: a8ff48cb7083 ("ALSA: pcm: Free chmap at PCM free callback, too") Link: https://lore.kernel.org/r/20211116071314.15065-2-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/core/pcm.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 6fd3677685d7..ba4a987ed1c6 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -810,7 +810,11 @@ EXPORT_SYMBOL(snd_pcm_new_internal); static void free_chmap(struct snd_pcm_str *pstr) { if (pstr->chmap_kctl) { - snd_ctl_remove(pstr->pcm->card, pstr->chmap_kctl); + struct snd_card *card = pstr->pcm->card; + + down_write(&card->controls_rwsem); + snd_ctl_remove(card, pstr->chmap_kctl); + up_write(&card->controls_rwsem); pstr->chmap_kctl = NULL; } } -- cgit v1.2.3 From 80bd64af75b4bb11c0329bc66c35da2ddfb66d88 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 16 Nov 2021 08:13:14 +0100 Subject: ALSA: hda: Add missing rwsem around snd_ctl_remove() calls snd_ctl_remove() has to be called with card->controls_rwsem held (when called after the card instantiation). This patch add the missing rwsem calls around it. Fixes: d13bd412dce2 ("ALSA: hda - Manage kcontrol lists") Link: https://lore.kernel.org/r/20211116071314.15065-3-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 0c4a337c9fc0..eda70814369b 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1727,8 +1727,11 @@ void snd_hda_ctls_clear(struct hda_codec *codec) { int i; struct hda_nid_item *items = codec->mixers.list; + + down_write(&codec->card->controls_rwsem); for (i = 0; i < codec->mixers.used; i++) snd_ctl_remove(codec->card, items[i].kctl); + up_write(&codec->card->controls_rwsem); snd_array_free(&codec->mixers); snd_array_free(&codec->nids); } -- cgit v1.2.3 From 7206998f578d5553989bc01ea2e544b622e79539 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 16 Nov 2021 08:24:59 +0100 Subject: ALSA: hda: Fix potential deadlock at codec unbinding When a codec is unbound dynamically via sysfs while its stream is in use, we may face a potential deadlock at the proc remove or a UAF. This happens since the hda_pcm is managed by a linked list, as it handles the hda_pcm object release via kref. When a PCM is opened at the unbinding time, the release of hda_pcm gets delayed and it ends up with the close of the PCM stream releasing the associated hda_pcm object of its own. The hda_pcm destructor contains the PCM device release that includes the removal of procfs entries. And, this removal has the sync of the close of all in-use files -- which would never finish because it's called from the PCM file descriptor itself, i.e. it's trying to shoot its foot. For addressing the deadlock above, this patch changes the way to manage and release the hda_pcm object. The kref of hda_pcm is dropped, and instead a simple refcount is introduced in hda_codec for keeping the track of the active PCM streams, and at each PCM open and close, this refcount is adjusted accordingly. At unbinding, the driver calls snd_device_disconnect() for each PCM stream, then synchronizes with the refcount finish, and finally releases the object resources. Fixes: bbbc7e8502c9 ("ALSA: hda - Allocate hda_pcm objects dynamically") Link: https://lore.kernel.org/r/20211116072459.18930-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- include/sound/hda_codec.h | 8 +++++--- sound/pci/hda/hda_bind.c | 5 +++++ sound/pci/hda/hda_codec.c | 42 ++++++++++++++++++++++++++---------------- sound/pci/hda/hda_local.h | 1 + 4 files changed, 37 insertions(+), 19 deletions(-) diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h index 0e45963bb767..82d9daa17851 100644 --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -8,7 +8,7 @@ #ifndef __SOUND_HDA_CODEC_H #define __SOUND_HDA_CODEC_H -#include +#include #include #include #include @@ -166,8 +166,8 @@ struct hda_pcm { bool own_chmap; /* codec driver provides own channel maps */ /* private: */ struct hda_codec *codec; - struct kref kref; struct list_head list; + unsigned int disconnected:1; }; /* codec information */ @@ -187,6 +187,8 @@ struct hda_codec { /* PCM to create, set by patch_ops.build_pcms callback */ struct list_head pcm_list_head; + refcount_t pcm_ref; + wait_queue_head_t remove_sleep; /* codec specific info */ void *spec; @@ -420,7 +422,7 @@ void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec); static inline void snd_hda_codec_pcm_get(struct hda_pcm *pcm) { - kref_get(&pcm->kref); + refcount_inc(&pcm->codec->pcm_ref); } void snd_hda_codec_pcm_put(struct hda_pcm *pcm); diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c index 1c8bffc3eec6..7153bd53e189 100644 --- a/sound/pci/hda/hda_bind.c +++ b/sound/pci/hda/hda_bind.c @@ -156,6 +156,11 @@ static int hda_codec_driver_remove(struct device *dev) return codec->bus->core.ext_ops->hdev_detach(&codec->core); } + refcount_dec(&codec->pcm_ref); + snd_hda_codec_disconnect_pcms(codec); + wait_event(codec->remove_sleep, !refcount_read(&codec->pcm_ref)); + snd_power_sync_ref(codec->bus->card); + if (codec->patch_ops.free) codec->patch_ops.free(codec); snd_hda_codec_cleanup_for_unbind(codec); diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index eda70814369b..7016b48227bf 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -703,20 +703,10 @@ get_hda_cvt_setup(struct hda_codec *codec, hda_nid_t nid) /* * PCM device */ -static void release_pcm(struct kref *kref) -{ - struct hda_pcm *pcm = container_of(kref, struct hda_pcm, kref); - - if (pcm->pcm) - snd_device_free(pcm->codec->card, pcm->pcm); - clear_bit(pcm->device, pcm->codec->bus->pcm_dev_bits); - kfree(pcm->name); - kfree(pcm); -} - void snd_hda_codec_pcm_put(struct hda_pcm *pcm) { - kref_put(&pcm->kref, release_pcm); + if (refcount_dec_and_test(&pcm->codec->pcm_ref)) + wake_up(&pcm->codec->remove_sleep); } EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_put); @@ -731,7 +721,6 @@ struct hda_pcm *snd_hda_codec_pcm_new(struct hda_codec *codec, return NULL; pcm->codec = codec; - kref_init(&pcm->kref); va_start(args, fmt); pcm->name = kvasprintf(GFP_KERNEL, fmt, args); va_end(args); @@ -741,6 +730,7 @@ struct hda_pcm *snd_hda_codec_pcm_new(struct hda_codec *codec, } list_add_tail(&pcm->list, &codec->pcm_list_head); + refcount_inc(&codec->pcm_ref); return pcm; } EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_new); @@ -748,15 +738,31 @@ EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_new); /* * codec destructor */ +void snd_hda_codec_disconnect_pcms(struct hda_codec *codec) +{ + struct hda_pcm *pcm; + + list_for_each_entry(pcm, &codec->pcm_list_head, list) { + if (pcm->disconnected) + continue; + if (pcm->pcm) + snd_device_disconnect(codec->card, pcm->pcm); + snd_hda_codec_pcm_put(pcm); + pcm->disconnected = 1; + } +} + static void codec_release_pcms(struct hda_codec *codec) { struct hda_pcm *pcm, *n; list_for_each_entry_safe(pcm, n, &codec->pcm_list_head, list) { - list_del_init(&pcm->list); + list_del(&pcm->list); if (pcm->pcm) - snd_device_disconnect(codec->card, pcm->pcm); - snd_hda_codec_pcm_put(pcm); + snd_device_free(pcm->codec->card, pcm->pcm); + clear_bit(pcm->device, pcm->codec->bus->pcm_dev_bits); + kfree(pcm->name); + kfree(pcm); } } @@ -769,6 +775,7 @@ void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec) codec->registered = 0; } + snd_hda_codec_disconnect_pcms(codec); cancel_delayed_work_sync(&codec->jackpoll_work); if (!codec->in_freeing) snd_hda_ctls_clear(codec); @@ -792,6 +799,7 @@ void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec) remove_conn_list(codec); snd_hdac_regmap_exit(&codec->core); codec->configured = 0; + refcount_set(&codec->pcm_ref, 1); /* reset refcount */ } EXPORT_SYMBOL_GPL(snd_hda_codec_cleanup_for_unbind); @@ -958,6 +966,8 @@ int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card, snd_array_init(&codec->verbs, sizeof(struct hda_verb *), 8); INIT_LIST_HEAD(&codec->conn_list); INIT_LIST_HEAD(&codec->pcm_list_head); + refcount_set(&codec->pcm_ref, 1); + init_waitqueue_head(&codec->remove_sleep); INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work); codec->depop_delay = -1; diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index ea8ab8b43337..4662a47add7e 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -137,6 +137,7 @@ int __snd_hda_add_vmaster(struct hda_codec *codec, char *name, int snd_hda_codec_reset(struct hda_codec *codec); void snd_hda_codec_register(struct hda_codec *codec); void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec); +void snd_hda_codec_disconnect_pcms(struct hda_codec *codec); #define snd_hda_regmap_sync(codec) snd_hdac_regmap_sync(&(codec)->core) -- cgit v1.2.3 From 2c95b92ecd92e784785b1db8cccc4f0f2bfa850c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 16 Nov 2021 08:33:58 +0100 Subject: ALSA: memalloc: Unify x86 SG-buffer handling (take#3) This is a second attempt to unify the x86-specific SG-buffer handling code with the new standard non-contiguous page handler. The first try (in commit 2d9ea39917a4) failed due to the wrong page and address calculations, hence reverted. (And the second try failed due to a copy&paste error.) Now it's corrected with the previous fix for noncontig pages, and the proper sg page iteration by this patch. After the migration, SNDRV_DMA_TYPE_DMA_SG becomes identical with SNDRV_DMA_TYPE_NONCONTIG on x86, while others still fall back to SNDRV_DMA_TYPE_DEV. Tested-by: Alex Xu (Hello71) Tested-by: Harald Arnesen Link: https://lore.kernel.org/r/20211017074859.24112-4-tiwai@suse.de Link: https://lore.kernel.org/r/20211109062235.22310-1-tiwai@suse.de Link: https://lore.kernel.org/r/20211116073358.19741-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- include/sound/memalloc.h | 14 ++-- sound/core/Makefile | 1 - sound/core/memalloc.c | 53 ++++++++++++- sound/core/sgbuf.c | 201 ----------------------------------------------- 4 files changed, 56 insertions(+), 213 deletions(-) delete mode 100644 sound/core/sgbuf.c diff --git a/include/sound/memalloc.h b/include/sound/memalloc.h index 1051b84e8579..653dfffb3ac8 100644 --- a/include/sound/memalloc.h +++ b/include/sound/memalloc.h @@ -36,13 +36,6 @@ struct snd_dma_device { #define SNDRV_DMA_TYPE_CONTINUOUS 1 /* continuous no-DMA memory */ #define SNDRV_DMA_TYPE_DEV 2 /* generic device continuous */ #define SNDRV_DMA_TYPE_DEV_WC 5 /* continuous write-combined */ -#ifdef CONFIG_SND_DMA_SGBUF -#define SNDRV_DMA_TYPE_DEV_SG 3 /* generic device SG-buffer */ -#define SNDRV_DMA_TYPE_DEV_WC_SG 6 /* SG write-combined */ -#else -#define SNDRV_DMA_TYPE_DEV_SG SNDRV_DMA_TYPE_DEV /* no SG-buf support */ -#define SNDRV_DMA_TYPE_DEV_WC_SG SNDRV_DMA_TYPE_DEV_WC -#endif #ifdef CONFIG_GENERIC_ALLOCATOR #define SNDRV_DMA_TYPE_DEV_IRAM 4 /* generic device iram-buffer */ #else @@ -51,6 +44,13 @@ struct snd_dma_device { #define SNDRV_DMA_TYPE_VMALLOC 7 /* vmalloc'ed buffer */ #define SNDRV_DMA_TYPE_NONCONTIG 8 /* non-coherent SG buffer */ #define SNDRV_DMA_TYPE_NONCOHERENT 9 /* non-coherent buffer */ +#ifdef CONFIG_SND_DMA_SGBUF +#define SNDRV_DMA_TYPE_DEV_SG SNDRV_DMA_TYPE_NONCONTIG +#define SNDRV_DMA_TYPE_DEV_WC_SG 6 /* SG write-combined */ +#else +#define SNDRV_DMA_TYPE_DEV_SG SNDRV_DMA_TYPE_DEV /* no SG-buf support */ +#define SNDRV_DMA_TYPE_DEV_WC_SG SNDRV_DMA_TYPE_DEV_WC +#endif /* * info for buffer allocation diff --git a/sound/core/Makefile b/sound/core/Makefile index 79e1407cd0de..350d704ced98 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile @@ -19,7 +19,6 @@ snd-$(CONFIG_SND_JACK) += ctljack.o jack.o snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_misc.o \ pcm_memory.o memalloc.o snd-pcm-$(CONFIG_SND_PCM_TIMER) += pcm_timer.o -snd-pcm-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o snd-pcm-$(CONFIG_SND_PCM_ELD) += pcm_drm_eld.o snd-pcm-$(CONFIG_SND_PCM_IEC958) += pcm_iec958.o diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index 9fc971a704a9..d1fcd1d5adae 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -620,6 +620,52 @@ static const struct snd_malloc_ops snd_dma_noncontig_ops = { .get_chunk_size = snd_dma_noncontig_get_chunk_size, }; +/* x86-specific SG-buffer with WC pages */ +#ifdef CONFIG_SND_DMA_SGBUF +#define sg_wc_address(it) ((unsigned long)page_address(sg_page_iter_page(it))) + +static void *snd_dma_sg_wc_alloc(struct snd_dma_buffer *dmab, size_t size) +{ + void *p = snd_dma_noncontig_alloc(dmab, size); + struct sg_table *sgt = dmab->private_data; + struct sg_page_iter iter; + + if (!p) + return NULL; + for_each_sgtable_page(sgt, &iter, 0) + set_memory_wc(sg_wc_address(&iter), 1); + return p; +} + +static void snd_dma_sg_wc_free(struct snd_dma_buffer *dmab) +{ + struct sg_table *sgt = dmab->private_data; + struct sg_page_iter iter; + + for_each_sgtable_page(sgt, &iter, 0) + set_memory_wb(sg_wc_address(&iter), 1); + snd_dma_noncontig_free(dmab); +} + +static int snd_dma_sg_wc_mmap(struct snd_dma_buffer *dmab, + struct vm_area_struct *area) +{ + area->vm_page_prot = pgprot_writecombine(area->vm_page_prot); + return dma_mmap_noncontiguous(dmab->dev.dev, area, + dmab->bytes, dmab->private_data); +} + +static const struct snd_malloc_ops snd_dma_sg_wc_ops = { + .alloc = snd_dma_sg_wc_alloc, + .free = snd_dma_sg_wc_free, + .mmap = snd_dma_sg_wc_mmap, + .sync = snd_dma_noncontig_sync, + .get_addr = snd_dma_noncontig_get_addr, + .get_page = snd_dma_noncontig_get_page, + .get_chunk_size = snd_dma_noncontig_get_chunk_size, +}; +#endif /* CONFIG_SND_DMA_SGBUF */ + /* * Non-coherent pages allocator */ @@ -679,14 +725,13 @@ static const struct snd_malloc_ops *dma_ops[] = { [SNDRV_DMA_TYPE_DEV_WC] = &snd_dma_wc_ops, [SNDRV_DMA_TYPE_NONCONTIG] = &snd_dma_noncontig_ops, [SNDRV_DMA_TYPE_NONCOHERENT] = &snd_dma_noncoherent_ops, +#ifdef CONFIG_SND_DMA_SGBUF + [SNDRV_DMA_TYPE_DEV_WC_SG] = &snd_dma_sg_wc_ops, +#endif #ifdef CONFIG_GENERIC_ALLOCATOR [SNDRV_DMA_TYPE_DEV_IRAM] = &snd_dma_iram_ops, #endif /* CONFIG_GENERIC_ALLOCATOR */ #endif /* CONFIG_HAS_DMA */ -#ifdef CONFIG_SND_DMA_SGBUF - [SNDRV_DMA_TYPE_DEV_SG] = &snd_dma_sg_ops, - [SNDRV_DMA_TYPE_DEV_WC_SG] = &snd_dma_sg_ops, -#endif }; static const struct snd_malloc_ops *snd_dma_get_ops(struct snd_dma_buffer *dmab) diff --git a/sound/core/sgbuf.c b/sound/core/sgbuf.c deleted file mode 100644 index 8352a5cdb19f..000000000000 --- a/sound/core/sgbuf.c +++ /dev/null @@ -1,201 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Scatter-Gather buffer - * - * Copyright (c) by Takashi Iwai - */ - -#include -#include -#include -#include -#include -#include "memalloc_local.h" - -struct snd_sg_page { - void *buf; - dma_addr_t addr; -}; - -struct snd_sg_buf { - int size; /* allocated byte size */ - int pages; /* allocated pages */ - int tblsize; /* allocated table size */ - struct snd_sg_page *table; /* address table */ - struct page **page_table; /* page table (for vmap/vunmap) */ - struct device *dev; -}; - -/* table entries are align to 32 */ -#define SGBUF_TBL_ALIGN 32 -#define sgbuf_align_table(tbl) ALIGN((tbl), SGBUF_TBL_ALIGN) - -static void snd_dma_sg_free(struct snd_dma_buffer *dmab) -{ - struct snd_sg_buf *sgbuf = dmab->private_data; - struct snd_dma_buffer tmpb; - int i; - - if (!sgbuf) - return; - - vunmap(dmab->area); - dmab->area = NULL; - - tmpb.dev.type = SNDRV_DMA_TYPE_DEV; - if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG) - tmpb.dev.type = SNDRV_DMA_TYPE_DEV_WC; - tmpb.dev.dev = sgbuf->dev; - for (i = 0; i < sgbuf->pages; i++) { - if (!(sgbuf->table[i].addr & ~PAGE_MASK)) - continue; /* continuous pages */ - tmpb.area = sgbuf->table[i].buf; - tmpb.addr = sgbuf->table[i].addr & PAGE_MASK; - tmpb.bytes = (sgbuf->table[i].addr & ~PAGE_MASK) << PAGE_SHIFT; - snd_dma_free_pages(&tmpb); - } - - kfree(sgbuf->table); - kfree(sgbuf->page_table); - kfree(sgbuf); - dmab->private_data = NULL; -} - -#define MAX_ALLOC_PAGES 32 - -static void *snd_dma_sg_alloc(struct snd_dma_buffer *dmab, size_t size) -{ - struct snd_sg_buf *sgbuf; - unsigned int i, pages, chunk, maxpages; - struct snd_dma_buffer tmpb; - struct snd_sg_page *table; - struct page **pgtable; - int type = SNDRV_DMA_TYPE_DEV; - pgprot_t prot = PAGE_KERNEL; - void *area; - - dmab->private_data = sgbuf = kzalloc(sizeof(*sgbuf), GFP_KERNEL); - if (!sgbuf) - return NULL; - if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG) { - type = SNDRV_DMA_TYPE_DEV_WC; -#ifdef pgprot_noncached - prot = pgprot_noncached(PAGE_KERNEL); -#endif - } - sgbuf->dev = dmab->dev.dev; - pages = snd_sgbuf_aligned_pages(size); - sgbuf->tblsize = sgbuf_align_table(pages); - table = kcalloc(sgbuf->tblsize, sizeof(*table), GFP_KERNEL); - if (!table) - goto _failed; - sgbuf->table = table; - pgtable = kcalloc(sgbuf->tblsize, sizeof(*pgtable), GFP_KERNEL); - if (!pgtable) - goto _failed; - sgbuf->page_table = pgtable; - - /* allocate pages */ - maxpages = MAX_ALLOC_PAGES; - while (pages > 0) { - chunk = pages; - /* don't be too eager to take a huge chunk */ - if (chunk > maxpages) - chunk = maxpages; - chunk <<= PAGE_SHIFT; - if (snd_dma_alloc_pages_fallback(type, dmab->dev.dev, - chunk, &tmpb) < 0) { - if (!sgbuf->pages) - goto _failed; - size = sgbuf->pages * PAGE_SIZE; - break; - } - chunk = tmpb.bytes >> PAGE_SHIFT; - for (i = 0; i < chunk; i++) { - table->buf = tmpb.area; - table->addr = tmpb.addr; - if (!i) - table->addr |= chunk; /* mark head */ - table++; - *pgtable++ = virt_to_page(tmpb.area); - tmpb.area += PAGE_SIZE; - tmpb.addr += PAGE_SIZE; - } - sgbuf->pages += chunk; - pages -= chunk; - if (chunk < maxpages) - maxpages = chunk; - } - - sgbuf->size = size; - area = vmap(sgbuf->page_table, sgbuf->pages, VM_MAP, prot); - if (!area) - goto _failed; - return area; - - _failed: - snd_dma_sg_free(dmab); /* free the table */ - return NULL; -} - -static dma_addr_t snd_dma_sg_get_addr(struct snd_dma_buffer *dmab, - size_t offset) -{ - struct snd_sg_buf *sgbuf = dmab->private_data; - dma_addr_t addr; - - addr = sgbuf->table[offset >> PAGE_SHIFT].addr; - addr &= ~((dma_addr_t)PAGE_SIZE - 1); - return addr + offset % PAGE_SIZE; -} - -static struct page *snd_dma_sg_get_page(struct snd_dma_buffer *dmab, - size_t offset) -{ - struct snd_sg_buf *sgbuf = dmab->private_data; - unsigned int idx = offset >> PAGE_SHIFT; - - if (idx >= (unsigned int)sgbuf->pages) - return NULL; - return sgbuf->page_table[idx]; -} - -static unsigned int snd_dma_sg_get_chunk_size(struct snd_dma_buffer *dmab, - unsigned int ofs, - unsigned int size) -{ - struct snd_sg_buf *sg = dmab->private_data; - unsigned int start, end, pg; - - start = ofs >> PAGE_SHIFT; - end = (ofs + size - 1) >> PAGE_SHIFT; - /* check page continuity */ - pg = sg->table[start].addr >> PAGE_SHIFT; - for (;;) { - start++; - if (start > end) - break; - pg++; - if ((sg->table[start].addr >> PAGE_SHIFT) != pg) - return (start << PAGE_SHIFT) - ofs; - } - /* ok, all on continuous pages */ - return size; -} - -static int snd_dma_sg_mmap(struct snd_dma_buffer *dmab, - struct vm_area_struct *area) -{ - if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG) - area->vm_page_prot = pgprot_writecombine(area->vm_page_prot); - return -ENOENT; /* continue with the default mmap handler */ -} - -const struct snd_malloc_ops snd_dma_sg_ops = { - .alloc = snd_dma_sg_alloc, - .free = snd_dma_sg_free, - .get_addr = snd_dma_sg_get_addr, - .get_page = snd_dma_sg_get_page, - .get_chunk_size = snd_dma_sg_get_chunk_size, - .mmap = snd_dma_sg_mmap, -}; -- cgit v1.2.3 From 5f55c9693a222ee1b8ec62a57fbcff59af0c4837 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 16 Nov 2021 11:50:21 +0000 Subject: ASoC: qcom: sdm845: only setup slim ports once Currently same slim channel map setup for every dai link, which is redundant. Fix this by adding a flag and conditionally setting these channel maps. Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20211116115021.14213-1-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/sdm845.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c index 0adfc5708949..10d724bd1d67 100644 --- a/sound/soc/qcom/sdm845.c +++ b/sound/soc/qcom/sdm845.c @@ -33,6 +33,7 @@ struct sdm845_snd_data { struct snd_soc_jack jack; bool jack_setup; + bool slim_port_setup; bool stream_prepared[AFE_PORT_MAX]; struct snd_soc_card *card; uint32_t pri_mi2s_clk_count; @@ -224,6 +225,7 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct sdm845_snd_data *pdata = snd_soc_card_get_drvdata(card); + struct snd_soc_dai_link *link = rtd->dai_link; struct snd_jack *jack; /* * Codec SLIMBUS configuration @@ -276,6 +278,10 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd) } break; case SLIMBUS_0_RX...SLIMBUS_6_TX: + /* setting up wcd multiple times for slim port is redundant */ + if (pdata->slim_port_setup || !link->no_pcm) + return 0; + for_each_rtd_codec_dais(rtd, i, codec_dai) { rval = snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch), @@ -295,8 +301,10 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd) dev_warn(card->dev, "Failed to set jack: %d\n", rval); return rval; } - } + + pdata->slim_port_setup = true; + break; default: break; -- cgit v1.2.3 From 7548a391c53cab2af0954d252cc5a9a793fd4c0e Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 16 Nov 2021 14:41:31 +0200 Subject: ASoC: SOF: i.MX: simplify Kconfig Follow the Intel example and simplify the Kconfig a) start from the end-product for 'select' chains b) use 'depends on' to filter out configurations. c) use snd-sof-of as a common module without any 'select' Signed-off-by: Pierre-Louis Bossart Reviewed-by: Daniel Baluta Signed-off-by: Daniel Baluta Link: https://lore.kernel.org/r/20211116124131.46414-1-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/Kconfig | 4 +++- sound/soc/sof/Makefile | 2 +- sound/soc/sof/imx/Kconfig | 46 +++++++++++++--------------------------------- 3 files changed, 17 insertions(+), 35 deletions(-) diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index 041c54639c4d..b6fa659179b6 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -40,12 +40,14 @@ config SND_SOC_SOF_ACPI_DEV config SND_SOC_SOF_OF tristate "SOF OF enumeration support" depends on OF || COMPILE_TEST - select SND_SOC_SOF help This adds support for Device Tree enumeration. This option is required to enable i.MX8 devices. Say Y if you need this option. If unsure select "N". +config SND_SOC_SOF_OF_DEV + tristate + config SND_SOC_SOF_COMPRESS bool select SND_SOC_COMPRESS diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index 06e5f49f7ee8..1dac5cb4dfd6 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -17,7 +17,7 @@ obj-$(CONFIG_SND_SOC_SOF_NOCODEC) += snd-sof-nocodec.o obj-$(CONFIG_SND_SOC_SOF_ACPI_DEV) += snd-sof-acpi.o -obj-$(CONFIG_SND_SOC_SOF_OF) += snd-sof-of.o +obj-$(CONFIG_SND_SOC_SOF_OF_DEV) += snd-sof-of.o obj-$(CONFIG_SND_SOC_SOF_PCI_DEV) += snd-sof-pci.o obj-$(CONFIG_SND_SOC_SOF_INTEL_TOPLEVEL) += intel/ diff --git a/sound/soc/sof/imx/Kconfig b/sound/soc/sof/imx/Kconfig index 34cf228c188f..9b8d5bb1e449 100644 --- a/sound/soc/sof/imx/Kconfig +++ b/sound/soc/sof/imx/Kconfig @@ -11,53 +11,33 @@ config SND_SOC_SOF_IMX_TOPLEVEL if SND_SOC_SOF_IMX_TOPLEVEL -config SND_SOC_SOF_IMX_OF - def_tristate SND_SOC_SOF_OF - select SND_SOC_SOF_IMX8 if SND_SOC_SOF_IMX8_SUPPORT - select SND_SOC_SOF_IMX8M if SND_SOC_SOF_IMX8M_SUPPORT - help - This option is not user-selectable but automagically handled by - 'select' statements at a higher level. - config SND_SOC_SOF_IMX_COMMON tristate + select SND_SOC_SOF_OF_DEV + select SND_SOC_SOF + select SND_SOC_SOF_XTENSA + select SND_SOC_SOF_COMPRESS help This option is not user-selectable but automagically handled by 'select' statements at a higher level. -config SND_SOC_SOF_IMX8_SUPPORT - bool "SOF support for i.MX8" - depends on IMX_SCU=y || IMX_SCU=SND_SOC_SOF_IMX_OF - depends on IMX_DSP=y || IMX_DSP=SND_SOC_SOF_IMX_OF +config SND_SOC_SOF_IMX8 + tristate "SOF support for i.MX8" + depends on IMX_SCU + depends on IMX_DSP + select SND_SOC_SOF_IMX_COMMON help This adds support for Sound Open Firmware for NXP i.MX8 platforms. Say Y if you have such a device. If unsure select "N". -config SND_SOC_SOF_IMX8 - tristate +config SND_SOC_SOF_IMX8M + tristate "SOF support for i.MX8M" + depends on IMX_DSP select SND_SOC_SOF_IMX_COMMON - select SND_SOC_SOF_XTENSA - select SND_SOC_SOF_COMPRESS - help - This option is not user-selectable but automagically handled by - 'select' statements at a higher level. - -config SND_SOC_SOF_IMX8M_SUPPORT - bool "SOF support for i.MX8M" - depends on IMX_DSP=y || IMX_DSP=SND_SOC_SOF_OF help This adds support for Sound Open Firmware for NXP i.MX8M platforms. Say Y if you have such a device. If unsure select "N". -config SND_SOC_SOF_IMX8M - tristate - select SND_SOC_SOF_IMX_COMMON - select SND_SOC_SOF_XTENSA - select SND_SOC_SOF_COMPRESS - help - This option is not user-selectable but automagically handled by - 'select' statements at a higher level. - -endif ## SND_SOC_SOF_IMX_IMX_TOPLEVEL +endif ## SND_SOC_SOF_IMX_TOPLEVEL -- cgit v1.2.3 From 976001b10fa4441917f216452e70fd8c5aeccd94 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 16 Nov 2021 16:38:58 +0000 Subject: ASoC: cs42l42: Remove redundant writes to DETECT_MODE There are multiple places where DETECT_MODE is included in a register write, but in every case it is written as 0. Removing these redundant writes makes the code less cluttered and also makes it obvious that DETECT_MODE is never changed. A single initialization to 0 is added to cs42l42_setup_hs_type_detect(). Signed-off-by: Richard Fitzgerald Link: https://lore.kernel.org/r/20211116163901.45390-2-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l42.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 56804a3f285e..92bdc3a355ff 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -1270,10 +1270,8 @@ static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42) /* Turn on level detect circuitry */ regmap_update_bits(cs42l42->regmap, CS42L42_MISC_DET_CTL, - CS42L42_DETECT_MODE_MASK | CS42L42_HSBIAS_CTL_MASK | CS42L42_PDN_MIC_LVL_DET_MASK, - (0 << CS42L42_DETECT_MODE_SHIFT) | (3 << CS42L42_HSBIAS_CTL_SHIFT) | (0 << CS42L42_PDN_MIC_LVL_DET_SHIFT)); @@ -1300,10 +1298,8 @@ static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42) /* Make sure button detect and HS bias circuits are off */ regmap_update_bits(cs42l42->regmap, CS42L42_MISC_DET_CTL, - CS42L42_DETECT_MODE_MASK | CS42L42_HSBIAS_CTL_MASK | CS42L42_PDN_MIC_LVL_DET_MASK, - (0 << CS42L42_DETECT_MODE_SHIFT) | (1 << CS42L42_HSBIAS_CTL_SHIFT) | (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT)); } @@ -1351,10 +1347,8 @@ static void cs42l42_init_hs_type_detect(struct cs42l42_private *cs42l42) /* Make sure button detect and HS bias circuits are off */ regmap_update_bits(cs42l42->regmap, CS42L42_MISC_DET_CTL, - CS42L42_DETECT_MODE_MASK | CS42L42_HSBIAS_CTL_MASK | CS42L42_PDN_MIC_LVL_DET_MASK, - (0 << CS42L42_DETECT_MODE_SHIFT) | (1 << CS42L42_HSBIAS_CTL_SHIFT) | (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT)); @@ -1398,10 +1392,8 @@ static void cs42l42_init_hs_type_detect(struct cs42l42_private *cs42l42) /* Power up HS bias to 2.7V */ regmap_update_bits(cs42l42->regmap, CS42L42_MISC_DET_CTL, - CS42L42_DETECT_MODE_MASK | CS42L42_HSBIAS_CTL_MASK | CS42L42_PDN_MIC_LVL_DET_MASK, - (0 << CS42L42_DETECT_MODE_SHIFT) | (3 << CS42L42_HSBIAS_CTL_SHIFT) | (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT)); @@ -1448,10 +1440,8 @@ static void cs42l42_cancel_hs_type_detect(struct cs42l42_private *cs42l42) /* Ground HS bias */ regmap_update_bits(cs42l42->regmap, CS42L42_MISC_DET_CTL, - CS42L42_DETECT_MODE_MASK | CS42L42_HSBIAS_CTL_MASK | CS42L42_PDN_MIC_LVL_DET_MASK, - (0 << CS42L42_DETECT_MODE_SHIFT) | (1 << CS42L42_HSBIAS_CTL_SHIFT) | (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT)); @@ -1829,6 +1819,9 @@ static void cs42l42_setup_hs_type_detect(struct cs42l42_private *cs42l42) cs42l42->hs_type = CS42L42_PLUG_INVALID; + regmap_update_bits(cs42l42->regmap, CS42L42_MISC_DET_CTL, + CS42L42_DETECT_MODE_MASK, 0); + /* Latch analog controls to VP power domain */ regmap_update_bits(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_LATCH_TO_VP_MASK | -- cgit v1.2.3 From f2dfbaaa5404cadf70213146a5b4b89b647d9092 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 16 Nov 2021 16:38:59 +0000 Subject: ASoC: cs42l42: Remove redundant writes to RS_PLUG/RS_UNPLUG masks The RS_PLUG and RS_UNPLUG interrupt masks are always written as 1 so those writes are redundant and can be deleted. This makes it completely clear in the code that only the TS_PLUG and TS_UNPLUG masks are being changed. Signed-off-by: Richard Fitzgerald Link: https://lore.kernel.org/r/20211116163901.45390-3-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l42.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 92bdc3a355ff..3674f73301dc 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -1320,12 +1320,8 @@ static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42) /* Unmask tip sense interrupts */ regmap_update_bits(cs42l42->regmap, CS42L42_TSRS_PLUG_INT_MASK, - CS42L42_RS_PLUG_MASK | - CS42L42_RS_UNPLUG_MASK | CS42L42_TS_PLUG_MASK | CS42L42_TS_UNPLUG_MASK, - (1 << CS42L42_RS_PLUG_SHIFT) | - (1 << CS42L42_RS_UNPLUG_SHIFT) | (0 << CS42L42_TS_PLUG_SHIFT) | (0 << CS42L42_TS_UNPLUG_SHIFT)); } @@ -1335,12 +1331,8 @@ static void cs42l42_init_hs_type_detect(struct cs42l42_private *cs42l42) /* Mask tip sense interrupts */ regmap_update_bits(cs42l42->regmap, CS42L42_TSRS_PLUG_INT_MASK, - CS42L42_RS_PLUG_MASK | - CS42L42_RS_UNPLUG_MASK | CS42L42_TS_PLUG_MASK | CS42L42_TS_UNPLUG_MASK, - (1 << CS42L42_RS_PLUG_SHIFT) | - (1 << CS42L42_RS_UNPLUG_SHIFT) | (1 << CS42L42_TS_PLUG_SHIFT) | (1 << CS42L42_TS_UNPLUG_SHIFT)); -- cgit v1.2.3 From 3edde6de090617adea18f2068489086c0d8087e3 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 16 Nov 2021 16:39:00 +0000 Subject: ASoC: cs42l42: Simplify reporting of jack unplug When reporting a jack unplug there's no need to make the reported flags conditional on which flags were reported during the plug event. It's perfectly safe to report all flags and buttons as not-present and let the higher code filter for changes. There's also no need to make two separate snd_soc_jack_report() calls for presence flags and button flags. It can all be done in one report. Signed-off-by: Richard Fitzgerald Link: https://lore.kernel.org/r/20211116163901.45390-4-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l42.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 3674f73301dc..8efcee3e60d3 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -1657,18 +1657,8 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data) cs42l42->plug_state = CS42L42_TS_UNPLUG; cs42l42_cancel_hs_type_detect(cs42l42); - switch (cs42l42->hs_type) { - case CS42L42_PLUG_CTIA: - case CS42L42_PLUG_OMTP: - snd_soc_jack_report(cs42l42->jack, 0, SND_JACK_HEADSET); - break; - case CS42L42_PLUG_HEADPHONE: - snd_soc_jack_report(cs42l42->jack, 0, SND_JACK_HEADPHONE); - break; - default: - break; - } snd_soc_jack_report(cs42l42->jack, 0, + SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3); -- cgit v1.2.3 From bbf0e1d36519a5cd2c08dc1348f997cd5240eb2e Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 16 Nov 2021 16:39:01 +0000 Subject: ASoC: cs42l42: Remove redundant pll_divout member Now that struct cs42l42_private has pll_config, the current PLL configuration can be looked up directly in pll_ratio_table. This makes the pll_divout member of cs42l42_private redundant since it was only a copy of the value from pll_ratio_table. Signed-off-by: Richard Fitzgerald Link: https://lore.kernel.org/r/20211116163901.45390-5-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l42.c | 9 +++------ sound/soc/codecs/cs42l42.h | 1 - 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 8efcee3e60d3..0c4303547fd8 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -734,10 +734,6 @@ static int cs42l42_pll_config(struct snd_soc_component *component) CS42L42_PLL_DIVOUT_MASK, (pll_ratio_table[i].pll_divout * pll_ratio_table[i].n) << CS42L42_PLL_DIVOUT_SHIFT); - if (pll_ratio_table[i].n != 1) - cs42l42->pll_divout = pll_ratio_table[i].pll_divout; - else - cs42l42->pll_divout = 0; snd_soc_component_update_bits(component, CS42L42_PLL_CAL_RATIO, CS42L42_PLL_CAL_RATIO_MASK, @@ -1004,12 +1000,13 @@ static int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream) snd_soc_component_update_bits(component, CS42L42_PLL_CTL1, CS42L42_PLL_START_MASK, 1); - if (cs42l42->pll_divout) { + if (pll_ratio_table[cs42l42->pll_config].n > 1) { usleep_range(CS42L42_PLL_DIVOUT_TIME_US, CS42L42_PLL_DIVOUT_TIME_US * 2); + regval = pll_ratio_table[cs42l42->pll_config].pll_divout; snd_soc_component_update_bits(component, CS42L42_PLL_CTL3, CS42L42_PLL_DIVOUT_MASK, - cs42l42->pll_divout << + regval << CS42L42_PLL_DIVOUT_SHIFT); } diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h index c8b3267a318b..75ade987d0db 100644 --- a/sound/soc/codecs/cs42l42.h +++ b/sound/soc/codecs/cs42l42.h @@ -845,7 +845,6 @@ struct cs42l42_private { int bclk; u32 sclk; u32 srate; - u8 pll_divout; u8 plug_state; u8 hs_type; u8 ts_inv; -- cgit v1.2.3 From 8ae77801c81d16a09f6b67a6f8d91255d34f5f2c Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 16 Nov 2021 17:21:34 +0200 Subject: ASoC: SOF: utils: Add generic function to get the reply for a tx message The code to get the reply for a tx is identical in all but one place: imx8_get_reply(), imx8m_get_reply(), atom_get_reply(), bdw_get_reply(). hda_dsp_ipc_get_reply() have additional check in place for PROBES and special handling of PM messages. Add a generic implementation to the core which can be used as drop in replacement. The reply size check is changed to be able to handle cases when the reply size is not know beforehand (this is the case for PROBES and DEBUG_MEM_USAGE for example). Signed-off-by: Peter Ujfalusi Reviewed-by: Rander Wang Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Reviewed-by: Guennadi Liakhovetski Signed-off-by: Daniel Baluta Link: https://lore.kernel.org/r/20211116152137.52129-2-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/sof/sof-priv.h | 6 +++++ 2 files changed, 67 insertions(+) diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index a4036d0b3d3a..6771b444065d 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -394,6 +394,67 @@ int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, u32 header, } EXPORT_SYMBOL(sof_ipc_tx_message_no_pm); +/* Generic helper function to retrieve the reply */ +void snd_sof_ipc_get_reply(struct snd_sof_dev *sdev) +{ + struct snd_sof_ipc_msg *msg = sdev->msg; + struct sof_ipc_reply reply; + int ret = 0; + + /* + * Sometimes, there is unexpected reply ipc arriving. The reply + * ipc belongs to none of the ipcs sent from driver. + * In this case, the driver must ignore the ipc. + */ + if (!msg) { + dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n"); + return; + } + + /* get the generic reply */ + snd_sof_dsp_mailbox_read(sdev, sdev->host_box.offset, &reply, + sizeof(reply)); + + if (reply.error < 0) { + memcpy(msg->reply_data, &reply, sizeof(reply)); + ret = reply.error; + } else if (!reply.hdr.size) { + /* Reply should always be >= sizeof(struct sof_ipc_reply) */ + if (msg->reply_size) + dev_err(sdev->dev, + "empty reply received, expected %zu bytes\n", + msg->reply_size); + else + dev_err(sdev->dev, "empty reply received\n"); + + ret = -EINVAL; + } else if (msg->reply_size > 0) { + if (reply.hdr.size == msg->reply_size) { + ret = 0; + } else if (reply.hdr.size < msg->reply_size) { + dev_dbg(sdev->dev, + "reply size (%u) is less than expected (%zu)\n", + reply.hdr.size, msg->reply_size); + + msg->reply_size = reply.hdr.size; + ret = 0; + } else { + dev_err(sdev->dev, + "reply size (%u) exceeds the buffer size (%zu)\n", + reply.hdr.size, msg->reply_size); + ret = -EINVAL; + } + + /* get the full message if reply.hdr.size <= msg->reply_size */ + if (!ret) + snd_sof_dsp_mailbox_read(sdev, sdev->host_box.offset, + msg->reply_data, msg->reply_size); + } + + msg->reply_error = ret; +} +EXPORT_SYMBOL(snd_sof_ipc_get_reply); + /* handle reply message from DSP */ void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id) { diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index ba341b1bda0c..2c97ffa98e3e 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -515,6 +515,7 @@ void snd_sof_fw_unload(struct snd_sof_dev *sdev); */ struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev); void snd_sof_ipc_free(struct snd_sof_dev *sdev); +void snd_sof_ipc_get_reply(struct snd_sof_dev *sdev); void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id); void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev); int snd_sof_ipc_stream_pcm_params(struct snd_sof_dev *sdev, @@ -527,6 +528,11 @@ int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, u32 header, void *msg_data, size_t msg_bytes, void *reply_data, size_t reply_bytes); int sof_ipc_init_msg_memory(struct snd_sof_dev *sdev); +static inline void snd_sof_ipc_process_reply(struct snd_sof_dev *sdev, u32 msg_id) +{ + snd_sof_ipc_get_reply(sdev); + snd_sof_ipc_reply(sdev, msg_id); +} /* * Trace/debug -- cgit v1.2.3 From 18c45f270352fb76c8b5b133b3ae3971769f8a22 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 16 Nov 2021 17:21:35 +0200 Subject: ASoC: SOF: imx: Use the generic helper to get the reply Make use of the generic snd_sof_ipc_process_reply() from the core instead the local implementation. snd_sof_ipc_process_reply() handles the reply retrieving and the ipc reply Signed-off-by: Peter Ujfalusi Reviewed-by: Rander Wang Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Reviewed-by: Guennadi Liakhovetski Signed-off-by: Daniel Baluta Link: https://lore.kernel.org/r/20211116152137.52129-3-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/imx/imx8.c | 37 +------------------------------------ sound/soc/sof/imx/imx8m.c | 37 +------------------------------------ 2 files changed, 2 insertions(+), 72 deletions(-) diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c index dd59a74480d6..0aeb44d0acc7 100644 --- a/sound/soc/sof/imx/imx8.c +++ b/sound/soc/sof/imx/imx8.c @@ -59,40 +59,6 @@ struct imx8_priv { }; -static void imx8_get_reply(struct snd_sof_dev *sdev) -{ - struct snd_sof_ipc_msg *msg = sdev->msg; - struct sof_ipc_reply reply; - int ret = 0; - - if (!msg) { - dev_warn(sdev->dev, "unexpected ipc interrupt\n"); - return; - } - - /* get reply */ - sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); - - if (reply.error < 0) { - memcpy(msg->reply_data, &reply, sizeof(reply)); - ret = reply.error; - } else { - /* reply has correct size? */ - if (reply.hdr.size != msg->reply_size) { - dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n", - msg->reply_size, reply.hdr.size); - ret = -EINVAL; - } - - /* read the message */ - if (msg->reply_size > 0) - sof_mailbox_read(sdev, sdev->host_box.offset, - msg->reply_data, msg->reply_size); - } - - msg->reply_error = ret; -} - static int imx8_get_mailbox_offset(struct snd_sof_dev *sdev) { return MBOX_OFFSET; @@ -109,8 +75,7 @@ static void imx8_dsp_handle_reply(struct imx_dsp_ipc *ipc) unsigned long flags; spin_lock_irqsave(&priv->sdev->ipc_lock, flags); - imx8_get_reply(priv->sdev); - snd_sof_ipc_reply(priv->sdev, 0); + snd_sof_ipc_process_reply(priv->sdev, 0); spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags); } diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c index e4618980cf8b..f454a5d0a87e 100644 --- a/sound/soc/sof/imx/imx8m.c +++ b/sound/soc/sof/imx/imx8m.c @@ -32,40 +32,6 @@ struct imx8m_priv { struct platform_device *ipc_dev; }; -static void imx8m_get_reply(struct snd_sof_dev *sdev) -{ - struct snd_sof_ipc_msg *msg = sdev->msg; - struct sof_ipc_reply reply; - int ret = 0; - - if (!msg) { - dev_warn(sdev->dev, "unexpected ipc interrupt\n"); - return; - } - - /* get reply */ - sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); - - if (reply.error < 0) { - memcpy(msg->reply_data, &reply, sizeof(reply)); - ret = reply.error; - } else { - /* reply has correct size? */ - if (reply.hdr.size != msg->reply_size) { - dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n", - msg->reply_size, reply.hdr.size); - ret = -EINVAL; - } - - /* read the message */ - if (msg->reply_size > 0) - sof_mailbox_read(sdev, sdev->host_box.offset, - msg->reply_data, msg->reply_size); - } - - msg->reply_error = ret; -} - static int imx8m_get_mailbox_offset(struct snd_sof_dev *sdev) { return MBOX_OFFSET; @@ -82,8 +48,7 @@ static void imx8m_dsp_handle_reply(struct imx_dsp_ipc *ipc) unsigned long flags; spin_lock_irqsave(&priv->sdev->ipc_lock, flags); - imx8m_get_reply(priv->sdev); - snd_sof_ipc_reply(priv->sdev, 0); + snd_sof_ipc_process_reply(priv->sdev, 0); spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags); } -- cgit v1.2.3 From 0bd2891bda4550774946abbfac88443a16c15d5a Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 16 Nov 2021 17:21:36 +0200 Subject: ASoC: SOF: intel: Use the generic helper to get the reply Make use of the generic snd_sof_ipc_process_reply() from the core instead the local implementation. snd_sof_ipc_process_reply() handles the reply retrieving and the ipc reply Signed-off-by: Peter Ujfalusi Reviewed-by: Rander Wang Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Reviewed-by: Guennadi Liakhovetski Signed-off-by: Daniel Baluta Link: https://lore.kernel.org/r/20211116152137.52129-4-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/atom.c | 43 +------------------------------------------ sound/soc/sof/intel/bdw.c | 43 +------------------------------------------ sound/soc/sof/intel/hda-ipc.c | 29 ++--------------------------- 3 files changed, 4 insertions(+), 111 deletions(-) diff --git a/sound/soc/sof/intel/atom.c b/sound/soc/sof/intel/atom.c index 74c630bb9847..cdc96a7df493 100644 --- a/sound/soc/sof/intel/atom.c +++ b/sound/soc/sof/intel/atom.c @@ -27,7 +27,6 @@ static void atom_host_done(struct snd_sof_dev *sdev); static void atom_dsp_done(struct snd_sof_dev *sdev); -static void atom_get_reply(struct snd_sof_dev *sdev); /* * Debug @@ -154,8 +153,7 @@ irqreturn_t atom_irq_thread(int irq, void *context) * because the done bit can't be set in cmd_done function * which is triggered by msg */ - atom_get_reply(sdev); - snd_sof_ipc_reply(sdev, ipcx); + snd_sof_ipc_process_reply(sdev, ipcx); atom_dsp_done(sdev); @@ -195,45 +193,6 @@ int atom_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) } EXPORT_SYMBOL_NS(atom_send_msg, SND_SOC_SOF_INTEL_ATOM_HIFI_EP); -static void atom_get_reply(struct snd_sof_dev *sdev) -{ - struct snd_sof_ipc_msg *msg = sdev->msg; - struct sof_ipc_reply reply; - int ret = 0; - - /* - * Sometimes, there is unexpected reply ipc arriving. The reply - * ipc belongs to none of the ipcs sent from driver. - * In this case, the driver must ignore the ipc. - */ - if (!msg) { - dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n"); - return; - } - - /* get reply */ - sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); - - if (reply.error < 0) { - memcpy(msg->reply_data, &reply, sizeof(reply)); - ret = reply.error; - } else { - /* reply correct size ? */ - if (reply.hdr.size != msg->reply_size) { - dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n", - msg->reply_size, reply.hdr.size); - ret = -EINVAL; - } - - /* read the message */ - if (msg->reply_size > 0) - sof_mailbox_read(sdev, sdev->host_box.offset, - msg->reply_data, msg->reply_size); - } - - msg->reply_error = ret; -} - int atom_get_mailbox_offset(struct snd_sof_dev *sdev) { return MBOX_OFFSET; diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c index 2c09a523288e..156006bed017 100644 --- a/sound/soc/sof/intel/bdw.c +++ b/sound/soc/sof/intel/bdw.c @@ -75,7 +75,6 @@ static const struct snd_sof_debugfs_map bdw_debugfs[] = { static void bdw_host_done(struct snd_sof_dev *sdev); static void bdw_dsp_done(struct snd_sof_dev *sdev); -static void bdw_get_reply(struct snd_sof_dev *sdev); /* * DSP Control. @@ -326,8 +325,7 @@ static irqreturn_t bdw_irq_thread(int irq, void *context) * because the done bit can't be set in cmd_done function * which is triggered by msg */ - bdw_get_reply(sdev); - snd_sof_ipc_reply(sdev, ipcx); + snd_sof_ipc_process_reply(sdev, ipcx); bdw_dsp_done(sdev); @@ -372,45 +370,6 @@ static int bdw_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) return 0; } -static void bdw_get_reply(struct snd_sof_dev *sdev) -{ - struct snd_sof_ipc_msg *msg = sdev->msg; - struct sof_ipc_reply reply; - int ret = 0; - - /* - * Sometimes, there is unexpected reply ipc arriving. The reply - * ipc belongs to none of the ipcs sent from driver. - * In this case, the driver must ignore the ipc. - */ - if (!msg) { - dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n"); - return; - } - - /* get reply */ - sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); - - if (reply.error < 0) { - memcpy(msg->reply_data, &reply, sizeof(reply)); - ret = reply.error; - } else { - /* reply correct size ? */ - if (reply.hdr.size != msg->reply_size) { - dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n", - msg->reply_size, reply.hdr.size); - ret = -EINVAL; - } - - /* read the message */ - if (msg->reply_size > 0) - sof_mailbox_read(sdev, sdev->host_box.offset, - msg->reply_data, msg->reply_size); - } - - msg->reply_error = ret; -} - static int bdw_get_mailbox_offset(struct snd_sof_dev *sdev) { return MBOX_OFFSET; diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c index 11f20a5a62df..2019087a84ce 100644 --- a/sound/soc/sof/intel/hda-ipc.c +++ b/sound/soc/sof/intel/hda-ipc.c @@ -70,7 +70,6 @@ void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev) struct snd_sof_ipc_msg *msg = sdev->msg; struct sof_ipc_reply reply; struct sof_ipc_cmd_hdr *hdr; - int ret = 0; /* * Sometimes, there is unexpected reply ipc arriving. The reply @@ -94,35 +93,11 @@ void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev) reply.hdr.cmd = SOF_IPC_GLB_REPLY; reply.hdr.size = sizeof(reply); memcpy(msg->reply_data, &reply, sizeof(reply)); - goto out; - } - - /* get IPC reply from DSP in the mailbox */ - sof_mailbox_read(sdev, sdev->host_box.offset, &reply, - sizeof(reply)); - if (reply.error < 0) { - memcpy(msg->reply_data, &reply, sizeof(reply)); - ret = reply.error; + msg->reply_error = 0; } else { - /* reply correct size ? */ - if (reply.hdr.size != msg->reply_size && - /* getter payload is never known upfront */ - ((reply.hdr.cmd & SOF_GLB_TYPE_MASK) != SOF_IPC_GLB_PROBE)) { - dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n", - msg->reply_size, reply.hdr.size); - ret = -EINVAL; - } - - /* read the message */ - if (msg->reply_size > 0) - sof_mailbox_read(sdev, sdev->host_box.offset, - msg->reply_data, msg->reply_size); + snd_sof_ipc_get_reply(sdev); } - -out: - msg->reply_error = ret; - } /* IPC handler thread */ -- cgit v1.2.3 From 2f0b1b013bbc5d6f4c7c386e12f423d6b4ef3245 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 16 Nov 2021 17:21:37 +0200 Subject: ASoC: SOF: debug: Add support for IPC message injection In order to stress test the firmware's ability to handle (mis)crafted IPC messages this patch adds a debugfs interface where a binary file (message) can be written and the message is sent to the firmware as it is. Read on the same file will return the reply from the firmware if it is available as a binary. Signed-off-by: Peter Ujfalusi Reviewed-by: Rander Wang Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Reviewed-by: Guennadi Liakhovetski Signed-off-by: Daniel Baluta Link: https://lore.kernel.org/r/20211116152137.52129-5-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/Kconfig | 8 ++++ sound/soc/sof/debug.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/sof/sof-priv.h | 4 ++ 3 files changed, 119 insertions(+) diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index b6fa659179b6..89eea5558190 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -194,6 +194,14 @@ config SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST Say Y if you want to enable IPC flood test. If unsure, select "N". +config SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR + bool "SOF enable IPC message injector" + help + This option enables the IPC message injector which can be used to send + crafted IPC messages to the DSP to test its robustness. + Say Y if you want to enable the IPC message injector. + If unsure, select "N". + config SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT bool "SOF retain DSP context on any FW exceptions" help diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index dc1df5fb7b4c..2f8b5ac9b78a 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -336,6 +336,104 @@ static int sof_debug_ipc_flood_test(struct snd_sof_dev *sdev, } #endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR) +static ssize_t msg_inject_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct snd_sof_dfsentry *dfse = file->private_data; + struct sof_ipc_reply *rhdr = dfse->msg_inject_rx; + + if (!rhdr->hdr.size || !count || *ppos) + return 0; + + if (count > rhdr->hdr.size) + count = rhdr->hdr.size; + + if (copy_to_user(buffer, dfse->msg_inject_rx, count)) + return -EFAULT; + + *ppos += count; + return count; +} + +static ssize_t msg_inject_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct snd_sof_dfsentry *dfse = file->private_data; + struct snd_sof_dev *sdev = dfse->sdev; + struct sof_ipc_cmd_hdr *hdr = dfse->msg_inject_tx; + size_t size; + int ret, err; + + if (*ppos) + return 0; + + size = simple_write_to_buffer(dfse->msg_inject_tx, SOF_IPC_MSG_MAX_SIZE, + ppos, buffer, count); + if (size != count) + return size > 0 ? -EFAULT : size; + + ret = pm_runtime_get_sync(sdev->dev); + if (ret < 0 && ret != -EACCES) { + dev_err_ratelimited(sdev->dev, "%s: DSP resume failed: %d\n", + __func__, ret); + pm_runtime_put_noidle(sdev->dev); + goto out; + } + + /* send the message */ + memset(dfse->msg_inject_rx, 0, SOF_IPC_MSG_MAX_SIZE); + ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd, dfse->msg_inject_tx, count, + dfse->msg_inject_rx, SOF_IPC_MSG_MAX_SIZE); + + pm_runtime_mark_last_busy(sdev->dev); + err = pm_runtime_put_autosuspend(sdev->dev); + if (err < 0) + dev_err_ratelimited(sdev->dev, "%s: DSP idle failed: %d\n", + __func__, err); + + /* return size if test is successful */ + if (ret >= 0) + ret = size; + +out: + return ret; +} + +static const struct file_operations msg_inject_fops = { + .open = simple_open, + .read = msg_inject_read, + .write = msg_inject_write, + .llseek = default_llseek, +}; + +static int snd_sof_debugfs_msg_inject_item(struct snd_sof_dev *sdev, + const char *name, mode_t mode, + const struct file_operations *fops) +{ + struct snd_sof_dfsentry *dfse; + + dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); + if (!dfse) + return -ENOMEM; + + /* pre allocate the tx and rx buffers */ + dfse->msg_inject_tx = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); + dfse->msg_inject_rx = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); + if (!dfse->msg_inject_tx || !dfse->msg_inject_rx) + return -ENOMEM; + + dfse->type = SOF_DFSENTRY_TYPE_BUF; + dfse->sdev = sdev; + + debugfs_create_file(name, mode, sdev->debugfs_root, dfse, fops); + /* add to dfsentry list */ + list_add(&dfse->list, &sdev->dfsentry_list); + + return 0; +} +#endif + static ssize_t sof_dfsentry_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { @@ -812,6 +910,15 @@ int snd_sof_dbg_init(struct snd_sof_dev *sdev) return err; #endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR) + err = snd_sof_debugfs_msg_inject_item(sdev, "ipc_msg_inject", 0644, + &msg_inject_fops); + + /* errors are only due to memory allocation, not debugfs */ + if (err < 0) + return err; +#endif + return 0; } EXPORT_SYMBOL_GPL(snd_sof_dbg_init); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 2c97ffa98e3e..9a8af76b2f8b 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -325,6 +325,10 @@ struct snd_sof_dfsentry { enum sof_debugfs_access_type access_type; #if ENABLE_DEBUGFS_CACHEBUF char *cache_buf; /* buffer to cache the contents of debugfs memory */ +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR) + void *msg_inject_tx; + void *msg_inject_rx; #endif struct snd_sof_dev *sdev; struct list_head list; /* list in sdev dfsentry list */ -- cgit v1.2.3 From 7fabe7fed182498cac568100d8e28d4b95f8a80e Mon Sep 17 00:00:00 2001 From: Olivier Moysan Date: Wed, 17 Nov 2021 12:00:31 +0100 Subject: ASoC: stm32: sai: increase channels_max limit The SAI peripheral supports up to 16 channels in TDM mode (8L+8R). The driver currently supports TDM over two channels. Increase SAI DAI playback/record channels_max, to also allow up to 16 channels in TDM mode. Signed-off-by: Olivier Moysan Link: https://lore.kernel.org/r/20211117110031.19345-1-olivier.moysan@foss.st.com Signed-off-by: Mark Brown --- sound/soc/stm/stm32_sai_sub.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index 9c3b8e209656..95cd38a502bb 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -1294,7 +1294,7 @@ static struct snd_soc_dai_driver stm32_sai_playback_dai = { .id = 1, /* avoid call to fmt_single_name() */ .playback = { .channels_min = 1, - .channels_max = 2, + .channels_max = 16, .rate_min = 8000, .rate_max = 192000, .rates = SNDRV_PCM_RATE_CONTINUOUS, @@ -1312,7 +1312,7 @@ static struct snd_soc_dai_driver stm32_sai_capture_dai = { .id = 1, /* avoid call to fmt_single_name() */ .capture = { .channels_min = 1, - .channels_max = 2, + .channels_max = 16, .rate_min = 8000, .rate_max = 192000, .rates = SNDRV_PCM_RATE_CONTINUOUS, -- cgit v1.2.3 From 37c4fd0db7c961145d9d1909ecab386fdf703c26 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 17 Nov 2021 14:30:40 +0100 Subject: ALSA: hda: Do disconnect jacks at codec unbind The HD-audio codec driver remove may happen also at dynamically unbinding during operation, hence it needs manual triggers of snd_device_disconnect() calls, while it's missing for the jack objects that are associated with the codec. This patch adds the manual disconnection call for jacks when the remove happens without card->shutdown (i.e. not under the full removal). Link: https://lore.kernel.org/r/20211117133040.20272-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_bind.c | 2 ++ sound/pci/hda/hda_jack.c | 11 +++++++++++ sound/pci/hda/hda_jack.h | 1 + 3 files changed, 14 insertions(+) diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c index 7153bd53e189..c572fb5886d5 100644 --- a/sound/pci/hda/hda_bind.c +++ b/sound/pci/hda/hda_bind.c @@ -14,6 +14,7 @@ #include #include #include "hda_local.h" +#include "hda_jack.h" /* * find a matching codec id @@ -158,6 +159,7 @@ static int hda_codec_driver_remove(struct device *dev) refcount_dec(&codec->pcm_ref); snd_hda_codec_disconnect_pcms(codec); + snd_hda_jack_tbl_disconnect(codec); wait_event(codec->remove_sleep, !refcount_read(&codec->pcm_ref)); snd_power_sync_ref(codec->bus->card); diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index f29975e3e98d..7d7786df60ea 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -158,6 +158,17 @@ snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid, int dev_id) return jack; } +void snd_hda_jack_tbl_disconnect(struct hda_codec *codec) +{ + struct hda_jack_tbl *jack = codec->jacktbl.list; + int i; + + for (i = 0; i < codec->jacktbl.used; i++, jack++) { + if (!codec->bus->shutdown && jack->jack) + snd_device_disconnect(codec->card, jack->jack); + } +} + void snd_hda_jack_tbl_clear(struct hda_codec *codec) { struct hda_jack_tbl *jack = codec->jacktbl.list; diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h index 2abf7aac243a..ff7d289c034b 100644 --- a/sound/pci/hda/hda_jack.h +++ b/sound/pci/hda/hda_jack.h @@ -69,6 +69,7 @@ struct hda_jack_tbl * snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec, unsigned char tag, int dev_id); +void snd_hda_jack_tbl_disconnect(struct hda_codec *codec); void snd_hda_jack_tbl_clear(struct hda_codec *codec); void snd_hda_jack_set_dirty_all(struct hda_codec *codec); -- cgit v1.2.3 From 846aef1d7cc05651e27c17c3b4e2b5ce5cdec97b Mon Sep 17 00:00:00 2001 From: Ajit Kumar Pandey Date: Wed, 17 Nov 2021 11:37:14 +0200 Subject: ASoC: SOF: amd: Add Renoir ACP HW support This patch initializes ACP HW block to support SOF on AMD Renoir platform. Signed-off-by: Ajit Kumar Pandey Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Signed-off-by: Daniel Baluta Link: https://lore.kernel.org/r/20211117093734.17407-2-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/Kconfig | 1 + sound/soc/sof/Makefile | 1 + sound/soc/sof/amd/Kconfig | 30 ++++++++++ sound/soc/sof/amd/Makefile | 11 ++++ sound/soc/sof/amd/acp-dsp-offset.h | 20 +++++++ sound/soc/sof/amd/acp.c | 118 +++++++++++++++++++++++++++++++++++++ sound/soc/sof/amd/acp.h | 36 +++++++++++ sound/soc/sof/amd/renoir.c | 34 +++++++++++ 8 files changed, 251 insertions(+) create mode 100644 sound/soc/sof/amd/Kconfig create mode 100644 sound/soc/sof/amd/Makefile create mode 100644 sound/soc/sof/amd/acp-dsp-offset.h create mode 100644 sound/soc/sof/amd/acp.c create mode 100644 sound/soc/sof/amd/acp.h create mode 100644 sound/soc/sof/amd/renoir.c diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index b6fa659179b6..35c68192d2ce 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -225,6 +225,7 @@ config SND_SOC_SOF_PROBE_WORK_QUEUE When selected, the probe is handled in two steps, for example to avoid lockdeps if request_module is used in the probe. +source "sound/soc/sof/amd/Kconfig" source "sound/soc/sof/imx/Kconfig" source "sound/soc/sof/intel/Kconfig" source "sound/soc/sof/xtensa/Kconfig" diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index 1dac5cb4dfd6..81ad8cb666e3 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -22,4 +22,5 @@ obj-$(CONFIG_SND_SOC_SOF_PCI_DEV) += snd-sof-pci.o obj-$(CONFIG_SND_SOC_SOF_INTEL_TOPLEVEL) += intel/ obj-$(CONFIG_SND_SOC_SOF_IMX_TOPLEVEL) += imx/ +obj-$(CONFIG_SND_SOC_SOF_AMD_TOPLEVEL) += amd/ obj-$(CONFIG_SND_SOC_SOF_XTENSA) += xtensa/ diff --git a/sound/soc/sof/amd/Kconfig b/sound/soc/sof/amd/Kconfig new file mode 100644 index 000000000000..400dd5a24ae6 --- /dev/null +++ b/sound/soc/sof/amd/Kconfig @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +# This file is provided under a dual BSD/GPLv2 license. When using or +# redistributing this file, you may do so under either license. +# +# Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved. + +config SND_SOC_SOF_AMD_TOPLEVEL + tristate "SOF support for AMD audio DSPs" + depends on X86 || COMPILE_TEST + help + This adds support for Sound Open Firmware for AMD platforms. + Say Y if you have such a device. + If unsure select "N". + +if SND_SOC_SOF_AMD_TOPLEVEL + +config SND_SOC_SOF_AMD_COMMON + tristate + select SND_SOC_SOF + help + This option is not user-selectable but automatically handled by + 'select' statements at a higher level + +config SND_SOC_SOF_AMD_RENOIR + tristate "SOF support for RENOIR" + select SND_SOC_SOF_AMD_COMMON + help + Select this option for SOF support on AMD Renoir platform + +endif diff --git a/sound/soc/sof/amd/Makefile b/sound/soc/sof/amd/Makefile new file mode 100644 index 000000000000..ac2ecd21be5f --- /dev/null +++ b/sound/soc/sof/amd/Makefile @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +# This file is provided under a dual BSD/GPLv2 license. When using or +# redistributing this file, you may do so under either license. +# +# Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved. + +snd-sof-amd-acp-objs := acp.o +snd-sof-amd-renoir-objs := renoir.o + +obj-$(CONFIG_SND_SOC_SOF_AMD_COMMON) += snd-sof-amd-acp.o +obj-$(CONFIG_SND_SOC_SOF_AMD_RENOIR) +=snd-sof-amd-renoir.o diff --git a/sound/soc/sof/amd/acp-dsp-offset.h b/sound/soc/sof/amd/acp-dsp-offset.h new file mode 100644 index 000000000000..2cc2a9a842c5 --- /dev/null +++ b/sound/soc/sof/amd/acp-dsp-offset.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved. + * + * Author: Ajit Kumar Pandey + */ + +#ifndef _ACP_DSP_IP_OFFSET_H +#define _ACP_DSP_IP_OFFSET_H + +#define ACP_SOFT_RESET 0x1000 + +/* Registers from ACP_PGFSM block */ +#define ACP_PGFSM_CONTROL 0x141C +#define ACP_PGFSM_STATUS 0x1420 + +#endif diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c new file mode 100644 index 000000000000..687a67419335 --- /dev/null +++ b/sound/soc/sof/amd/acp.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved. +// +// Authors: Vijendar Mukunda +// Ajit Kumar Pandey + +/* + * Hardware interface for generic AMD ACP processor + */ + +#include +#include +#include + +#include "../ops.h" +#include "acp.h" +#include "acp-dsp-offset.h" + +static int acp_power_on(struct snd_sof_dev *sdev) +{ + unsigned int val; + int ret; + + val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_PGFSM_STATUS); + + if (val == ACP_POWERED_ON) + return 0; + + if (val & ACP_PGFSM_STATUS_MASK) + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_PGFSM_CONTROL, + ACP_PGFSM_CNTL_POWER_ON_MASK); + + ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_PGFSM_STATUS, val, !val, + ACP_REG_POLL_INTERVAL, ACP_REG_POLL_TIMEOUT_US); + if (ret < 0) + dev_err(sdev->dev, "timeout in ACP_PGFSM_STATUS read\n"); + + return ret; +} + +static int acp_reset(struct snd_sof_dev *sdev) +{ + unsigned int val; + int ret; + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, ACP_ASSERT_RESET); + + ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, val, + val & ACP_SOFT_RESET_DONE_MASK, + ACP_REG_POLL_INTERVAL, ACP_REG_POLL_TIMEOUT_US); + if (ret < 0) { + dev_err(sdev->dev, "timeout asserting reset\n"); + return ret; + } + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, ACP_RELEASE_RESET); + + ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, val, !val, + ACP_REG_POLL_INTERVAL, ACP_REG_POLL_TIMEOUT_US); + if (ret < 0) + dev_err(sdev->dev, "timeout in releasing reset\n"); + + return ret; +} + +static int acp_init(struct snd_sof_dev *sdev) +{ + int ret; + + /* power on */ + ret = acp_power_on(sdev); + if (ret) { + dev_err(sdev->dev, "ACP power on failed\n"); + return ret; + } + /* Reset */ + return acp_reset(sdev); +} + +int amd_sof_acp_probe(struct snd_sof_dev *sdev) +{ + struct pci_dev *pci = to_pci_dev(sdev->dev); + struct acp_dev_data *adata; + unsigned int addr; + + adata = devm_kzalloc(sdev->dev, sizeof(struct acp_dev_data), + GFP_KERNEL); + if (!adata) + return -ENOMEM; + + adata->dev = sdev; + addr = pci_resource_start(pci, ACP_DSP_BAR); + sdev->bar[ACP_DSP_BAR] = devm_ioremap(sdev->dev, addr, pci_resource_len(pci, ACP_DSP_BAR)); + if (!sdev->bar[ACP_DSP_BAR]) { + dev_err(sdev->dev, "ioremap error\n"); + return -ENXIO; + } + + pci_set_master(pci); + + sdev->pdata->hw_pdata = adata; + + return acp_init(sdev); +} +EXPORT_SYMBOL_NS(amd_sof_acp_probe, SND_SOC_SOF_AMD_COMMON); + +int amd_sof_acp_remove(struct snd_sof_dev *sdev) +{ + return acp_reset(sdev); +} +EXPORT_SYMBOL_NS(amd_sof_acp_remove, SND_SOC_SOF_AMD_COMMON); + +MODULE_DESCRIPTION("AMD ACP sof driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h new file mode 100644 index 000000000000..c7ac8f9941d5 --- /dev/null +++ b/sound/soc/sof/amd/acp.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved. + * + * Author: Ajit Kumar Pandey + */ + +#ifndef __SOF_AMD_ACP_H +#define __SOF_AMD_ACP_H + +#define ACP_DSP_BAR 0 + +#define ACP_REG_POLL_INTERVAL 500 +#define ACP_REG_POLL_TIMEOUT_US 2000 + +#define ACP_PGFSM_CNTL_POWER_ON_MASK 0x01 +#define ACP_PGFSM_STATUS_MASK 0x03 +#define ACP_POWERED_ON 0x00 +#define ACP_ASSERT_RESET 0x01 +#define ACP_RELEASE_RESET 0x00 +#define ACP_SOFT_RESET_DONE_MASK 0x00010001 + +/* Common device data struct for ACP devices */ +struct acp_dev_data { + struct snd_sof_dev *dev; +}; + +/* ACP device probe/remove */ +int amd_sof_acp_probe(struct snd_sof_dev *sdev); +int amd_sof_acp_remove(struct snd_sof_dev *sdev); + +extern const struct snd_sof_dsp_ops sof_renoir_ops; +#endif diff --git a/sound/soc/sof/amd/renoir.c b/sound/soc/sof/amd/renoir.c new file mode 100644 index 000000000000..3d1dc6c2fa9b --- /dev/null +++ b/sound/soc/sof/amd/renoir.c @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2021 Advanced Micro Devices, Inc. +// +// Authors: Ajit Kumar Pandey + +/* + * Hardware interface for Audio DSP on Renoir platform + */ + +#include +#include + +#include "../ops.h" +#include "acp.h" + +/* AMD Renoir DSP ops */ +const struct snd_sof_dsp_ops sof_renoir_ops = { + /* probe and remove */ + .probe = amd_sof_acp_probe, + .remove = amd_sof_acp_remove, + + /* Register IO */ + .write = sof_io_write, + .read = sof_io_read, +}; +EXPORT_SYMBOL(sof_renoir_ops); + +MODULE_IMPORT_NS(SND_SOC_SOF_AMD_COMMON); +MODULE_DESCRIPTION("RENOIR SOF Driver"); +MODULE_LICENSE("Dual BSD/GPL"); -- cgit v1.2.3 From 0e44572a28a49109eae23af1545c658b86c4bf00 Mon Sep 17 00:00:00 2001 From: Ajit Kumar Pandey Date: Wed, 17 Nov 2021 11:37:15 +0200 Subject: ASoC: SOF: amd: Add helper callbacks for ACP's DMA configuration ACP DMA is used for loading SOF firmware into DSP memory and data transfer from system memory to DSP memory. Add helper callbacks to initialize and configure ACP DMA block for fw loading. Signed-off-by: Vijendar Mukunda Signed-off-by: Ajit Kumar Pandey Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Signed-off-by: Daniel Baluta Link: https://lore.kernel.org/r/20211117093734.17407-3-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp-dsp-offset.h | 47 ++++++++ sound/soc/sof/amd/acp.c | 222 ++++++++++++++++++++++++++++++++++++- sound/soc/sof/amd/acp.h | 91 +++++++++++++++ 3 files changed, 359 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/amd/acp-dsp-offset.h b/sound/soc/sof/amd/acp-dsp-offset.h index 2cc2a9a842c5..bfb02390b414 100644 --- a/sound/soc/sof/amd/acp-dsp-offset.h +++ b/sound/soc/sof/amd/acp-dsp-offset.h @@ -11,10 +11,57 @@ #ifndef _ACP_DSP_IP_OFFSET_H #define _ACP_DSP_IP_OFFSET_H +/* Registers from ACP_DMA_0 block */ +#define ACP_DMA_CNTL_0 0x00 +#define ACP_DMA_DSCR_STRT_IDX_0 0x20 +#define ACP_DMA_DSCR_CNT_0 0x40 +#define ACP_DMA_PRIO_0 0x60 +#define ACP_DMA_CUR_DSCR_0 0x80 +#define ACP_DMA_ERR_STS_0 0xC0 +#define ACP_DMA_DESC_BASE_ADDR 0xE0 +#define ACP_DMA_DESC_MAX_NUM_DSCR 0xE4 +#define ACP_DMA_CH_STS 0xE8 +#define ACP_DMA_CH_GROUP 0xEC +#define ACP_DMA_CH_RST_STS 0xF0 + +/* Registers from ACP_AXI2AXIATU block */ +#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1 0xC00 +#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_1 0xC04 +#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2 0xC08 +#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_2 0xC0C +#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_3 0xC10 +#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_3 0xC14 +#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_4 0xC18 +#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_4 0xC1C +#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5 0xC20 +#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_5 0xC24 +#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_6 0xC28 +#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_6 0xC2C +#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_7 0xC30 +#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_7 0xC34 +#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_8 0xC38 +#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_8 0xC3C +#define ACPAXI2AXI_ATU_CTRL 0xC40 #define ACP_SOFT_RESET 0x1000 /* Registers from ACP_PGFSM block */ #define ACP_PGFSM_CONTROL 0x141C #define ACP_PGFSM_STATUS 0x1420 +/* Registers from ACP_INTR block */ +#define ACP_DSP_SW_INTR_CNTL 0x1814 +#define ACP_ERROR_STATUS 0x18C4 + +/* Registers from ACP_SHA block */ +#define ACP_SHA_DSP_FW_QUALIFIER 0x1C70 +#define ACP_SHA_DMA_CMD 0x1CB0 +#define ACP_SHA_MSG_LENGTH 0x1CB4 +#define ACP_SHA_DMA_STRT_ADDR 0x1CB8 +#define ACP_SHA_DMA_DESTINATION_ADDR 0x1CBC +#define ACP_SHA_DMA_CMD_STS 0x1CC0 +#define ACP_SHA_DMA_ERR_STATUS 0x1CC4 +#define ACP_SHA_TRANSFER_BYTE_CNT 0x1CC8 + +#define ACP_SCRATCH_REG_0 0x10000 + #endif diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index 687a67419335..3778f781f16a 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -20,6 +20,219 @@ #include "acp.h" #include "acp-dsp-offset.h" +static void configure_acp_groupregisters(struct acp_dev_data *adata) +{ + struct snd_sof_dev *sdev = adata->dev; + + /* Group Enable */ + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_BASE_ADDR_GRP_1, + ACP_SRAM_PTE_OFFSET | BIT(31)); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1, + PAGE_SIZE_4K_ENABLE); + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_CTRL, ACP_ATU_CACHE_INVALID); +} + +static void init_dma_descriptor(struct acp_dev_data *adata) +{ + struct snd_sof_dev *sdev = adata->dev; + unsigned int addr; + + addr = ACP_SRAM_PTE_OFFSET + offsetof(struct scratch_reg_conf, dma_desc); + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_DESC_BASE_ADDR, addr); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_DESC_MAX_NUM_DSCR, ACP_MAX_DESC_CNT); +} + +static void configure_dma_descriptor(struct acp_dev_data *adata, unsigned short idx, + struct dma_descriptor *dscr_info) +{ + struct snd_sof_dev *sdev = adata->dev; + unsigned int offset; + + offset = ACP_SCRATCH_REG_0 + offsetof(struct scratch_reg_conf, dma_desc) + + idx * sizeof(struct dma_descriptor); + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, offset, dscr_info->src_addr); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, offset + 0x4, dscr_info->dest_addr); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, offset + 0x8, dscr_info->tx_cnt.u32_all); +} + +static int config_dma_channel(struct acp_dev_data *adata, unsigned int ch, + unsigned int idx, unsigned int dscr_count) +{ + struct snd_sof_dev *sdev = adata->dev; + unsigned int val, status; + int ret; + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_CNTL_0 + ch * sizeof(u32), + ACP_DMA_CH_RST | ACP_DMA_CH_GRACEFUL_RST_EN); + + ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_DMA_CH_RST_STS, val, + val & (1 << ch), ACP_REG_POLL_INTERVAL, + ACP_REG_POLL_TIMEOUT_US); + if (ret < 0) { + status = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_ERROR_STATUS); + val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_DMA_ERR_STS_0 + ch * sizeof(u32)); + + dev_err(sdev->dev, "ACP_DMA_ERR_STS :0x%x ACP_ERROR_STATUS :0x%x\n", val, status); + return ret; + } + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, (ACP_DMA_CNTL_0 + ch * sizeof(u32)), 0); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_DSCR_CNT_0 + ch * sizeof(u32), dscr_count); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_DSCR_STRT_IDX_0 + ch * sizeof(u32), idx); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_PRIO_0 + ch * sizeof(u32), 0); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_CNTL_0 + ch * sizeof(u32), ACP_DMA_CH_RUN); + + return ret; +} + +static int acpbus_dma_start(struct acp_dev_data *adata, unsigned int ch, + unsigned int dscr_count, struct dma_descriptor *dscr_info) +{ + struct snd_sof_dev *sdev = adata->dev; + int ret; + u16 dscr; + + if (!dscr_info || !dscr_count) + return -EINVAL; + + for (dscr = 0; dscr < dscr_count; dscr++) + configure_dma_descriptor(adata, dscr, dscr_info++); + + ret = config_dma_channel(adata, ch, 0, dscr_count); + if (ret < 0) + dev_err(sdev->dev, "config dma ch failed:%d\n", ret); + + return ret; +} + +int configure_and_run_dma(struct acp_dev_data *adata, unsigned int src_addr, + unsigned int dest_addr, int dsp_data_size) +{ + struct snd_sof_dev *sdev = adata->dev; + unsigned int desc_count, index; + int ret; + + for (desc_count = 0; desc_count < ACP_MAX_DESC && dsp_data_size >= 0; + desc_count++, dsp_data_size -= ACP_PAGE_SIZE) { + adata->dscr_info[desc_count].src_addr = src_addr + desc_count * ACP_PAGE_SIZE; + adata->dscr_info[desc_count].dest_addr = dest_addr + desc_count * ACP_PAGE_SIZE; + adata->dscr_info[desc_count].tx_cnt.bits.count = ACP_PAGE_SIZE; + if (dsp_data_size < ACP_PAGE_SIZE) + adata->dscr_info[desc_count].tx_cnt.bits.count = dsp_data_size; + } + + ret = acpbus_dma_start(adata, 0, desc_count, adata->dscr_info); + if (ret) + dev_err(sdev->dev, "acpbus_dma_start failed\n"); + + /* Clear descriptor array */ + for (index = 0; index < desc_count; index++) + memset(&adata->dscr_info[index], 0x00, sizeof(struct dma_descriptor)); + + return ret; +} + +int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr, + unsigned int start_addr, unsigned int dest_addr, + unsigned int image_length) +{ + struct snd_sof_dev *sdev = adata->dev; + unsigned int tx_count, fw_qualifier, val; + int ret; + + if (!image_addr) { + dev_err(sdev->dev, "SHA DMA image address is NULL\n"); + return -EINVAL; + } + + val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SHA_DMA_CMD); + if (val & ACP_SHA_RUN) { + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_CMD, ACP_SHA_RESET); + ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SHA_DMA_CMD_STS, + val, val & ACP_SHA_RESET, + ACP_REG_POLL_INTERVAL, + ACP_REG_POLL_TIMEOUT_US); + if (ret < 0) { + dev_err(sdev->dev, "SHA DMA Failed to Reset\n"); + return ret; + } + } + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_STRT_ADDR, start_addr); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_DESTINATION_ADDR, dest_addr); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_MSG_LENGTH, image_length); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_CMD, ACP_SHA_RUN); + + ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SHA_TRANSFER_BYTE_CNT, + tx_count, tx_count == image_length, + ACP_REG_POLL_INTERVAL, ACP_DMA_COMPLETE_TIMEOUT_US); + if (ret < 0) { + dev_err(sdev->dev, "SHA DMA Failed to Transfer Length %x\n", tx_count); + return ret; + } + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DSP_FW_QUALIFIER, DSP_FW_RUN_ENABLE); + + fw_qualifier = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SHA_DSP_FW_QUALIFIER); + if (!(fw_qualifier & DSP_FW_RUN_ENABLE)) { + dev_err(sdev->dev, "PSP validation failed\n"); + return -EINVAL; + } + + return ret; +} + +int acp_dma_status(struct acp_dev_data *adata, unsigned char ch) +{ + struct snd_sof_dev *sdev = adata->dev; + unsigned int val; + int ret = 0; + + val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_DMA_CNTL_0 + ch * sizeof(u32)); + if (val & ACP_DMA_CH_RUN) { + ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_DMA_CH_STS, val, !val, + ACP_REG_POLL_INTERVAL, + ACP_DMA_COMPLETE_TIMEOUT_US); + if (ret < 0) + dev_err(sdev->dev, "DMA_CHANNEL %d status timeout\n", ch); + } + + return ret; +} + +void memcpy_from_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *dst, size_t bytes) +{ + unsigned int reg_offset = offset + ACP_SCRATCH_REG_0; + int i, j; + + for (i = 0, j = 0; i < bytes; i = i + 4, j++) + dst[j] = snd_sof_dsp_read(sdev, ACP_DSP_BAR, reg_offset + i); +} + +void memcpy_to_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *src, size_t bytes) +{ + unsigned int reg_offset = offset + ACP_SCRATCH_REG_0; + int i, j; + + for (i = 0, j = 0; i < bytes; i = i + 4, j++) + snd_sof_dsp_write(sdev, ACP_DSP_BAR, reg_offset + i, src[j]); +} + +static int acp_memory_init(struct snd_sof_dev *sdev) +{ + struct acp_dev_data *adata = sdev->pdata->hw_pdata; + + snd_sof_dsp_update_bits(sdev, ACP_DSP_BAR, ACP_DSP_SW_INTR_CNTL, + ACP_DSP_INTR_EN_MASK, ACP_DSP_INTR_EN_MASK); + configure_acp_groupregisters(adata); + init_dma_descriptor(adata); + + return 0; +} + static int acp_power_on(struct snd_sof_dev *sdev) { unsigned int val; @@ -86,6 +299,7 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev) struct pci_dev *pci = to_pci_dev(sdev->dev); struct acp_dev_data *adata; unsigned int addr; + int ret; adata = devm_kzalloc(sdev->dev, sizeof(struct acp_dev_data), GFP_KERNEL); @@ -104,7 +318,13 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev) sdev->pdata->hw_pdata = adata; - return acp_init(sdev); + ret = acp_init(sdev); + if (ret < 0) + return ret; + + acp_memory_init(sdev); + + return 0; } EXPORT_SYMBOL_NS(amd_sof_acp_probe, SND_SOC_SOF_AMD_COMMON); diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index c7ac8f9941d5..ff01d0ef67ef 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -15,6 +15,7 @@ #define ACP_REG_POLL_INTERVAL 500 #define ACP_REG_POLL_TIMEOUT_US 2000 +#define ACP_DMA_COMPLETE_TIMEOUT_US 5000 #define ACP_PGFSM_CNTL_POWER_ON_MASK 0x01 #define ACP_PGFSM_STATUS_MASK 0x03 @@ -23,11 +24,101 @@ #define ACP_RELEASE_RESET 0x00 #define ACP_SOFT_RESET_DONE_MASK 0x00010001 +#define ACP_DSP_INTR_EN_MASK 0x00000001 +#define ACP_SRAM_PTE_OFFSET 0x02050000 +#define PAGE_SIZE_4K_ENABLE 0x2 +#define ACP_PAGE_SIZE 0x1000 +#define ACP_DMA_CH_RUN 0x02 +#define ACP_MAX_DESC_CNT 0x02 +#define DSP_FW_RUN_ENABLE 0x01 +#define ACP_SHA_RUN 0x01 +#define ACP_SHA_RESET 0x02 +#define ACP_DMA_CH_RST 0x01 +#define ACP_DMA_CH_GRACEFUL_RST_EN 0x10 +#define ACP_ATU_CACHE_INVALID 0x01 +#define ACP_MAX_DESC 128 +#define ACPBUS_REG_BASE_OFFSET ACP_DMA_CNTL_0 + +struct acp_atu_grp_pte { + u32 low; + u32 high; +}; + +union dma_tx_cnt { + struct { + unsigned int count : 19; + unsigned int reserved : 12; + unsigned ioc : 1; + } bitfields, bits; + unsigned int u32_all; + signed int i32_all; +}; + +struct dma_descriptor { + unsigned int src_addr; + unsigned int dest_addr; + union dma_tx_cnt tx_cnt; + unsigned int reserved; +}; + +/* Scratch memory structure for communication b/w host and dsp */ +struct scratch_ipc_conf { + /* DSP mailbox */ + u8 sof_out_box[512]; + /* Host mailbox */ + u8 sof_in_box[512]; + /* Debug memory */ + u8 sof_debug_box[1024]; + /* Exception memory*/ + u8 sof_except_box[1024]; + /* Stream buffer */ + u8 sof_stream_box[1024]; + /* Trace buffer */ + u8 sof_trace_box[1024]; + /* Host msg flag */ + u32 sof_host_msg_write; + /* Host ack flag*/ + u32 sof_host_ack_write; + /* DSP msg flag */ + u32 sof_dsp_msg_write; + /* Dsp ack flag */ + u32 sof_dsp_ack_write; +}; + +struct scratch_reg_conf { + struct scratch_ipc_conf info; + struct acp_atu_grp_pte grp1_pte[16]; + struct acp_atu_grp_pte grp2_pte[16]; + struct acp_atu_grp_pte grp3_pte[16]; + struct acp_atu_grp_pte grp4_pte[16]; + struct acp_atu_grp_pte grp5_pte[16]; + struct acp_atu_grp_pte grp6_pte[16]; + struct acp_atu_grp_pte grp7_pte[16]; + struct acp_atu_grp_pte grp8_pte[16]; + struct dma_descriptor dma_desc[64]; + unsigned int reg_offset[8]; + unsigned int buf_size[8]; + u8 acp_tx_fifo_buf[256]; + u8 acp_rx_fifo_buf[256]; + unsigned int reserve[]; +}; + /* Common device data struct for ACP devices */ struct acp_dev_data { struct snd_sof_dev *dev; + struct dma_descriptor dscr_info[ACP_MAX_DESC]; }; +void memcpy_to_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *src, size_t bytes); +void memcpy_from_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *dst, size_t bytes); + +int acp_dma_status(struct acp_dev_data *adata, unsigned char ch); +int configure_and_run_dma(struct acp_dev_data *adata, unsigned int src_addr, + unsigned int dest_addr, int dsp_data_size); +int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr, + unsigned int start_addr, unsigned int dest_addr, + unsigned int image_length); + /* ACP device probe/remove */ int amd_sof_acp_probe(struct snd_sof_dev *sdev); int amd_sof_acp_remove(struct snd_sof_dev *sdev); -- cgit v1.2.3 From 7e51a9e38ab204eba2844b8773486392d7444435 Mon Sep 17 00:00:00 2001 From: Ajit Kumar Pandey Date: Wed, 17 Nov 2021 11:37:16 +0200 Subject: ASoC: SOF: amd: Add fw loader and renoir dsp ops to load firmware Add acp-loader module with ops callback to load and run firmware on ACP DSP block on Renoir platform. Signed-off-by: Ajit Kumar Pandey Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Signed-off-by: Daniel Baluta Link: https://lore.kernel.org/r/20211117093734.17407-4-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/Makefile | 2 +- sound/soc/sof/amd/acp-dsp-offset.h | 3 + sound/soc/sof/amd/acp-loader.c | 199 +++++++++++++++++++++++++++++++++++++ sound/soc/sof/amd/acp.h | 27 +++++ sound/soc/sof/amd/renoir.c | 15 +++ 5 files changed, 245 insertions(+), 1 deletion(-) create mode 100644 sound/soc/sof/amd/acp-loader.c diff --git a/sound/soc/sof/amd/Makefile b/sound/soc/sof/amd/Makefile index ac2ecd21be5f..031fb9493876 100644 --- a/sound/soc/sof/amd/Makefile +++ b/sound/soc/sof/amd/Makefile @@ -4,7 +4,7 @@ # # Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved. -snd-sof-amd-acp-objs := acp.o +snd-sof-amd-acp-objs := acp.o acp-loader.o snd-sof-amd-renoir-objs := renoir.o obj-$(CONFIG_SND_SOC_SOF_AMD_COMMON) += snd-sof-amd-acp.o diff --git a/sound/soc/sof/amd/acp-dsp-offset.h b/sound/soc/sof/amd/acp-dsp-offset.h index bfb02390b414..f4bc7e9abafb 100644 --- a/sound/soc/sof/amd/acp-dsp-offset.h +++ b/sound/soc/sof/amd/acp-dsp-offset.h @@ -24,6 +24,9 @@ #define ACP_DMA_CH_GROUP 0xEC #define ACP_DMA_CH_RST_STS 0xF0 +/* Registers from ACP_DSP_0 block */ +#define ACP_DSP0_RUNSTALL 0x414 + /* Registers from ACP_AXI2AXIATU block */ #define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1 0xC00 #define ACPAXI2AXI_ATU_BASE_ADDR_GRP_1 0xC04 diff --git a/sound/soc/sof/amd/acp-loader.c b/sound/soc/sof/amd/acp-loader.c new file mode 100644 index 000000000000..2dc15ae38155 --- /dev/null +++ b/sound/soc/sof/amd/acp-loader.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2021 Advanced Micro Devices, Inc. +// +// Authors: Ajit Kumar Pandey + +/* + * Hardware interface for ACP DSP Firmware binaries loader + */ + +#include +#include +#include + +#include "../ops.h" +#include "acp-dsp-offset.h" +#include "acp.h" + +#define FW_BIN 0 +#define FW_DATA_BIN 1 + +#define FW_BIN_PTE_OFFSET 0x00 +#define FW_DATA_BIN_PTE_OFFSET 0x08 + +#define ACP_DSP_RUN 0x00 + +int acp_dsp_block_read(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type, + u32 offset, void *dest, size_t size) +{ + switch (blk_type) { + case SOF_FW_BLK_TYPE_SRAM: + offset = offset - ACP_SCRATCH_MEMORY_ADDRESS; + memcpy_from_scratch(sdev, offset, dest, size); + break; + default: + dev_err(sdev->dev, "bad blk type 0x%x\n", blk_type); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_NS(acp_dsp_block_read, SND_SOC_SOF_AMD_COMMON); + +int acp_dsp_block_write(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type, + u32 offset, void *src, size_t size) +{ + struct snd_sof_pdata *plat_data = sdev->pdata; + struct pci_dev *pci = to_pci_dev(sdev->dev); + struct acp_dev_data *adata; + void *dest; + u32 dma_size, page_count; + unsigned int size_fw; + + adata = sdev->pdata->hw_pdata; + + switch (blk_type) { + case SOF_FW_BLK_TYPE_IRAM: + if (!adata->bin_buf) { + size_fw = plat_data->fw->size; + page_count = PAGE_ALIGN(size_fw) >> PAGE_SHIFT; + dma_size = page_count * ACP_PAGE_SIZE; + adata->bin_buf = dma_alloc_coherent(&pci->dev, dma_size, + &adata->sha_dma_addr, + GFP_ATOMIC); + if (!adata->bin_buf) + return -ENOMEM; + } + adata->fw_bin_size = size + offset; + dest = adata->bin_buf + offset; + break; + case SOF_FW_BLK_TYPE_DRAM: + if (!adata->data_buf) { + adata->data_buf = dma_alloc_coherent(&pci->dev, + ACP_DEFAULT_DRAM_LENGTH, + &adata->dma_addr, + GFP_ATOMIC); + if (!adata->data_buf) + return -ENOMEM; + } + dest = adata->data_buf + offset; + adata->fw_data_bin_size = size + offset; + break; + case SOF_FW_BLK_TYPE_SRAM: + offset = offset - ACP_SCRATCH_MEMORY_ADDRESS; + memcpy_to_scratch(sdev, offset, src, size); + return 0; + default: + dev_err(sdev->dev, "bad blk type 0x%x\n", blk_type); + return -EINVAL; + } + + memcpy(dest, src, size); + return 0; +} +EXPORT_SYMBOL_NS(acp_dsp_block_write, SND_SOC_SOF_AMD_COMMON); + +int acp_get_bar_index(struct snd_sof_dev *sdev, u32 type) +{ + return type; +} +EXPORT_SYMBOL_NS(acp_get_bar_index, SND_SOC_SOF_AMD_COMMON); + +static void configure_pte_for_fw_loading(int type, int num_pages, struct acp_dev_data *adata) +{ + struct snd_sof_dev *sdev; + unsigned int low, high; + dma_addr_t addr; + u16 page_idx; + u32 offset; + + sdev = adata->dev; + + switch (type) { + case FW_BIN: + offset = FW_BIN_PTE_OFFSET; + addr = adata->sha_dma_addr; + break; + case FW_DATA_BIN: + offset = adata->fw_bin_page_count * 8; + addr = adata->dma_addr; + break; + default: + dev_err(sdev->dev, "Invalid data type %x\n", type); + return; + } + + for (page_idx = 0; page_idx < num_pages; page_idx++) { + low = lower_32_bits(addr); + high = upper_32_bits(addr); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + offset, low); + high |= BIT(31); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + offset + 4, high); + offset += 8; + addr += PAGE_SIZE; + } +} + +/* pre fw run operations */ +int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev) +{ + struct pci_dev *pci = to_pci_dev(sdev->dev); + struct snd_sof_pdata *plat_data = sdev->pdata; + struct acp_dev_data *adata; + unsigned int src_addr, size_fw; + u32 page_count, dma_size; + int ret; + + adata = sdev->pdata->hw_pdata; + size_fw = adata->fw_bin_size; + + page_count = PAGE_ALIGN(size_fw) >> PAGE_SHIFT; + adata->fw_bin_page_count = page_count; + + configure_pte_for_fw_loading(FW_BIN, page_count, adata); + ret = configure_and_run_sha_dma(adata, adata->bin_buf, ACP_SYSTEM_MEMORY_WINDOW, + ACP_IRAM_BASE_ADDRESS, size_fw); + if (ret < 0) { + dev_err(sdev->dev, "SHA DMA transfer failed status: %d\n", ret); + return ret; + } + configure_pte_for_fw_loading(FW_DATA_BIN, ACP_DRAM_PAGE_COUNT, adata); + + src_addr = ACP_SYSTEM_MEMORY_WINDOW + page_count * ACP_PAGE_SIZE; + ret = configure_and_run_dma(adata, src_addr, ACP_DATA_RAM_BASE_ADDRESS, + adata->fw_data_bin_size); + if (ret < 0) { + dev_err(sdev->dev, "acp dma configuration failed: %d\n", ret); + return ret; + } + + ret = acp_dma_status(adata, 0); + if (ret < 0) + dev_err(sdev->dev, "acp dma transfer status: %d\n", ret); + + /* Free memory once DMA is complete */ + dma_size = (PAGE_ALIGN(plat_data->fw->size) >> PAGE_SHIFT) * ACP_PAGE_SIZE; + dma_free_coherent(&pci->dev, dma_size, adata->bin_buf, adata->sha_dma_addr); + dma_free_coherent(&pci->dev, ACP_DEFAULT_DRAM_LENGTH, adata->data_buf, adata->dma_addr); + adata->bin_buf = NULL; + adata->data_buf = NULL; + + return ret; +} +EXPORT_SYMBOL_NS(acp_dsp_pre_fw_run, SND_SOC_SOF_AMD_COMMON); + +int acp_sof_dsp_run(struct snd_sof_dev *sdev) +{ + int val; + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DSP0_RUNSTALL, ACP_DSP_RUN); + val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_DSP0_RUNSTALL); + dev_dbg(sdev->dev, "ACP_DSP0_RUNSTALL : 0x%0x\n", val); + + return 0; +} +EXPORT_SYMBOL_NS(acp_sof_dsp_run, SND_SOC_SOF_AMD_COMMON); diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index ff01d0ef67ef..e755a31374c6 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -11,6 +11,8 @@ #ifndef __SOF_AMD_ACP_H #define __SOF_AMD_ACP_H +#include "../sof-priv.h" + #define ACP_DSP_BAR 0 #define ACP_REG_POLL_INTERVAL 500 @@ -39,6 +41,13 @@ #define ACP_MAX_DESC 128 #define ACPBUS_REG_BASE_OFFSET ACP_DMA_CNTL_0 +#define ACP_DEFAULT_DRAM_LENGTH 0x00080000 +#define ACP_SCRATCH_MEMORY_ADDRESS 0x02050000 +#define ACP_SYSTEM_MEMORY_WINDOW 0x4000000 +#define ACP_IRAM_BASE_ADDRESS 0x000000 +#define ACP_DATA_RAM_BASE_ADDRESS 0x01000000 +#define ACP_DRAM_PAGE_COUNT 128 + struct acp_atu_grp_pte { u32 low; u32 high; @@ -106,6 +115,13 @@ struct scratch_reg_conf { /* Common device data struct for ACP devices */ struct acp_dev_data { struct snd_sof_dev *dev; + unsigned int fw_bin_size; + unsigned int fw_data_bin_size; + u32 fw_bin_page_count; + dma_addr_t sha_dma_addr; + u8 *bin_buf; + dma_addr_t dma_addr; + u8 *data_buf; struct dma_descriptor dscr_info[ACP_MAX_DESC]; }; @@ -123,5 +139,16 @@ int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr, int amd_sof_acp_probe(struct snd_sof_dev *sdev); int amd_sof_acp_remove(struct snd_sof_dev *sdev); +/* DSP Loader callbacks */ +int acp_sof_dsp_run(struct snd_sof_dev *sdev); +int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev); +int acp_get_bar_index(struct snd_sof_dev *sdev, u32 type); + +/* Block IO callbacks */ +int acp_dsp_block_write(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type, + u32 offset, void *src, size_t size); +int acp_dsp_block_read(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type, + u32 offset, void *dest, size_t size); + extern const struct snd_sof_dsp_ops sof_renoir_ops; #endif diff --git a/sound/soc/sof/amd/renoir.c b/sound/soc/sof/amd/renoir.c index 3d1dc6c2fa9b..bca80784b322 100644 --- a/sound/soc/sof/amd/renoir.c +++ b/sound/soc/sof/amd/renoir.c @@ -26,6 +26,21 @@ const struct snd_sof_dsp_ops sof_renoir_ops = { /* Register IO */ .write = sof_io_write, .read = sof_io_read, + + /* Block IO */ + .block_read = acp_dsp_block_read, + .block_write = acp_dsp_block_write, + + /* Module loading */ + .load_module = snd_sof_parse_module_memcpy, + + /*Firmware loading */ + .load_firmware = snd_sof_load_firmware_memcpy, + .pre_fw_run = acp_dsp_pre_fw_run, + .get_bar_index = acp_get_bar_index, + + /* DSP core boot */ + .run = acp_sof_dsp_run, }; EXPORT_SYMBOL(sof_renoir_ops); -- cgit v1.2.3 From 738a2b5e2cc9fd63d48faac11c8d60a5a2313a9d Mon Sep 17 00:00:00 2001 From: Ajit Kumar Pandey Date: Wed, 17 Nov 2021 11:37:17 +0200 Subject: ASoC: SOF: amd: Add IPC support for ACP IP block Add IPC module for generic ACP block and exposed ops callback for to synchronize SOF IPC message between host and DSP Signed-off-by: Balakishore Pati Signed-off-by: Ajit Kumar Pandey Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Signed-off-by: Daniel Baluta Link: https://lore.kernel.org/r/20211117093734.17407-5-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/Makefile | 2 +- sound/soc/sof/amd/acp-dsp-offset.h | 2 + sound/soc/sof/amd/acp-ipc.c | 187 +++++++++++++++++++++++++++++++++++++ sound/soc/sof/amd/acp.c | 44 ++++++++- sound/soc/sof/amd/acp.h | 15 +++ sound/soc/sof/amd/renoir.c | 8 ++ 6 files changed, 256 insertions(+), 2 deletions(-) create mode 100644 sound/soc/sof/amd/acp-ipc.c diff --git a/sound/soc/sof/amd/Makefile b/sound/soc/sof/amd/Makefile index 031fb9493876..29928b16002f 100644 --- a/sound/soc/sof/amd/Makefile +++ b/sound/soc/sof/amd/Makefile @@ -4,7 +4,7 @@ # # Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved. -snd-sof-amd-acp-objs := acp.o acp-loader.o +snd-sof-amd-acp-objs := acp.o acp-loader.o acp-ipc.o snd-sof-amd-renoir-objs := renoir.o obj-$(CONFIG_SND_SOC_SOF_AMD_COMMON) += snd-sof-amd-acp.o diff --git a/sound/soc/sof/amd/acp-dsp-offset.h b/sound/soc/sof/amd/acp-dsp-offset.h index f4bc7e9abafb..3a1c848020ca 100644 --- a/sound/soc/sof/amd/acp-dsp-offset.h +++ b/sound/soc/sof/amd/acp-dsp-offset.h @@ -53,6 +53,8 @@ /* Registers from ACP_INTR block */ #define ACP_DSP_SW_INTR_CNTL 0x1814 +#define ACP_DSP_SW_INTR_STAT 0x1818 +#define ACP_SW_INTR_TRIG 0x181C #define ACP_ERROR_STATUS 0x18C4 /* Registers from ACP_SHA block */ diff --git a/sound/soc/sof/amd/acp-ipc.c b/sound/soc/sof/amd/acp-ipc.c new file mode 100644 index 000000000000..e132223b4c66 --- /dev/null +++ b/sound/soc/sof/amd/acp-ipc.c @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2021 Advanced Micro Devices, Inc. +// +// Authors: Balakishore Pati +// Ajit Kumar Pandey + +/* ACP-specific SOF IPC code */ + +#include +#include "../ops.h" +#include "acp.h" +#include "acp-dsp-offset.h" + +void acp_mailbox_write(struct snd_sof_dev *sdev, u32 offset, void *message, size_t bytes) +{ + memcpy_to_scratch(sdev, offset, message, bytes); +} +EXPORT_SYMBOL_NS(acp_mailbox_write, SND_SOC_SOF_AMD_COMMON); + +void acp_mailbox_read(struct snd_sof_dev *sdev, u32 offset, void *message, size_t bytes) +{ + memcpy_from_scratch(sdev, offset, message, bytes); +} +EXPORT_SYMBOL_NS(acp_mailbox_read, SND_SOC_SOF_AMD_COMMON); + +static void acpbus_trigger_host_to_dsp_swintr(struct acp_dev_data *adata) +{ + struct snd_sof_dev *sdev = adata->dev; + u32 swintr_trigger; + + swintr_trigger = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SW_INTR_TRIG); + swintr_trigger |= 0x01; + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SW_INTR_TRIG, swintr_trigger); +} + +static void acp_ipc_host_msg_set(struct snd_sof_dev *sdev) +{ + unsigned int host_msg = offsetof(struct scratch_ipc_conf, sof_host_msg_write); + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + host_msg, 1); +} + +static void acp_dsp_ipc_host_done(struct snd_sof_dev *sdev) +{ + unsigned int dsp_msg = offsetof(struct scratch_ipc_conf, sof_dsp_msg_write); + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_msg, 0); +} + +static void acp_dsp_ipc_dsp_done(struct snd_sof_dev *sdev) +{ + unsigned int dsp_ack = offsetof(struct scratch_ipc_conf, sof_dsp_ack_write); + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_ack, 0); +} + +int acp_sof_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + struct acp_dev_data *adata = sdev->pdata->hw_pdata; + unsigned int offset = offsetof(struct scratch_ipc_conf, sof_in_box); + + acp_mailbox_write(sdev, offset, msg->msg_data, msg->msg_size); + acp_ipc_host_msg_set(sdev); + + /* Trigger host to dsp interrupt for the msg */ + acpbus_trigger_host_to_dsp_swintr(adata); + return 0; +} +EXPORT_SYMBOL_NS(acp_sof_ipc_send_msg, SND_SOC_SOF_AMD_COMMON); + +static void acp_dsp_ipc_get_reply(struct snd_sof_dev *sdev) +{ + struct snd_sof_ipc_msg *msg = sdev->msg; + struct sof_ipc_reply reply; + struct sof_ipc_cmd_hdr *hdr; + unsigned int offset = offsetof(struct scratch_ipc_conf, sof_in_box); + int ret = 0; + + /* + * Sometimes, there is unexpected reply ipc arriving. The reply + * ipc belongs to none of the ipcs sent from driver. + * In this case, the driver must ignore the ipc. + */ + if (!msg) { + dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n"); + return; + } + hdr = msg->msg_data; + if (hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CTX_SAVE) || + hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE)) { + /* + * memory windows are powered off before sending IPC reply, + * so we can't read the mailbox for CTX_SAVE and PM_GATE + * replies. + */ + reply.error = 0; + reply.hdr.cmd = SOF_IPC_GLB_REPLY; + reply.hdr.size = sizeof(reply); + memcpy(msg->reply_data, &reply, sizeof(reply)); + goto out; + } + /* get IPC reply from DSP in the mailbox */ + acp_mailbox_read(sdev, offset, &reply, sizeof(reply)); + if (reply.error < 0) { + memcpy(msg->reply_data, &reply, sizeof(reply)); + ret = reply.error; + } else { + /* reply correct size ? */ + if (reply.hdr.size != msg->reply_size && + !(reply.hdr.cmd & SOF_IPC_GLB_PROBE)) { + dev_err(sdev->dev, "reply expected %zu got %u bytes\n", + msg->reply_size, reply.hdr.size); + ret = -EINVAL; + } + /* read the message */ + if (msg->reply_size > 0) + acp_mailbox_read(sdev, offset, msg->reply_data, msg->reply_size); + } +out: + msg->reply_error = ret; +} + +irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context) +{ + struct snd_sof_dev *sdev = context; + unsigned int dsp_msg_write = offsetof(struct scratch_ipc_conf, sof_dsp_msg_write); + unsigned int dsp_ack_write = offsetof(struct scratch_ipc_conf, sof_dsp_ack_write); + bool ipc_irq = false; + int dsp_msg, dsp_ack; + + dsp_msg = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_msg_write); + if (dsp_msg) { + snd_sof_ipc_msgs_rx(sdev); + acp_dsp_ipc_host_done(sdev); + ipc_irq = true; + } + + dsp_ack = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_ack_write); + if (dsp_ack) { + spin_lock_irq(&sdev->ipc_lock); + /* handle immediate reply from DSP core */ + acp_dsp_ipc_get_reply(sdev); + snd_sof_ipc_reply(sdev, 0); + /* set the done bit */ + acp_dsp_ipc_dsp_done(sdev); + spin_unlock_irq(&sdev->ipc_lock); + ipc_irq = true; + } + + if (!ipc_irq) + dev_dbg_ratelimited(sdev->dev, "nothing to do in IPC IRQ thread\n"); + + return IRQ_HANDLED; +} +EXPORT_SYMBOL_NS(acp_sof_ipc_irq_thread, SND_SOC_SOF_AMD_COMMON); + +int acp_sof_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, + void *p, size_t sz) +{ + unsigned int offset = offsetof(struct scratch_ipc_conf, sof_out_box); + + if (!substream || !sdev->stream_box.size) + acp_mailbox_read(sdev, offset, p, sz); + + return 0; +} +EXPORT_SYMBOL_NS(acp_sof_ipc_msg_data, SND_SOC_SOF_AMD_COMMON); + +int acp_sof_ipc_pcm_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, + const struct sof_ipc_pcm_params_reply *reply) +{ + /* TODO: Implement stream hw params to validate stream offset */ + return 0; +} +EXPORT_SYMBOL_NS(acp_sof_ipc_pcm_params, SND_SOC_SOF_AMD_COMMON); + +int acp_sof_ipc_get_mailbox_offset(struct snd_sof_dev *sdev) +{ + return ACP_SCRATCH_MEMORY_ADDRESS; +} +EXPORT_SYMBOL_NS(acp_sof_ipc_get_mailbox_offset, SND_SOC_SOF_AMD_COMMON); + +MODULE_DESCRIPTION("AMD ACP sof-ipc driver"); diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index 3778f781f16a..43a57d15e3ca 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -233,6 +233,34 @@ static int acp_memory_init(struct snd_sof_dev *sdev) return 0; } +static irqreturn_t acp_irq_thread(int irq, void *context) +{ + struct snd_sof_dev *sdev = context; + unsigned int val; + + val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_DSP_SW_INTR_STAT); + if (val & ACP_DSP_TO_HOST_IRQ) { + sof_ops(sdev)->irq_thread(irq, sdev); + val |= ACP_DSP_TO_HOST_IRQ; + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DSP_SW_INTR_STAT, val); + return IRQ_HANDLED; + } + + return IRQ_NONE; +}; + +static irqreturn_t acp_irq_handler(int irq, void *dev_id) +{ + struct snd_sof_dev *sdev = dev_id; + unsigned int val; + + val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_DSP_SW_INTR_STAT); + if (val) + return IRQ_WAKE_THREAD; + + return IRQ_NONE; +} + static int acp_power_on(struct snd_sof_dev *sdev) { unsigned int val; @@ -318,9 +346,20 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev) sdev->pdata->hw_pdata = adata; + sdev->ipc_irq = pci->irq; + ret = request_threaded_irq(sdev->ipc_irq, acp_irq_handler, acp_irq_thread, + IRQF_SHARED, "AudioDSP", sdev); + if (ret < 0) { + dev_err(sdev->dev, "failed to register IRQ %d\n", + sdev->ipc_irq); + return ret; + } + ret = acp_init(sdev); - if (ret < 0) + if (ret < 0) { + free_irq(sdev->ipc_irq, sdev); return ret; + } acp_memory_init(sdev); @@ -330,6 +369,9 @@ EXPORT_SYMBOL_NS(amd_sof_acp_probe, SND_SOC_SOF_AMD_COMMON); int amd_sof_acp_remove(struct snd_sof_dev *sdev) { + if (sdev->ipc_irq) + free_irq(sdev->ipc_irq, sdev); + return acp_reset(sdev); } EXPORT_SYMBOL_NS(amd_sof_acp_remove, SND_SOC_SOF_AMD_COMMON); diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index e755a31374c6..ac8340119125 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -48,6 +48,8 @@ #define ACP_DATA_RAM_BASE_ADDRESS 0x01000000 #define ACP_DRAM_PAGE_COUNT 128 +#define ACP_DSP_TO_HOST_IRQ 0x04 + struct acp_atu_grp_pte { u32 low; u32 high; @@ -150,5 +152,18 @@ int acp_dsp_block_write(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_t int acp_dsp_block_read(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type, u32 offset, void *dest, size_t size); +/* IPC callbacks */ +irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context); +int acp_sof_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, + void *p, size_t sz); +int acp_sof_ipc_send_msg(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg); +int acp_sof_ipc_get_mailbox_offset(struct snd_sof_dev *sdev); +int acp_sof_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id); +int acp_sof_ipc_pcm_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, + const struct sof_ipc_pcm_params_reply *reply); +void acp_mailbox_write(struct snd_sof_dev *sdev, u32 offset, void *message, size_t bytes); +void acp_mailbox_read(struct snd_sof_dev *sdev, u32 offset, void *message, size_t bytes); + extern const struct snd_sof_dsp_ops sof_renoir_ops; #endif diff --git a/sound/soc/sof/amd/renoir.c b/sound/soc/sof/amd/renoir.c index bca80784b322..9d95ea66f867 100644 --- a/sound/soc/sof/amd/renoir.c +++ b/sound/soc/sof/amd/renoir.c @@ -41,6 +41,14 @@ const struct snd_sof_dsp_ops sof_renoir_ops = { /* DSP core boot */ .run = acp_sof_dsp_run, + + /*IPC */ + .send_msg = acp_sof_ipc_send_msg, + .ipc_msg_data = acp_sof_ipc_msg_data, + .ipc_pcm_params = acp_sof_ipc_pcm_params, + .get_mailbox_offset = acp_sof_ipc_get_mailbox_offset, + .irq_thread = acp_sof_ipc_irq_thread, + .fw_ready = sof_fw_ready, }; EXPORT_SYMBOL(sof_renoir_ops); -- cgit v1.2.3 From bda93076d184ad80a8cab09bf29ace7692de18f7 Mon Sep 17 00:00:00 2001 From: Ajit Kumar Pandey Date: Wed, 17 Nov 2021 11:37:18 +0200 Subject: ASoC: SOF: amd: Add dai driver dsp ops callback for Renoir Add dsp ops callback to register I2S and DMIC sof dai's with ALSA Signed-off-by: Ajit Kumar Pandey Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Signed-off-by: Daniel Baluta Link: https://lore.kernel.org/r/20211117093734.17407-6-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp-dsp-offset.h | 2 + sound/soc/sof/amd/renoir.c | 91 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/sound/soc/sof/amd/acp-dsp-offset.h b/sound/soc/sof/amd/acp-dsp-offset.h index 3a1c848020ca..1d11e9d69dce 100644 --- a/sound/soc/sof/amd/acp-dsp-offset.h +++ b/sound/soc/sof/amd/acp-dsp-offset.h @@ -47,6 +47,8 @@ #define ACPAXI2AXI_ATU_CTRL 0xC40 #define ACP_SOFT_RESET 0x1000 +#define ACP_I2S_PIN_CONFIG 0x1400 + /* Registers from ACP_PGFSM block */ #define ACP_PGFSM_CONTROL 0x141C #define ACP_PGFSM_STATUS 0x1420 diff --git a/sound/soc/sof/amd/renoir.c b/sound/soc/sof/amd/renoir.c index 9d95ea66f867..ca5582b3f82d 100644 --- a/sound/soc/sof/amd/renoir.c +++ b/sound/soc/sof/amd/renoir.c @@ -15,7 +15,94 @@ #include #include "../ops.h" +#include "../sof-audio.h" #include "acp.h" +#include "acp-dsp-offset.h" + +#define I2S_BT_INSTANCE 0 +#define I2S_SP_INSTANCE 1 +#define PDM_DMIC_INSTANCE 2 + +#define I2S_MODE 0x04 + +static int renoir_dai_probe(struct snd_soc_dai *dai) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component); + unsigned int val; + + val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_I2S_PIN_CONFIG); + if (val != I2S_MODE) { + dev_err(sdev->dev, "I2S Mode is not supported (I2S_PIN_CONFIG: %#x)\n", val); + return -EINVAL; + } + + return 0; +} + +static struct snd_soc_dai_driver renoir_sof_dai[] = { + [I2S_BT_INSTANCE] = { + .id = I2S_BT_INSTANCE, + .name = "acp-sof-bt", + .playback = { + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 96000, + }, + .capture = { + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + /* Supporting only stereo for I2S BT controller capture */ + .channels_min = 2, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .probe = &renoir_dai_probe, + }, + + [I2S_SP_INSTANCE] = { + .id = I2S_SP_INSTANCE, + .name = "acp-sof-sp", + .playback = { + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 96000, + }, + .capture = { + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + /* Supporting only stereo for I2S SP controller capture */ + .channels_min = 2, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .probe = &renoir_dai_probe, + }, + + [PDM_DMIC_INSTANCE] = { + .id = PDM_DMIC_INSTANCE, + .name = "acp-sof-dmic", + .capture = { + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 4, + .rate_min = 8000, + .rate_max = 48000, + }, + }, +}; /* AMD Renoir DSP ops */ const struct snd_sof_dsp_ops sof_renoir_ops = { @@ -49,6 +136,10 @@ const struct snd_sof_dsp_ops sof_renoir_ops = { .get_mailbox_offset = acp_sof_ipc_get_mailbox_offset, .irq_thread = acp_sof_ipc_irq_thread, .fw_ready = sof_fw_ready, + + /* DAI drivers */ + .drv = renoir_sof_dai, + .num_drv = ARRAY_SIZE(renoir_sof_dai), }; EXPORT_SYMBOL(sof_renoir_ops); -- cgit v1.2.3 From e8afccf8fb75bae9c3292a0e51593af92839415e Mon Sep 17 00:00:00 2001 From: Ajit Kumar Pandey Date: Wed, 17 Nov 2021 11:37:19 +0200 Subject: ASoC: SOF: amd: Add PCM stream callback for Renoir dai's Add module to support ALSA pcm stream configurations for ACP I2S and DMIC endpoints Signed-off-by: Ajit Kumar Pandey Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Signed-off-by: Daniel Baluta Link: https://lore.kernel.org/r/20211117093734.17407-7-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/Makefile | 2 +- sound/soc/sof/amd/acp-pcm.c | 82 +++++++++++++++++++ sound/soc/sof/amd/acp-stream.c | 181 +++++++++++++++++++++++++++++++++++++++++ sound/soc/sof/amd/acp.c | 2 + sound/soc/sof/amd/acp.h | 28 +++++++ sound/soc/sof/amd/renoir.c | 11 +++ 6 files changed, 305 insertions(+), 1 deletion(-) create mode 100644 sound/soc/sof/amd/acp-pcm.c create mode 100644 sound/soc/sof/amd/acp-stream.c diff --git a/sound/soc/sof/amd/Makefile b/sound/soc/sof/amd/Makefile index 29928b16002f..7b88db9c5fb7 100644 --- a/sound/soc/sof/amd/Makefile +++ b/sound/soc/sof/amd/Makefile @@ -4,7 +4,7 @@ # # Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved. -snd-sof-amd-acp-objs := acp.o acp-loader.o acp-ipc.o +snd-sof-amd-acp-objs := acp.o acp-loader.o acp-ipc.o acp-pcm.o acp-stream.o snd-sof-amd-renoir-objs := renoir.o obj-$(CONFIG_SND_SOC_SOF_AMD_COMMON) += snd-sof-amd-acp.o diff --git a/sound/soc/sof/amd/acp-pcm.c b/sound/soc/sof/amd/acp-pcm.c new file mode 100644 index 000000000000..5b23830cb1f3 --- /dev/null +++ b/sound/soc/sof/amd/acp-pcm.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2021 Advanced Micro Devices, Inc. +// +// Authors: Ajit Kumar Pandey + +/* + * PCM interface for generic AMD audio ACP DSP block + */ +#include + +#include "../ops.h" +#include "acp.h" +#include "acp-dsp-offset.h" + +int acp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct sof_ipc_stream_params *ipc_params) +{ + struct acp_dsp_stream *stream = substream->runtime->private_data; + unsigned int buf_offset, index; + u32 size; + int ret; + + size = ipc_params->buffer.size; + stream->num_pages = ipc_params->buffer.pages; + stream->dmab = substream->runtime->dma_buffer_p; + + ret = acp_dsp_stream_config(sdev, stream); + if (ret < 0) { + dev_err(sdev->dev, "stream configuration failed\n"); + return ret; + } + + ipc_params->buffer.phy_addr = stream->reg_offset; + ipc_params->stream_tag = stream->stream_tag; + + /* write buffer size of stream in scratch memory */ + + buf_offset = offsetof(struct scratch_reg_conf, buf_size); + index = stream->stream_tag - 1; + buf_offset = buf_offset + index * 4; + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + buf_offset, size); + + return 0; +} +EXPORT_SYMBOL_NS(acp_pcm_hw_params, SND_SOC_SOF_AMD_COMMON); + +int acp_pcm_open(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream) +{ + struct acp_dsp_stream *stream; + + stream = acp_dsp_stream_get(sdev, 0); + if (!stream) + return -ENODEV; + + substream->runtime->private_data = stream; + stream->substream = substream; + + return 0; +} +EXPORT_SYMBOL_NS(acp_pcm_open, SND_SOC_SOF_AMD_COMMON); + +int acp_pcm_close(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream) +{ + struct acp_dsp_stream *stream; + + stream = substream->runtime->private_data; + if (!stream) { + dev_err(sdev->dev, "No open stream\n"); + return -EINVAL; + } + + stream->substream = NULL; + substream->runtime->private_data = NULL; + + return acp_dsp_stream_put(sdev, stream); +} +EXPORT_SYMBOL_NS(acp_pcm_close, SND_SOC_SOF_AMD_COMMON); diff --git a/sound/soc/sof/amd/acp-stream.c b/sound/soc/sof/amd/acp-stream.c new file mode 100644 index 000000000000..f2837bfbdb20 --- /dev/null +++ b/sound/soc/sof/amd/acp-stream.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2021 Advanced Micro Devices, Inc. +// +// Authors: Ajit Kumar Pandey + +/* + * Hardware interface for generic AMD audio DSP ACP IP + */ + +#include "../ops.h" +#include "acp-dsp-offset.h" +#include "acp.h" + +#define PTE_GRP1_OFFSET 0x00000000 +#define PTE_GRP2_OFFSET 0x00800000 +#define PTE_GRP3_OFFSET 0x01000000 +#define PTE_GRP4_OFFSET 0x01800000 +#define PTE_GRP5_OFFSET 0x02000000 +#define PTE_GRP6_OFFSET 0x02800000 +#define PTE_GRP7_OFFSET 0x03000000 +#define PTE_GRP8_OFFSET 0x03800000 + +int acp_dsp_stream_config(struct snd_sof_dev *sdev, struct acp_dsp_stream *stream) +{ + unsigned int pte_reg, pte_size, phy_addr_offset, index; + int stream_tag = stream->stream_tag; + u32 low, high, offset, reg_val; + dma_addr_t addr; + int page_idx; + + switch (stream_tag) { + case 1: + pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_1; + pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1; + offset = offsetof(struct scratch_reg_conf, grp1_pte); + stream->reg_offset = PTE_GRP1_OFFSET; + break; + case 2: + pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_2; + pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2; + offset = offsetof(struct scratch_reg_conf, grp2_pte); + stream->reg_offset = PTE_GRP2_OFFSET; + break; + case 3: + pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_3; + pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_3; + offset = offsetof(struct scratch_reg_conf, grp3_pte); + stream->reg_offset = PTE_GRP3_OFFSET; + break; + case 4: + pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_4; + pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_4; + offset = offsetof(struct scratch_reg_conf, grp4_pte); + stream->reg_offset = PTE_GRP4_OFFSET; + break; + case 5: + pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_5; + pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5; + offset = offsetof(struct scratch_reg_conf, grp5_pte); + stream->reg_offset = PTE_GRP5_OFFSET; + break; + case 6: + pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_6; + pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_6; + offset = offsetof(struct scratch_reg_conf, grp6_pte); + stream->reg_offset = PTE_GRP6_OFFSET; + break; + case 7: + pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_7; + pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_7; + offset = offsetof(struct scratch_reg_conf, grp7_pte); + stream->reg_offset = PTE_GRP7_OFFSET; + break; + case 8: + pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_8; + pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_8; + offset = offsetof(struct scratch_reg_conf, grp8_pte); + stream->reg_offset = PTE_GRP8_OFFSET; + break; + default: + dev_err(sdev->dev, "Invalid stream tag %d\n", stream_tag); + return -EINVAL; + } + + /* write phy_addr in scratch memory */ + + phy_addr_offset = offsetof(struct scratch_reg_conf, reg_offset); + index = stream_tag - 1; + phy_addr_offset = phy_addr_offset + index * 4; + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + + phy_addr_offset, stream->reg_offset); + + /* Group Enable */ + reg_val = ACP_SRAM_PTE_OFFSET + offset; + snd_sof_dsp_write(sdev, ACP_DSP_BAR, pte_reg, reg_val | BIT(31)); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, pte_size, PAGE_SIZE_4K_ENABLE); + + for (page_idx = 0; page_idx < stream->num_pages; page_idx++) { + addr = snd_sgbuf_get_addr(stream->dmab, page_idx * PAGE_SIZE); + + /* Load the low address of page int ACP SRAM through SRBM */ + low = lower_32_bits(addr); + high = upper_32_bits(addr); + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + offset, low); + + high |= BIT(31); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + offset + 4, high); + /* Move to next physically contiguous page */ + offset += 8; + } + + return 0; +} + +struct acp_dsp_stream *acp_dsp_stream_get(struct snd_sof_dev *sdev, int tag) +{ + struct acp_dev_data *adata = sdev->pdata->hw_pdata; + struct acp_dsp_stream *stream = adata->stream_buf; + int i; + + for (i = 0; i < ACP_MAX_STREAM; i++, stream++) { + if (stream->active) + continue; + + /* return stream if tag not specified*/ + if (!tag) { + stream->active = 1; + return stream; + } + + /* check if this is the requested stream tag */ + if (stream->stream_tag == tag) { + stream->active = 1; + return stream; + } + } + + dev_err(sdev->dev, "stream %d active or no inactive stream\n", tag); + return NULL; +} +EXPORT_SYMBOL_NS(acp_dsp_stream_get, SND_SOC_SOF_AMD_COMMON); + +int acp_dsp_stream_put(struct snd_sof_dev *sdev, + struct acp_dsp_stream *acp_stream) +{ + struct acp_dev_data *adata = sdev->pdata->hw_pdata; + struct acp_dsp_stream *stream = adata->stream_buf; + int i; + + /* Free an active stream */ + for (i = 0; i < ACP_MAX_STREAM; i++, stream++) { + if (stream == acp_stream) { + stream->active = 0; + return 0; + } + } + + dev_err(sdev->dev, "Cannot find active stream tag %d\n", acp_stream->stream_tag); + return -EINVAL; +} +EXPORT_SYMBOL_NS(acp_dsp_stream_put, SND_SOC_SOF_AMD_COMMON); + +int acp_dsp_stream_init(struct snd_sof_dev *sdev) +{ + struct acp_dev_data *adata = sdev->pdata->hw_pdata; + int i; + + for (i = 0; i < ACP_MAX_STREAM; i++) { + adata->stream_buf[i].sdev = sdev; + adata->stream_buf[i].active = 0; + adata->stream_buf[i].stream_tag = i + 1; + } + return 0; +} +EXPORT_SYMBOL_NS(acp_dsp_stream_init, SND_SOC_SOF_AMD_COMMON); diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index 43a57d15e3ca..74ede28aa8d8 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -363,6 +363,8 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev) acp_memory_init(sdev); + acp_dsp_stream_init(sdev); + return 0; } EXPORT_SYMBOL_NS(amd_sof_acp_probe, SND_SOC_SOF_AMD_COMMON); diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index ac8340119125..36d000c3f792 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -13,6 +13,8 @@ #include "../sof-priv.h" +#define ACP_MAX_STREAM 8 + #define ACP_DSP_BAR 0 #define ACP_REG_POLL_INTERVAL 500 @@ -114,6 +116,17 @@ struct scratch_reg_conf { unsigned int reserve[]; }; +struct acp_dsp_stream { + struct list_head list; + struct snd_sof_dev *sdev; + struct snd_pcm_substream *substream; + struct snd_dma_buffer *dmab; + int num_pages; + int stream_tag; + int active; + unsigned int reg_offset; +}; + /* Common device data struct for ACP devices */ struct acp_dev_data { struct snd_sof_dev *dev; @@ -125,6 +138,7 @@ struct acp_dev_data { dma_addr_t dma_addr; u8 *data_buf; struct dma_descriptor dscr_info[ACP_MAX_DESC]; + struct acp_dsp_stream stream_buf[ACP_MAX_STREAM]; }; void memcpy_to_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *src, size_t bytes); @@ -165,5 +179,19 @@ int acp_sof_ipc_pcm_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *s void acp_mailbox_write(struct snd_sof_dev *sdev, u32 offset, void *message, size_t bytes); void acp_mailbox_read(struct snd_sof_dev *sdev, u32 offset, void *message, size_t bytes); +/* ACP - DSP stream callbacks */ +int acp_dsp_stream_config(struct snd_sof_dev *sdev, struct acp_dsp_stream *stream); +int acp_dsp_stream_init(struct snd_sof_dev *sdev); +struct acp_dsp_stream *acp_dsp_stream_get(struct snd_sof_dev *sdev, int tag); +int acp_dsp_stream_put(struct snd_sof_dev *sdev, struct acp_dsp_stream *acp_stream); + +/* + * DSP PCM Operations. + */ +int acp_pcm_open(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); +int acp_pcm_close(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); +int acp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct sof_ipc_stream_params *ipc_params); + extern const struct snd_sof_dsp_ops sof_renoir_ops; #endif diff --git a/sound/soc/sof/amd/renoir.c b/sound/soc/sof/amd/renoir.c index ca5582b3f82d..0241c5dce156 100644 --- a/sound/soc/sof/amd/renoir.c +++ b/sound/soc/sof/amd/renoir.c @@ -140,6 +140,17 @@ const struct snd_sof_dsp_ops sof_renoir_ops = { /* DAI drivers */ .drv = renoir_sof_dai, .num_drv = ARRAY_SIZE(renoir_sof_dai), + + /* stream callbacks */ + .pcm_open = acp_pcm_open, + .pcm_close = acp_pcm_close, + .pcm_hw_params = acp_pcm_hw_params, + + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, }; EXPORT_SYMBOL(sof_renoir_ops); -- cgit v1.2.3 From f1bdd8d385a803565024c8feeedc17bf86aac4f5 Mon Sep 17 00:00:00 2001 From: Ajit Kumar Pandey Date: Wed, 17 Nov 2021 11:37:20 +0200 Subject: ASoC: amd: Add module to determine ACP configuration ACP hw block configuration differs across various distributions and hence it's required to register different drivers module for distributions. For now we support three ACP drivers: * ACP without SOF use case * ACP with SOF use case * ACP with SOF use case for DMIC and non SOF for I2S endpoints As all above driver registers with common PCI ID for ACP hw block we need code to determine ACP configuration and auto select driver module. This patch expose function that return configuration flag based on dmi checks for a system. ACP driver module probe register platform device based on such configuration flag to avoid conflict with other ACP drivers probed for same PCI ID. Signed-off-by: Ajit Kumar Pandey Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Signed-off-by: Daniel Baluta Link: https://lore.kernel.org/r/20211117093734.17407-8-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/amd/Kconfig | 6 ++++ sound/soc/amd/Makefile | 2 ++ sound/soc/amd/acp-config.c | 81 +++++++++++++++++++++++++++++++++++++++++++++ sound/soc/amd/mach-config.h | 29 ++++++++++++++++ 4 files changed, 118 insertions(+) create mode 100644 sound/soc/amd/acp-config.c create mode 100644 sound/soc/amd/mach-config.h diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig index 2c6af3f8f296..092966ff5ea7 100644 --- a/sound/soc/amd/Kconfig +++ b/sound/soc/amd/Kconfig @@ -96,4 +96,10 @@ config SND_SOC_AMD_YC_MACH Say m if you have such a device. If unsure select "N". +config SND_AMD_ACP_CONFIG + tristate "AMD ACP configuration selection" + help + This option adds an auto detection to determine which ACP + driver modules to use + source "sound/soc/amd/acp/Kconfig" diff --git a/sound/soc/amd/Makefile b/sound/soc/amd/Makefile index f1d42bbda709..4b1f77930a4a 100644 --- a/sound/soc/amd/Makefile +++ b/sound/soc/amd/Makefile @@ -3,6 +3,7 @@ acp_audio_dma-objs := acp-pcm-dma.o snd-soc-acp-da7219mx98357-mach-objs := acp-da7219-max98357a.o snd-soc-acp-rt5645-mach-objs := acp-rt5645.o snd-soc-acp-rt5682-mach-objs := acp3x-rt5682-max9836.o +snd-acp-config-objs := acp-config.o obj-$(CONFIG_SND_SOC_AMD_ACP) += acp_audio_dma.o obj-$(CONFIG_SND_SOC_AMD_CZ_DA7219MX98357_MACH) += snd-soc-acp-da7219mx98357-mach.o @@ -13,3 +14,4 @@ obj-$(CONFIG_SND_SOC_AMD_RENOIR) += renoir/ obj-$(CONFIG_SND_SOC_AMD_ACP5x) += vangogh/ obj-$(CONFIG_SND_SOC_AMD_ACP6x) += yc/ obj-$(CONFIG_SND_SOC_AMD_ACP_COMMON) += acp/ +obj-$(CONFIG_SND_AMD_ACP_CONFIG) += snd-acp-config.o diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c new file mode 100644 index 000000000000..c9abbb46b6f5 --- /dev/null +++ b/sound/soc/amd/acp-config.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2021 Advanced Micro Devices, Inc. +// +// Authors: Ajit Kumar Pandey +// + +/* ACP machine configuration module */ + +#include +#include +#include +#include +#include + +#include "../sof/amd/acp.h" +#include "mach-config.h" + +static int acp_quirk_data; + +static const struct config_entry config_table[] = { + { + .flags = FLAG_AMD_SOF, + .device = ACP_PCI_DEV_ID, + .dmi_table = (const struct dmi_system_id []) { + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "AMD"), + DMI_MATCH(DMI_PRODUCT_NAME, "Majolica-CZN"), + }, + }, + {} + }, + }, +}; + +int snd_amd_acp_find_config(struct pci_dev *pci) +{ + const struct config_entry *table = config_table; + u16 device = pci->device; + int i; + + for (i = 0; i < ARRAY_SIZE(config_table); i++, table++) { + if (table->device != device) + continue; + if (table->dmi_table && !dmi_check_system(table->dmi_table)) + continue; + acp_quirk_data = table->flags; + return table->flags; + } + + return 0; +} +EXPORT_SYMBOL(snd_amd_acp_find_config); + +struct snd_soc_acpi_mach snd_soc_acpi_amd_sof_machines[] = { + { + .id = "AMDI1019", + .drv_name = "renoir-dsp", + .pdata = (void *)&acp_quirk_data, + .fw_filename = "sof-rn.ri", + .sof_tplg_filename = "sof-acp.tplg", + }, + {}, +}; +EXPORT_SYMBOL(snd_soc_acpi_amd_sof_machines); + +struct snd_soc_acpi_mach snd_soc_acpi_amd_acp_machines[] = { + { + .id = "AMDI1019", + .drv_name = "renoir-acp", + .pdata = (void *)&acp_quirk_data, + }, + {}, +}; +EXPORT_SYMBOL(snd_soc_acpi_amd_acp_machines); + +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/amd/mach-config.h b/sound/soc/amd/mach-config.h new file mode 100644 index 000000000000..608f1e199775 --- /dev/null +++ b/sound/soc/amd/mach-config.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved. + * + * Author: Ajit Kumar Pandey + */ +#ifndef __AMD_MACH_CONFIG_H +#define __AMD_MACH_CONFIG_H + +#include + +#define FLAG_AMD_SOF BIT(1) +#define FLAG_AMD_SOF_ONLY_DMIC BIT(2) + +#define ACP_PCI_DEV_ID 0x15E2 + +extern struct snd_soc_acpi_mach snd_soc_acpi_amd_sof_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp_machines[]; + +struct config_entry { + u32 flags; + u16 device; + const struct dmi_system_id *dmi_table; +}; + +#endif -- cgit v1.2.3 From 11ddd4e371810017faf7ff7cb2349f321e50d1d3 Mon Sep 17 00:00:00 2001 From: Ajit Kumar Pandey Date: Wed, 17 Nov 2021 11:37:21 +0200 Subject: ASoC: SOF: amd: Add machine driver dsp ops for Renoir platform Add dsp ops callback to select and register machine driver. Signed-off-by: Ajit Kumar Pandey Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Signed-off-by: Daniel Baluta Link: https://lore.kernel.org/r/20211117093734.17407-9-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/renoir.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/sound/soc/sof/amd/renoir.c b/sound/soc/sof/amd/renoir.c index 0241c5dce156..3cd269bfe75d 100644 --- a/sound/soc/sof/amd/renoir.c +++ b/sound/soc/sof/amd/renoir.c @@ -104,6 +104,23 @@ static struct snd_soc_dai_driver renoir_sof_dai[] = { }, }; +static void amd_sof_machine_select(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *sof_pdata = sdev->pdata; + const struct sof_dev_desc *desc = sof_pdata->desc; + struct snd_soc_acpi_mach *mach; + + mach = snd_soc_acpi_find_machine(desc->machines); + if (!mach) { + dev_warn(sdev->dev, "No matching ASoC machine driver found\n"); + return; + } + + sof_pdata->tplg_filename = mach->sof_tplg_filename; + sof_pdata->fw_filename = mach->fw_filename; + sof_pdata->machine = mach; +} + /* AMD Renoir DSP ops */ const struct snd_sof_dsp_ops sof_renoir_ops = { /* probe and remove */ @@ -151,6 +168,11 @@ const struct snd_sof_dsp_ops sof_renoir_ops = { SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, + + /* Machine driver callbacks */ + .machine_select = amd_sof_machine_select, + .machine_register = sof_machine_register, + .machine_unregister = sof_machine_unregister, }; EXPORT_SYMBOL(sof_renoir_ops); -- cgit v1.2.3 From ec25a3b14261fcb05568a1fec15ca68152e9d208 Mon Sep 17 00:00:00 2001 From: Ajit Kumar Pandey Date: Wed, 17 Nov 2021 11:37:22 +0200 Subject: ASoC: SOF: amd: Add Renoir PCI driver interface Add PCI driver module to enable sof pci device support for Renoir. If machine flag set to FLAG_SOF_ONLY_DMIC this pci driver register platform device for non dsp based I2S platform device. If machine flag is not enabled for SOF pci probe will return without invoking sof device probe and registration Signed-off-by: Ajit Kumar Pandey Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Signed-off-by: Daniel Baluta Link: https://lore.kernel.org/r/20211117093734.17407-10-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/Kconfig | 5 +- sound/soc/sof/amd/Makefile | 2 +- sound/soc/sof/amd/acp.h | 3 + sound/soc/sof/amd/pci-rn.c | 160 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 168 insertions(+), 2 deletions(-) create mode 100644 sound/soc/sof/amd/pci-rn.c diff --git a/sound/soc/sof/amd/Kconfig b/sound/soc/sof/amd/Kconfig index 400dd5a24ae6..085232e04582 100644 --- a/sound/soc/sof/amd/Kconfig +++ b/sound/soc/sof/amd/Kconfig @@ -17,14 +17,17 @@ if SND_SOC_SOF_AMD_TOPLEVEL config SND_SOC_SOF_AMD_COMMON tristate select SND_SOC_SOF + select SND_SOC_SOF_PCI_DEV + select SND_AMD_ACP_CONFIG + select SND_SOC_ACPI if ACPI help This option is not user-selectable but automatically handled by 'select' statements at a higher level config SND_SOC_SOF_AMD_RENOIR tristate "SOF support for RENOIR" + depends on SND_SOC_SOF_PCI select SND_SOC_SOF_AMD_COMMON help Select this option for SOF support on AMD Renoir platform - endif diff --git a/sound/soc/sof/amd/Makefile b/sound/soc/sof/amd/Makefile index 7b88db9c5fb7..b27ce50014b8 100644 --- a/sound/soc/sof/amd/Makefile +++ b/sound/soc/sof/amd/Makefile @@ -5,7 +5,7 @@ # Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved. snd-sof-amd-acp-objs := acp.o acp-loader.o acp-ipc.o acp-pcm.o acp-stream.o -snd-sof-amd-renoir-objs := renoir.o +snd-sof-amd-renoir-objs := pci-rn.o renoir.o obj-$(CONFIG_SND_SOC_SOF_AMD_COMMON) += snd-sof-amd-acp.o obj-$(CONFIG_SND_SOC_SOF_AMD_RENOIR) +=snd-sof-amd-renoir.o diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index 36d000c3f792..5f6e9eff116a 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -194,4 +194,7 @@ int acp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substr struct snd_pcm_hw_params *params, struct sof_ipc_stream_params *ipc_params); extern const struct snd_sof_dsp_ops sof_renoir_ops; + +/* Machine configuration */ +int snd_amd_acp_find_config(struct pci_dev *pci); #endif diff --git a/sound/soc/sof/amd/pci-rn.c b/sound/soc/sof/amd/pci-rn.c new file mode 100644 index 000000000000..3c379a5ef231 --- /dev/null +++ b/sound/soc/sof/amd/pci-rn.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved. +// +// Authors: Ajit Kumar Pandey + +/* + * PCI interface for Renoir ACP device + */ + +#include +#include +#include +#include +#include + +#include "../ops.h" +#include "../sof-pci-dev.h" +#include "../../amd/mach-config.h" +#include "acp.h" + +#define ACP3x_REG_START 0x1240000 +#define ACP3x_REG_END 0x125C000 + +static struct platform_device *dmic_dev; +static struct platform_device *pdev; + +static const struct resource renoir_res[] = { + { + .start = 0, + .end = ACP3x_REG_END - ACP3x_REG_START, + .name = "acp_mem", + .flags = IORESOURCE_MEM, + }, + { + .start = 0, + .end = 0, + .name = "acp_dai_irq", + .flags = IORESOURCE_IRQ, + }, +}; + +static const struct sof_dev_desc renoir_desc = { + .machines = snd_soc_acpi_amd_sof_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = -1, + .resindex_imr_base = -1, + .irqindex_host_ipc = -1, + .default_fw_path = "amd/sof", + .default_tplg_path = "amd/sof-tplg", + .default_fw_filename = "sof-rn.ri", + .nocodec_tplg_filename = "sof-acp.tplg", + .ops = &sof_renoir_ops, +}; + +static int acp_pci_rn_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) +{ + struct platform_device_info pdevinfo; + struct device *dev = &pci->dev; + const struct resource *res_i2s; + struct resource *res; + unsigned int flag, i, addr; + int ret; + + flag = snd_amd_acp_find_config(pci); + if (flag != FLAG_AMD_SOF && flag != FLAG_AMD_SOF_ONLY_DMIC) + return -ENODEV; + + ret = sof_pci_probe(pci, pci_id); + if (ret != 0) + return ret; + + dmic_dev = platform_device_register_data(dev, "dmic-codec", PLATFORM_DEVID_NONE, NULL, 0); + if (IS_ERR(dmic_dev)) { + dev_err(dev, "failed to create DMIC device\n"); + sof_pci_remove(pci); + return PTR_ERR(dmic_dev); + } + + /* Register platform device only if flag set to FLAG_AMD_SOF_ONLY_DMIC */ + if (flag != FLAG_AMD_SOF_ONLY_DMIC) + return 0; + + addr = pci_resource_start(pci, 0); + res = devm_kzalloc(&pci->dev, sizeof(struct resource) * ARRAY_SIZE(renoir_res), GFP_KERNEL); + if (!res) { + sof_pci_remove(pci); + return -ENOMEM; + } + + res_i2s = renoir_res; + for (i = 0; i < ARRAY_SIZE(renoir_res); i++, res_i2s++) { + res[i].name = res_i2s->name; + res[i].flags = res_i2s->flags; + res[i].start = addr + res_i2s->start; + res[i].end = addr + res_i2s->end; + if (res_i2s->flags == IORESOURCE_IRQ) { + res[i].start = pci->irq; + res[i].end = res[i].start; + } + } + + memset(&pdevinfo, 0, sizeof(pdevinfo)); + + /* + * We have common PCI driver probe for ACP device but we have to support I2S without SOF + * for some distributions. Register platform device that will be used to support non dsp + * ACP's audio ends points on some machines. + */ + + pdevinfo.name = "acp_asoc_renoir"; + pdevinfo.id = 0; + pdevinfo.parent = &pci->dev; + pdevinfo.num_res = ARRAY_SIZE(renoir_res); + pdevinfo.res = &res[0]; + + pdev = platform_device_register_full(&pdevinfo); + if (IS_ERR(pdev)) { + dev_err(&pci->dev, "cannot register %s device\n", pdevinfo.name); + sof_pci_remove(pci); + platform_device_unregister(dmic_dev); + ret = PTR_ERR(pdev); + } + + return ret; +}; + +static void acp_pci_rn_remove(struct pci_dev *pci) +{ + if (dmic_dev) + platform_device_unregister(dmic_dev); + if (pdev) + platform_device_unregister(pdev); + + return sof_pci_remove(pci); +} + +/* PCI IDs */ +static const struct pci_device_id rn_pci_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_PCI_DEV_ID), + .driver_data = (unsigned long)&renoir_desc}, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, rn_pci_ids); + +/* pci_driver definition */ +static struct pci_driver snd_sof_pci_amd_rn_driver = { + .name = KBUILD_MODNAME, + .id_table = rn_pci_ids, + .probe = acp_pci_rn_probe, + .remove = acp_pci_rn_remove, +}; +module_pci_driver(snd_sof_pci_amd_rn_driver); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_IMPORT_NS(SND_SOC_SOF_AMD_COMMON); +MODULE_IMPORT_NS(SND_SOC_SOF_PCI_DEV); -- cgit v1.2.3 From 63fba90fc88b6cee9f8bead761a419169ecda6cc Mon Sep 17 00:00:00 2001 From: Ajit Kumar Pandey Date: Wed, 17 Nov 2021 11:37:23 +0200 Subject: ASoC: amd: acp-config: Remove legacy acpi based machine struct We have moved legacy based machine struct into platform driver to resolve module dependency with non-SOF ALSA build, hence removed it from acp-config driver module. Signed-off-by: Ajit Kumar Pandey Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Signed-off-by: Daniel Baluta Link: https://lore.kernel.org/r/20211117093734.17407-11-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/amd/acp-config.c | 10 ---------- sound/soc/amd/mach-config.h | 1 - 2 files changed, 11 deletions(-) diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c index c9abbb46b6f5..1493d52c9290 100644 --- a/sound/soc/amd/acp-config.c +++ b/sound/soc/amd/acp-config.c @@ -68,14 +68,4 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_sof_machines[] = { }; EXPORT_SYMBOL(snd_soc_acpi_amd_sof_machines); -struct snd_soc_acpi_mach snd_soc_acpi_amd_acp_machines[] = { - { - .id = "AMDI1019", - .drv_name = "renoir-acp", - .pdata = (void *)&acp_quirk_data, - }, - {}, -}; -EXPORT_SYMBOL(snd_soc_acpi_amd_acp_machines); - MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/amd/mach-config.h b/sound/soc/amd/mach-config.h index 608f1e199775..feb3756d9ac4 100644 --- a/sound/soc/amd/mach-config.h +++ b/sound/soc/amd/mach-config.h @@ -18,7 +18,6 @@ #define ACP_PCI_DEV_ID 0x15E2 extern struct snd_soc_acpi_mach snd_soc_acpi_amd_sof_machines[]; -extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp_machines[]; struct config_entry { u32 flags; -- cgit v1.2.3 From efb931cdc4b94a0f7ed17a76844f08cef1bdffe5 Mon Sep 17 00:00:00 2001 From: Ajit Kumar Pandey Date: Wed, 17 Nov 2021 11:37:24 +0200 Subject: ASoC: SOF: topology: Add support for AMD ACP DAIs Add new sof dais and config to pass topology file configuration to SOF firmware running on ACP's DSP core. ACP firmware support I2S_BT, I2S_SP and DMIC controller hence add three new dais to the list of supported sof_dais Signed-off-by: Ajit Kumar Pandey Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Signed-off-by: Daniel Baluta Link: https://lore.kernel.org/r/20211117093734.17407-12-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- include/sound/sof/dai-amd.h | 21 +++++++++ include/sound/sof/dai.h | 7 +++ sound/soc/sof/pcm.c | 36 +++++++++++++++ sound/soc/sof/topology.c | 109 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 173 insertions(+) create mode 100644 include/sound/sof/dai-amd.h diff --git a/include/sound/sof/dai-amd.h b/include/sound/sof/dai-amd.h new file mode 100644 index 000000000000..90d09dbdd709 --- /dev/null +++ b/include/sound/sof/dai-amd.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2021 Advanced Micro Devices, Inc.. All rights reserved. + */ + +#ifndef __INCLUDE_SOUND_SOF_DAI_AMD_H__ +#define __INCLUDE_SOUND_SOF_DAI_AMD_H__ + +#include + +/* ACP Configuration Request - SOF_IPC_DAI_AMD_CONFIG */ +struct sof_ipc_dai_acp_params { + struct sof_ipc_hdr hdr; + + uint32_t fsync_rate; /* FSYNC frequency in Hz */ + uint32_t tdm_slots; +} __packed; +#endif diff --git a/include/sound/sof/dai.h b/include/sound/sof/dai.h index 9625f47557b8..3782127a7095 100644 --- a/include/sound/sof/dai.h +++ b/include/sound/sof/dai.h @@ -12,6 +12,7 @@ #include #include #include +#include /* * DAI Configuration. @@ -66,6 +67,9 @@ enum sof_ipc_dai_type { SOF_DAI_INTEL_ALH, /**< Intel ALH */ SOF_DAI_IMX_SAI, /**< i.MX SAI */ SOF_DAI_IMX_ESAI, /**< i.MX ESAI */ + SOF_DAI_AMD_BT, /**< AMD ACP BT*/ + SOF_DAI_AMD_SP, /**< AMD ACP SP */ + SOF_DAI_AMD_DMIC, /**< AMD ACP DMIC */ }; /* general purpose DAI configuration */ @@ -90,6 +94,9 @@ struct sof_ipc_dai_config { struct sof_ipc_dai_alh_params alh; struct sof_ipc_dai_esai_params esai; struct sof_ipc_dai_sai_params sai; + struct sof_ipc_dai_acp_params acpbt; + struct sof_ipc_dai_acp_params acpsp; + struct sof_ipc_dai_acp_params acpdmic; }; } __packed; diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index fa0bfcd2474e..8d313c9862cb 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -826,6 +826,42 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa "channels_min: %d channels_max: %d\n", channels->min, channels->max); break; + case SOF_DAI_AMD_BT: + rate->min = dai->dai_config->acpbt.fsync_rate; + rate->max = dai->dai_config->acpbt.fsync_rate; + channels->min = dai->dai_config->acpbt.tdm_slots; + channels->max = dai->dai_config->acpbt.tdm_slots; + + dev_dbg(component->dev, + "AMD_BT rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, + "AMD_BT channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; + case SOF_DAI_AMD_SP: + rate->min = dai->dai_config->acpsp.fsync_rate; + rate->max = dai->dai_config->acpsp.fsync_rate; + channels->min = dai->dai_config->acpsp.tdm_slots; + channels->max = dai->dai_config->acpsp.tdm_slots; + + dev_dbg(component->dev, + "AMD_SP rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, + "AMD_SP channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; + case SOF_DAI_AMD_DMIC: + rate->min = dai->dai_config->acpdmic.fsync_rate; + rate->max = dai->dai_config->acpdmic.fsync_rate; + channels->min = dai->dai_config->acpdmic.tdm_slots; + channels->max = dai->dai_config->acpdmic.tdm_slots; + + dev_dbg(component->dev, + "AMD_DMIC rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, + "AMD_DMIC channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; default: dev_err(component->dev, "error: invalid DAI type %d\n", dai->dai_config->type); diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index bb9e62bbe5db..72e671c15a34 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -376,6 +376,9 @@ static const struct sof_dai_types sof_dais[] = { {"ALH", SOF_DAI_INTEL_ALH}, {"SAI", SOF_DAI_IMX_SAI}, {"ESAI", SOF_DAI_IMX_ESAI}, + {"ACP", SOF_DAI_AMD_BT}, + {"ACPSP", SOF_DAI_AMD_SP}, + {"ACPDMIC", SOF_DAI_AMD_DMIC}, }; static enum sof_ipc_dai_type find_dai(const char *name) @@ -2992,6 +2995,102 @@ static int sof_link_esai_load(struct snd_soc_component *scomp, int index, return ret; } +static int sof_link_acp_dmic_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg, + struct snd_soc_tplg_hw_config *hw_config, + struct sof_ipc_dai_config *config) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + u32 size = sizeof(*config); + int ret; + + /* handle master/slave and inverted clocks */ + sof_dai_set_format(hw_config, config); + + /* init IPC */ + memset(&config->acpdmic, 0, sizeof(struct sof_ipc_dai_acp_params)); + config->hdr.size = size; + + config->acpdmic.fsync_rate = le32_to_cpu(hw_config->fsync_rate); + config->acpdmic.tdm_slots = le32_to_cpu(hw_config->tdm_slots); + + dev_info(scomp->dev, "ACP_DMIC config ACP%d channel %d rate %d\n", + config->dai_index, config->acpdmic.tdm_slots, + config->acpdmic.fsync_rate); + + /* set config for all DAI's with name matching the link name */ + ret = sof_set_dai_config(sdev, size, link, config); + if (ret < 0) + dev_err(scomp->dev, "ACP_DMIC failed to save DAI config for ACP%d\n", + config->dai_index); + return ret; +} + +static int sof_link_acp_bt_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg, + struct snd_soc_tplg_hw_config *hw_config, + struct sof_ipc_dai_config *config) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + u32 size = sizeof(*config); + int ret; + + /* handle master/slave and inverted clocks */ + sof_dai_set_format(hw_config, config); + + /* init IPC */ + memset(&config->acpbt, 0, sizeof(struct sof_ipc_dai_acp_params)); + config->hdr.size = size; + + config->acpbt.fsync_rate = le32_to_cpu(hw_config->fsync_rate); + config->acpbt.tdm_slots = le32_to_cpu(hw_config->tdm_slots); + + dev_info(scomp->dev, "ACP_BT config ACP%d channel %d rate %d\n", + config->dai_index, config->acpbt.tdm_slots, + config->acpbt.fsync_rate); + + /* set config for all DAI's with name matching the link name */ + ret = sof_set_dai_config(sdev, size, link, config); + if (ret < 0) + dev_err(scomp->dev, "ACP_BT failed to save DAI config for ACP%d\n", + config->dai_index); + return ret; +} + +static int sof_link_acp_sp_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg, + struct snd_soc_tplg_hw_config *hw_config, + struct sof_ipc_dai_config *config) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + u32 size = sizeof(*config); + int ret; + + /* handle master/slave and inverted clocks */ + sof_dai_set_format(hw_config, config); + + /* init IPC */ + memset(&config->acpsp, 0, sizeof(struct sof_ipc_dai_acp_params)); + config->hdr.size = size; + + config->acpsp.fsync_rate = le32_to_cpu(hw_config->fsync_rate); + config->acpsp.tdm_slots = le32_to_cpu(hw_config->tdm_slots); + + dev_info(scomp->dev, "ACP_SP config ACP%d channel %d rate %d\n", + config->dai_index, config->acpsp.tdm_slots, + config->acpsp.fsync_rate); + + /* set config for all DAI's with name matching the link name */ + ret = sof_set_dai_config(sdev, size, link, config); + if (ret < 0) + dev_err(scomp->dev, "ACP_SP failed to save DAI config for ACP%d\n", + config->dai_index); + return ret; +} + static int sof_link_dmic_load(struct snd_soc_component *scomp, int index, struct snd_soc_dai_link *link, struct snd_soc_tplg_link_config *cfg, @@ -3277,6 +3376,16 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, case SOF_DAI_IMX_ESAI: ret = sof_link_esai_load(scomp, index, link, cfg, hw_config + curr_conf, config); break; + case SOF_DAI_AMD_BT: + ret = sof_link_acp_bt_load(scomp, index, link, cfg, hw_config + curr_conf, config); + break; + case SOF_DAI_AMD_SP: + ret = sof_link_acp_sp_load(scomp, index, link, cfg, hw_config + curr_conf, config); + break; + case SOF_DAI_AMD_DMIC: + ret = sof_link_acp_dmic_load(scomp, index, link, cfg, hw_config + curr_conf, + config); + break; default: dev_err(scomp->dev, "error: invalid DAI type %d\n", common_config.type); ret = -EINVAL; -- cgit v1.2.3 From 4627421fb883928af5220c66a304bed1f9b77e8d Mon Sep 17 00:00:00 2001 From: V sujith kumar Reddy Date: Wed, 17 Nov 2021 11:37:25 +0200 Subject: ASoC: SOF: amd: Add trace logger support Add trace support and configure trace stream for ACP firmware. Signed-off-by: Vishnuvardhanrao Ravuapati Signed-off-by: V sujith kumar Reddy Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Signed-off-by: Daniel Baluta Link: https://lore.kernel.org/r/20211117093734.17407-13-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/Makefile | 2 +- sound/soc/sof/amd/acp-trace.c | 84 +++++++++++++++++++++++++++++++++++++++++++ sound/soc/sof/amd/acp.h | 5 +++ sound/soc/sof/amd/renoir.c | 4 +++ 4 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 sound/soc/sof/amd/acp-trace.c diff --git a/sound/soc/sof/amd/Makefile b/sound/soc/sof/amd/Makefile index b27ce50014b8..7b9f1a0af3c8 100644 --- a/sound/soc/sof/amd/Makefile +++ b/sound/soc/sof/amd/Makefile @@ -4,7 +4,7 @@ # # Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved. -snd-sof-amd-acp-objs := acp.o acp-loader.o acp-ipc.o acp-pcm.o acp-stream.o +snd-sof-amd-acp-objs := acp.o acp-loader.o acp-ipc.o acp-pcm.o acp-stream.o acp-trace.o snd-sof-amd-renoir-objs := pci-rn.o renoir.o obj-$(CONFIG_SND_SOC_SOF_AMD_COMMON) += snd-sof-amd-acp.o diff --git a/sound/soc/sof/amd/acp-trace.c b/sound/soc/sof/amd/acp-trace.c new file mode 100644 index 000000000000..fa4da8947186 --- /dev/null +++ b/sound/soc/sof/amd/acp-trace.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved. +// +// Authors: Vishnuvardhanrao Ravuapati +// V Sujith Kumar Reddy + +/*This file support Host TRACE Logger driver callback for SOF FW */ + +#include "acp.h" + +#define ACP_LOGGER_STREAM 8 +#define NUM_PAGES 16 + +int acp_sof_trace_release(struct snd_sof_dev *sdev) +{ + struct acp_dsp_stream *stream; + struct acp_dev_data *adata; + int ret; + + adata = sdev->pdata->hw_pdata; + stream = adata->dtrace_stream; + ret = acp_dsp_stream_put(sdev, stream); + if (ret < 0) { + dev_err(sdev->dev, "Failed to release trace stream\n"); + return ret; + } + + adata->dtrace_stream = NULL; + return 0; +} +EXPORT_SYMBOL_NS(acp_sof_trace_release, SND_SOC_SOF_AMD_COMMON); + +static int acp_sof_trace_prepare(struct snd_sof_dev *sdev, + struct sof_ipc_dma_trace_params_ext *params) +{ + struct acp_dsp_stream *stream; + struct acp_dev_data *adata; + int ret; + + adata = sdev->pdata->hw_pdata; + stream = adata->dtrace_stream; + stream->dmab = &sdev->dmatb; + stream->num_pages = NUM_PAGES; + + ret = acp_dsp_stream_config(sdev, stream); + if (ret < 0) { + dev_err(sdev->dev, "Failed to configure trace stream\n"); + return ret; + } + + params->buffer.phy_addr = stream->reg_offset; + params->stream_tag = stream->stream_tag; + + return 0; +} + +int acp_sof_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag) +{ + struct sof_ipc_dma_trace_params_ext *params; + struct acp_dsp_stream *stream; + struct acp_dev_data *adata; + int ret; + + adata = sdev->pdata->hw_pdata; + stream = acp_dsp_stream_get(sdev, ACP_LOGGER_STREAM); + if (!stream) + return -ENODEV; + + adata->dtrace_stream = stream; + params = container_of(stream_tag, struct sof_ipc_dma_trace_params_ext, stream_tag); + ret = acp_sof_trace_prepare(sdev, params); + if (ret < 0) { + acp_dsp_stream_put(sdev, stream); + return ret; + } + + *stream_tag = stream->stream_tag; + return 0; +} +EXPORT_SYMBOL_NS(acp_sof_trace_init, SND_SOC_SOF_AMD_COMMON); diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index 5f6e9eff116a..fd923f72a01a 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -139,6 +139,7 @@ struct acp_dev_data { u8 *data_buf; struct dma_descriptor dscr_info[ACP_MAX_DESC]; struct acp_dsp_stream stream_buf[ACP_MAX_STREAM]; + struct acp_dsp_stream *dtrace_stream; }; void memcpy_to_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *src, size_t bytes); @@ -197,4 +198,8 @@ extern const struct snd_sof_dsp_ops sof_renoir_ops; /* Machine configuration */ int snd_amd_acp_find_config(struct pci_dev *pci); + +/* Trace */ +int acp_sof_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag); +int acp_sof_trace_release(struct snd_sof_dev *sdev); #endif diff --git a/sound/soc/sof/amd/renoir.c b/sound/soc/sof/amd/renoir.c index 3cd269bfe75d..43037109e130 100644 --- a/sound/soc/sof/amd/renoir.c +++ b/sound/soc/sof/amd/renoir.c @@ -173,6 +173,10 @@ const struct snd_sof_dsp_ops sof_renoir_ops = { .machine_select = amd_sof_machine_select, .machine_register = sof_machine_register, .machine_unregister = sof_machine_unregister, + + /* Trace Logger */ + .trace_init = acp_sof_trace_init, + .trace_release = acp_sof_trace_release, }; EXPORT_SYMBOL(sof_renoir_ops); -- cgit v1.2.3 From f063eba3e7a6aeec8e2abb00469e70c51432453b Mon Sep 17 00:00:00 2001 From: Ajit Kumar Pandey Date: Wed, 17 Nov 2021 11:37:26 +0200 Subject: ASoC: SOF: amd: Add support for SOF firmware authentication Add callback to notify PSP after loading firmware on DSP. PSP will validate the loaded firmware and set qualifier bit to run firmware on secured AMD systems. Signed-off-by: Julian Schroeder Signed-off-by: Ajit Kumar Pandey Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Reviewed-by: Curtis Malainey Signed-off-by: Daniel Baluta Link: https://lore.kernel.org/r/20211117093734.17407-14-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp-dsp-offset.h | 4 +++ sound/soc/sof/amd/acp.c | 66 +++++++++++++++++++++++++++++++++++++- sound/soc/sof/amd/acp.h | 21 ++++++++++++ sound/soc/sof/amd/pci-rn.c | 5 +++ 4 files changed, 95 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/amd/acp-dsp-offset.h b/sound/soc/sof/amd/acp-dsp-offset.h index 1d11e9d69dce..63f13c111b24 100644 --- a/sound/soc/sof/amd/acp-dsp-offset.h +++ b/sound/soc/sof/amd/acp-dsp-offset.h @@ -54,6 +54,9 @@ #define ACP_PGFSM_STATUS 0x1420 /* Registers from ACP_INTR block */ +#define ACP_EXTERNAL_INTR_ENB 0x1800 +#define ACP_EXTERNAL_INTR_CNTL 0x1804 +#define ACP_EXTERNAL_INTR_STAT 0x1808 #define ACP_DSP_SW_INTR_CNTL 0x1814 #define ACP_DSP_SW_INTR_STAT 0x1818 #define ACP_SW_INTR_TRIG 0x181C @@ -68,6 +71,7 @@ #define ACP_SHA_DMA_CMD_STS 0x1CC0 #define ACP_SHA_DMA_ERR_STATUS 0x1CC4 #define ACP_SHA_TRANSFER_BYTE_CNT 0x1CC8 +#define ACP_SHA_PSP_ACK 0x1C74 #define ACP_SCRATCH_REG_0 0x10000 diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index 74ede28aa8d8..4c5550e8d364 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -20,6 +20,22 @@ #include "acp.h" #include "acp-dsp-offset.h" +static int smn_write(struct pci_dev *dev, u32 smn_addr, u32 data) +{ + pci_write_config_dword(dev, 0x60, smn_addr); + pci_write_config_dword(dev, 0x64, data); + + return 0; +} + +static int smn_read(struct pci_dev *dev, u32 smn_addr, u32 *data) +{ + pci_write_config_dword(dev, 0x60, smn_addr); + pci_read_config_dword(dev, 0x64, data); + + return 0; +} + static void configure_acp_groupregisters(struct acp_dev_data *adata) { struct snd_sof_dev *sdev = adata->dev; @@ -135,6 +151,25 @@ int configure_and_run_dma(struct acp_dev_data *adata, unsigned int src_addr, return ret; } +static int psp_fw_validate(struct acp_dev_data *adata) +{ + struct snd_sof_dev *sdev = adata->dev; + int timeout; + u32 data; + + smn_write(adata->smn_dev, MP0_C2PMSG_26_REG, MBOX_ACP_SHA_DMA_COMMAND); + + for (timeout = ACP_PSP_TIMEOUT_COUNTER; timeout > 0; timeout--) { + msleep(20); + smn_read(adata->smn_dev, MP0_C2PMSG_26_REG, &data); + if (data & MBOX_READY_MASK) + return 0; + } + + dev_err(sdev->dev, "FW validation timedout: status %x\n", data & MBOX_STATUS_MASK); + return -ETIMEDOUT; +} + int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr, unsigned int start_addr, unsigned int dest_addr, unsigned int image_length) @@ -174,7 +209,9 @@ int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr, return ret; } - snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DSP_FW_QUALIFIER, DSP_FW_RUN_ENABLE); + ret = psp_fw_validate(adata); + if (ret) + return ret; fw_qualifier = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SHA_DSP_FW_QUALIFIER); if (!(fw_qualifier & DSP_FW_RUN_ENABLE)) { @@ -238,6 +275,13 @@ static irqreturn_t acp_irq_thread(int irq, void *context) struct snd_sof_dev *sdev = context; unsigned int val; + val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_EXTERNAL_INTR_STAT); + if (val & ACP_SHA_STAT) { + /* Clear SHA interrupt raised by PSP */ + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_EXTERNAL_INTR_STAT, val); + return IRQ_HANDLED; + } + val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_DSP_SW_INTR_STAT); if (val & ACP_DSP_TO_HOST_IRQ) { sof_ops(sdev)->irq_thread(irq, sdev); @@ -326,6 +370,7 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev) { struct pci_dev *pci = to_pci_dev(sdev->dev); struct acp_dev_data *adata; + const struct sof_amd_acp_desc *chip; unsigned int addr; int ret; @@ -346,18 +391,32 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev) sdev->pdata->hw_pdata = adata; + chip = get_chip_info(sdev->pdata); + if (!chip) { + dev_err(sdev->dev, "no such device supported, chip id:%x\n", pci->device); + return -EIO; + } + + adata->smn_dev = pci_get_device(PCI_VENDOR_ID_AMD, chip->host_bridge_id, NULL); + if (!adata->smn_dev) { + dev_err(sdev->dev, "Failed to get host bridge device\n"); + return -ENODEV; + } + sdev->ipc_irq = pci->irq; ret = request_threaded_irq(sdev->ipc_irq, acp_irq_handler, acp_irq_thread, IRQF_SHARED, "AudioDSP", sdev); if (ret < 0) { dev_err(sdev->dev, "failed to register IRQ %d\n", sdev->ipc_irq); + pci_dev_put(adata->smn_dev); return ret; } ret = acp_init(sdev); if (ret < 0) { free_irq(sdev->ipc_irq, sdev); + pci_dev_put(adata->smn_dev); return ret; } @@ -371,6 +430,11 @@ EXPORT_SYMBOL_NS(amd_sof_acp_probe, SND_SOC_SOF_AMD_COMMON); int amd_sof_acp_remove(struct snd_sof_dev *sdev) { + struct acp_dev_data *adata = sdev->pdata->hw_pdata; + + if (adata->smn_dev) + pci_dev_put(adata->smn_dev); + if (sdev->ipc_irq) free_irq(sdev->ipc_irq, sdev); diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index fd923f72a01a..a2f8e4219066 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -52,6 +52,15 @@ #define ACP_DSP_TO_HOST_IRQ 0x04 +#define HOST_BRIDGE_CZN 0x1630 +#define ACP_SHA_STAT 0x8000 +#define ACP_PSP_TIMEOUT_COUNTER 5 +#define ACP_EXT_INTR_ERROR_STAT 0x20000000 +#define MP0_C2PMSG_26_REG 0x03810570 +#define MBOX_ACP_SHA_DMA_COMMAND 0x330000 +#define MBOX_READY_MASK 0x80000000 +#define MBOX_STATUS_MASK 0xFFFF + struct acp_atu_grp_pte { u32 low; u32 high; @@ -140,6 +149,7 @@ struct acp_dev_data { struct dma_descriptor dscr_info[ACP_MAX_DESC]; struct acp_dsp_stream stream_buf[ACP_MAX_STREAM]; struct acp_dsp_stream *dtrace_stream; + struct pci_dev *smn_dev; }; void memcpy_to_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *src, size_t bytes); @@ -202,4 +212,15 @@ int snd_amd_acp_find_config(struct pci_dev *pci); /* Trace */ int acp_sof_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag); int acp_sof_trace_release(struct snd_sof_dev *sdev); + +struct sof_amd_acp_desc { + unsigned int host_bridge_id; +}; + +static inline const struct sof_amd_acp_desc *get_chip_info(struct snd_sof_pdata *pdata) +{ + const struct sof_dev_desc *desc = pdata->desc; + + return desc->chip_info; +} #endif diff --git a/sound/soc/sof/amd/pci-rn.c b/sound/soc/sof/amd/pci-rn.c index 3c379a5ef231..392ffbdf6417 100644 --- a/sound/soc/sof/amd/pci-rn.c +++ b/sound/soc/sof/amd/pci-rn.c @@ -43,12 +43,17 @@ static const struct resource renoir_res[] = { }, }; +static const struct sof_amd_acp_desc renoir_chip_info = { + .host_bridge_id = HOST_BRIDGE_CZN, +}; + static const struct sof_dev_desc renoir_desc = { .machines = snd_soc_acpi_amd_sof_machines, .resindex_lpe_base = 0, .resindex_pcicfg_base = -1, .resindex_imr_base = -1, .irqindex_host_ipc = -1, + .chip_info = &renoir_chip_info, .default_fw_path = "amd/sof", .default_tplg_path = "amd/sof-tplg", .default_fw_filename = "sof-rn.ri", -- cgit v1.2.3 From 56717d72f7a811799e8d138ff3d49325272c5cf6 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 17 Nov 2021 13:22:51 +0000 Subject: ASoC: wm_adsp: Remove the wmfw_add_ctl helper function The helper function wmfw_add_ctl is only called from one place and that place is a function with only 2 lines of code. Merge the helper function into the work function to simplify the code. Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20211117132300.1290-1-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 33 +++++++++------------------------ 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index d4f0d72cbcc8..404717e30f44 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -537,15 +537,20 @@ static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len) return out; } -static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) +static void wm_adsp_ctl_work(struct work_struct *work) { + struct wm_coeff_ctl *ctl = container_of(work, + struct wm_coeff_ctl, + work); struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; + struct wm_adsp *dsp = container_of(cs_ctl->dsp, + struct wm_adsp, + cs_dsp); struct snd_kcontrol_new *kcontrol; - int ret; kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL); if (!kcontrol) - return -ENOMEM; + return; kcontrol->name = ctl->name; kcontrol->info = wm_coeff_info; @@ -571,29 +576,9 @@ static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) break; } - ret = snd_soc_add_component_controls(dsp->component, kcontrol, 1); - if (ret < 0) - goto err_kcontrol; + snd_soc_add_component_controls(dsp->component, kcontrol, 1); kfree(kcontrol); - - return 0; - -err_kcontrol: - kfree(kcontrol); - return ret; -} - -static void wm_adsp_ctl_work(struct work_struct *work) -{ - struct wm_coeff_ctl *ctl = container_of(work, - struct wm_coeff_ctl, - work); - struct wm_adsp *dsp = container_of(ctl->cs_ctl->dsp, - struct wm_adsp, - cs_dsp); - - wmfw_add_ctl(dsp, ctl); } static int wm_adsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl) -- cgit v1.2.3 From 5065cfabec21a4acf562932f1d0a814c119e0a69 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 17 Nov 2021 13:22:52 +0000 Subject: firmware: cs_dsp: Add lockdep asserts to interface functions Some of the control functions exposed by the cs_dsp code require the pwr_lock to be held by the caller. Add lockdep_assert_held calls to ensure this is done correctly. Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20211117132300.1290-2-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/firmware/cirrus/cs_dsp.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index 1a0c6c793f6a..0d1ba7d8efa4 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -653,6 +653,8 @@ int cs_dsp_coeff_write_acked_control(struct cs_dsp_coeff_ctl *ctl, unsigned int unsigned int reg; int i, ret; + lockdep_assert_held(&dsp->pwr_lock); + if (!dsp->running) return -EPERM; @@ -754,6 +756,8 @@ int cs_dsp_coeff_write_ctrl(struct cs_dsp_coeff_ctl *ctl, const void *buf, size_ { int ret = 0; + lockdep_assert_held(&ctl->dsp->pwr_lock); + if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) ret = -EPERM; else if (buf != ctl->cache) @@ -811,6 +815,8 @@ int cs_dsp_coeff_read_ctrl(struct cs_dsp_coeff_ctl *ctl, void *buf, size_t len) { int ret = 0; + lockdep_assert_held(&ctl->dsp->pwr_lock); + if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { if (ctl->enabled && ctl->dsp->running) return cs_dsp_coeff_read_ctrl_raw(ctl, buf, len); @@ -1453,6 +1459,8 @@ struct cs_dsp_coeff_ctl *cs_dsp_get_ctl(struct cs_dsp *dsp, const char *name, in { struct cs_dsp_coeff_ctl *pos, *rslt = NULL; + lockdep_assert_held(&dsp->pwr_lock); + list_for_each_entry(pos, &dsp->ctl_list, list) { if (!pos->subname) continue; @@ -1548,6 +1556,8 @@ struct cs_dsp_alg_region *cs_dsp_find_alg_region(struct cs_dsp *dsp, { struct cs_dsp_alg_region *alg_region; + lockdep_assert_held(&dsp->pwr_lock); + list_for_each_entry(alg_region, &dsp->alg_regions, list) { if (id == alg_region->alg && type == alg_region->type) return alg_region; @@ -2783,6 +2793,8 @@ int cs_dsp_read_raw_data_block(struct cs_dsp *dsp, int mem_type, unsigned int me unsigned int reg; int ret; + lockdep_assert_held(&dsp->pwr_lock); + if (!mem) return -EINVAL; @@ -2836,6 +2848,8 @@ int cs_dsp_write_data_word(struct cs_dsp *dsp, int mem_type, unsigned int mem_ad __be32 val = cpu_to_be32(data & 0x00ffffffu); unsigned int reg; + lockdep_assert_held(&dsp->pwr_lock); + if (!mem) return -EINVAL; -- cgit v1.2.3 From 2925748eadc33cba3bded7b69475a1b002b124ac Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 17 Nov 2021 13:22:53 +0000 Subject: firmware: cs_dsp: Add version checks on coefficient loading The firmware coefficient files contain version information that is currently ignored by the cs_dsp code. This information specifies which version of the firmware the coefficient were generated for. Add a check into the code which prints a warning in the case the coefficient and firmware differ in version, in many cases this will be ok but it is not always, so best to let the user know there is a potential issue. Co-authored-by: Simon Trimmer Signed-off-by: Simon Trimmer Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20211117132300.1290-3-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/firmware/cirrus/cs_dsp.c | 49 +++++++++++++++++++++++++--------- include/linux/firmware/cirrus/cs_dsp.h | 2 ++ 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index 0d1ba7d8efa4..0da454a8498d 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -1569,7 +1569,7 @@ EXPORT_SYMBOL_GPL(cs_dsp_find_alg_region); static struct cs_dsp_alg_region *cs_dsp_create_region(struct cs_dsp *dsp, int type, __be32 id, - __be32 base) + __be32 ver, __be32 base) { struct cs_dsp_alg_region *alg_region; @@ -1579,6 +1579,7 @@ static struct cs_dsp_alg_region *cs_dsp_create_region(struct cs_dsp *dsp, alg_region->type = type; alg_region->alg = be32_to_cpu(id); + alg_region->ver = be32_to_cpu(ver); alg_region->base = be32_to_cpu(base); list_add_tail(&alg_region->list, &dsp->alg_regions); @@ -1628,14 +1629,14 @@ static void cs_dsp_parse_wmfw_v3_id_header(struct cs_dsp *dsp, nalgs); } -static int cs_dsp_create_regions(struct cs_dsp *dsp, __be32 id, int nregions, - const int *type, __be32 *base) +static int cs_dsp_create_regions(struct cs_dsp *dsp, __be32 id, __be32 ver, + int nregions, const int *type, __be32 *base) { struct cs_dsp_alg_region *alg_region; int i; for (i = 0; i < nregions; i++) { - alg_region = cs_dsp_create_region(dsp, type[i], id, base[i]); + alg_region = cs_dsp_create_region(dsp, type[i], id, ver, base[i]); if (IS_ERR(alg_region)) return PTR_ERR(alg_region); } @@ -1670,12 +1671,14 @@ static int cs_dsp_adsp1_setup_algs(struct cs_dsp *dsp) cs_dsp_parse_wmfw_id_header(dsp, &adsp1_id.fw, n_algs); alg_region = cs_dsp_create_region(dsp, WMFW_ADSP1_ZM, - adsp1_id.fw.id, adsp1_id.zm); + adsp1_id.fw.id, adsp1_id.fw.ver, + adsp1_id.zm); if (IS_ERR(alg_region)) return PTR_ERR(alg_region); alg_region = cs_dsp_create_region(dsp, WMFW_ADSP1_DM, - adsp1_id.fw.id, adsp1_id.dm); + adsp1_id.fw.id, adsp1_id.fw.ver, + adsp1_id.dm); if (IS_ERR(alg_region)) return PTR_ERR(alg_region); @@ -1698,6 +1701,7 @@ static int cs_dsp_adsp1_setup_algs(struct cs_dsp *dsp) alg_region = cs_dsp_create_region(dsp, WMFW_ADSP1_DM, adsp1_alg[i].alg.id, + adsp1_alg[i].alg.ver, adsp1_alg[i].dm); if (IS_ERR(alg_region)) { ret = PTR_ERR(alg_region); @@ -1719,6 +1723,7 @@ static int cs_dsp_adsp1_setup_algs(struct cs_dsp *dsp) alg_region = cs_dsp_create_region(dsp, WMFW_ADSP1_ZM, adsp1_alg[i].alg.id, + adsp1_alg[i].alg.ver, adsp1_alg[i].zm); if (IS_ERR(alg_region)) { ret = PTR_ERR(alg_region); @@ -1771,17 +1776,20 @@ static int cs_dsp_adsp2_setup_algs(struct cs_dsp *dsp) cs_dsp_parse_wmfw_id_header(dsp, &adsp2_id.fw, n_algs); alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_XM, - adsp2_id.fw.id, adsp2_id.xm); + adsp2_id.fw.id, adsp2_id.fw.ver, + adsp2_id.xm); if (IS_ERR(alg_region)) return PTR_ERR(alg_region); alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_YM, - adsp2_id.fw.id, adsp2_id.ym); + adsp2_id.fw.id, adsp2_id.fw.ver, + adsp2_id.ym); if (IS_ERR(alg_region)) return PTR_ERR(alg_region); alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_ZM, - adsp2_id.fw.id, adsp2_id.zm); + adsp2_id.fw.id, adsp2_id.fw.ver, + adsp2_id.zm); if (IS_ERR(alg_region)) return PTR_ERR(alg_region); @@ -1806,6 +1814,7 @@ static int cs_dsp_adsp2_setup_algs(struct cs_dsp *dsp) alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_XM, adsp2_alg[i].alg.id, + adsp2_alg[i].alg.ver, adsp2_alg[i].xm); if (IS_ERR(alg_region)) { ret = PTR_ERR(alg_region); @@ -1827,6 +1836,7 @@ static int cs_dsp_adsp2_setup_algs(struct cs_dsp *dsp) alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_YM, adsp2_alg[i].alg.id, + adsp2_alg[i].alg.ver, adsp2_alg[i].ym); if (IS_ERR(alg_region)) { ret = PTR_ERR(alg_region); @@ -1848,6 +1858,7 @@ static int cs_dsp_adsp2_setup_algs(struct cs_dsp *dsp) alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_ZM, adsp2_alg[i].alg.id, + adsp2_alg[i].alg.ver, adsp2_alg[i].zm); if (IS_ERR(alg_region)) { ret = PTR_ERR(alg_region); @@ -1873,7 +1884,7 @@ out: return ret; } -static int cs_dsp_halo_create_regions(struct cs_dsp *dsp, __be32 id, +static int cs_dsp_halo_create_regions(struct cs_dsp *dsp, __be32 id, __be32 ver, __be32 xm_base, __be32 ym_base) { static const int types[] = { @@ -1882,7 +1893,7 @@ static int cs_dsp_halo_create_regions(struct cs_dsp *dsp, __be32 id, }; __be32 bases[] = { xm_base, xm_base, ym_base, ym_base }; - return cs_dsp_create_regions(dsp, id, ARRAY_SIZE(types), types, bases); + return cs_dsp_create_regions(dsp, id, ver, ARRAY_SIZE(types), types, bases); } static int cs_dsp_halo_setup_algs(struct cs_dsp *dsp) @@ -1910,7 +1921,7 @@ static int cs_dsp_halo_setup_algs(struct cs_dsp *dsp) cs_dsp_parse_wmfw_v3_id_header(dsp, &halo_id.fw, n_algs); - ret = cs_dsp_halo_create_regions(dsp, halo_id.fw.id, + ret = cs_dsp_halo_create_regions(dsp, halo_id.fw.id, halo_id.fw.ver, halo_id.xm_base, halo_id.ym_base); if (ret) return ret; @@ -1934,6 +1945,7 @@ static int cs_dsp_halo_setup_algs(struct cs_dsp *dsp) be32_to_cpu(halo_alg[i].ym_base)); ret = cs_dsp_halo_create_regions(dsp, halo_alg[i].alg.id, + halo_alg[i].alg.ver, halo_alg[i].xm_base, halo_alg[i].ym_base); if (ret) @@ -1955,7 +1967,7 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware const struct cs_dsp_region *mem; struct cs_dsp_alg_region *alg_region; const char *region_name; - int ret, pos, blocks, type, offset, reg; + int ret, pos, blocks, type, offset, reg, version; struct cs_dsp_buf *buf; if (!firmware) @@ -1999,6 +2011,7 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware type = le16_to_cpu(blk->type); offset = le16_to_cpu(blk->offset); + version = le32_to_cpu(blk->ver) >> 8; cs_dsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n", file, blocks, le32_to_cpu(blk->id), @@ -2056,6 +2069,16 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware alg_region = cs_dsp_find_alg_region(dsp, type, le32_to_cpu(blk->id)); if (alg_region) { + if (version != alg_region->ver) + cs_dsp_warn(dsp, + "Algorithm coefficient version %d.%d.%d but expected %d.%d.%d\n", + (version >> 16) & 0xFF, + (version >> 8) & 0xFF, + version & 0xFF, + (alg_region->ver >> 16) & 0xFF, + (alg_region->ver >> 8) & 0xFF, + alg_region->ver & 0xFF); + reg = alg_region->base; reg = dsp->ops->region_to_reg(mem, reg); reg += offset; diff --git a/include/linux/firmware/cirrus/cs_dsp.h b/include/linux/firmware/cirrus/cs_dsp.h index 3a54b1afc48f..ce54705e2bec 100644 --- a/include/linux/firmware/cirrus/cs_dsp.h +++ b/include/linux/firmware/cirrus/cs_dsp.h @@ -54,12 +54,14 @@ struct cs_dsp_region { * struct cs_dsp_alg_region - Describes a logical algorithm region in DSP address space * @list: List node for internal use * @alg: Algorithm id + * @ver: Expected algorithm version * @type: Memory region type * @base: Address of region */ struct cs_dsp_alg_region { struct list_head list; unsigned int alg; + unsigned int ver; int type; unsigned int base; }; -- cgit v1.2.3 From 14055b5a3a23204c4702ae5d3f2a819ee081ce33 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 17 Nov 2021 13:22:54 +0000 Subject: firmware: cs_dsp: Add pre_run callback The code already has a post_run callback, add a matching pre_run callback to the client_ops that is called before execution is started. This callback provides a convenient place for the client code to set DSP controls or hardware that requires configuration before the DSP core actually starts execution. Note that placing this callback before cs_dsp_coeff_sync_controls is important to ensure that any control values are then correctly synced out to the chip. Co-authored-by: Simon Trimmer Signed-off-by: Simon Trimmer Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20211117132300.1290-4-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/firmware/cirrus/cs_dsp.c | 6 ++++++ include/linux/firmware/cirrus/cs_dsp.h | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index 0da454a8498d..ef7afadea42d 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -2627,6 +2627,12 @@ int cs_dsp_run(struct cs_dsp *dsp) goto err; } + if (dsp->client_ops->pre_run) { + ret = dsp->client_ops->pre_run(dsp); + if (ret) + goto err; + } + /* Sync set controls */ ret = cs_dsp_coeff_sync_controls(dsp); if (ret != 0) diff --git a/include/linux/firmware/cirrus/cs_dsp.h b/include/linux/firmware/cirrus/cs_dsp.h index ce54705e2bec..0bf849baeaa5 100644 --- a/include/linux/firmware/cirrus/cs_dsp.h +++ b/include/linux/firmware/cirrus/cs_dsp.h @@ -187,7 +187,8 @@ struct cs_dsp { * struct cs_dsp_client_ops - client callbacks * @control_add: Called under the pwr_lock when a control is created * @control_remove: Called under the pwr_lock when a control is destroyed - * @post_run: Called under the pwr_lock by cs_dsp_run() + * @pre_run: Called under the pwr_lock by cs_dsp_run() before the core is started + * @post_run: Called under the pwr_lock by cs_dsp_run() after the core is started * @post_stop: Called under the pwr_lock by cs_dsp_stop() * @watchdog_expired: Called when a watchdog expiry is detected * @@ -197,6 +198,7 @@ struct cs_dsp { struct cs_dsp_client_ops { int (*control_add)(struct cs_dsp_coeff_ctl *ctl); void (*control_remove)(struct cs_dsp_coeff_ctl *ctl); + int (*pre_run)(struct cs_dsp *dsp); int (*post_run)(struct cs_dsp *dsp); void (*post_stop)(struct cs_dsp *dsp); void (*watchdog_expired)(struct cs_dsp *dsp); -- cgit v1.2.3 From 40a34ae7308682bbbf5827145afa23dcdfb1f090 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 17 Nov 2021 13:22:55 +0000 Subject: firmware: cs_dsp: Print messages from bin files The coefficient file contains various info strings, and the equivalent strings are printed from the WMFW file as it is loaded. Add support for printing these from the coefficient file as well. Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20211117132300.1290-5-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/firmware/cirrus/cs_dsp.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index ef7afadea42d..3d21574f3a44 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -1968,6 +1968,7 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware struct cs_dsp_alg_region *alg_region; const char *region_name; int ret, pos, blocks, type, offset, reg, version; + char *text = NULL; struct cs_dsp_buf *buf; if (!firmware) @@ -2025,6 +2026,8 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware region_name = "Unknown"; switch (type) { case (WMFW_NAME_TEXT << 8): + text = kzalloc(le32_to_cpu(blk->len) + 1, GFP_KERNEL); + break; case (WMFW_INFO_TEXT << 8): case (WMFW_METADATA << 8): break; @@ -2094,6 +2097,13 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware break; } + if (text) { + memcpy(text, blk->data, le32_to_cpu(blk->len)); + cs_dsp_info(dsp, "%s: %s\n", dsp->fw_name, text); + kfree(text); + text = NULL; + } + if (reg) { if (le32_to_cpu(blk->len) > firmware->size - pos - sizeof(*blk)) { @@ -2144,6 +2154,7 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware out_fw: regmap_async_complete(regmap); cs_dsp_buf_free(&buf_list); + kfree(text); return ret; } -- cgit v1.2.3 From dcee767667f44ed0d40a3debf507a3ba027a1994 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 17 Nov 2021 13:22:56 +0000 Subject: firmware: cs_dsp: Add support for rev 2 coefficient files Add support for the revision 2 coefficient file, this format is identical to revision 1 and was simply added by accident to some firmware. However unfortunately many firmwares have leaked into production using this and as such driver support really needs to be added for it. Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20211117132300.1290-6-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/firmware/cirrus/cs_dsp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index 3d21574f3a44..62ba4ebbf11f 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -1990,6 +1990,7 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware switch (be32_to_cpu(hdr->rev) & 0xff) { case 1: + case 2: break; default: cs_dsp_err(dsp, "%s: Unsupported coefficient file format %d\n", -- cgit v1.2.3 From 86c6080407740937ed2ba0ccd181e947f77e2154 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 17 Nov 2021 13:22:57 +0000 Subject: firmware: cs_dsp: Perform NULL check in cs_dsp_coeff_write/read_ctrl Add a NULL check to the cs_dsp_coeff_write/read_ctrl functions. This is a major convenience for users of the cs_dsp library as it allows the call to cs_dsp_get_ctl to be inlined with the call to read/write the control itself. Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20211117132300.1290-7-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/firmware/cirrus/cs_dsp.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index 62ba4ebbf11f..9eecd1626537 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -758,6 +758,9 @@ int cs_dsp_coeff_write_ctrl(struct cs_dsp_coeff_ctl *ctl, const void *buf, size_ lockdep_assert_held(&ctl->dsp->pwr_lock); + if (!ctl) + return -ENOENT; + if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) ret = -EPERM; else if (buf != ctl->cache) @@ -817,6 +820,9 @@ int cs_dsp_coeff_read_ctrl(struct cs_dsp_coeff_ctl *ctl, void *buf, size_t len) lockdep_assert_held(&ctl->dsp->pwr_lock); + if (!ctl) + return -ENOENT; + if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { if (ctl->enabled && ctl->dsp->running) return cs_dsp_coeff_read_ctrl_raw(ctl, buf, len); -- cgit v1.2.3 From b329b3d39497a9fdb175d7e4fd77ae7170d5d26c Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 17 Nov 2021 13:22:58 +0000 Subject: firmware: cs_dsp: Clarify some kernel doc comments Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20211117132300.1290-8-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/firmware/cirrus/cs_dsp.c | 4 ++-- include/linux/firmware/cirrus/cs_dsp.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index 9eecd1626537..d1bcade2efe2 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -746,7 +746,7 @@ static int cs_dsp_coeff_write_ctrl_raw(struct cs_dsp_coeff_ctl *ctl, * cs_dsp_coeff_write_ctrl() - Writes the given buffer to the given coefficient control * @ctl: pointer to coefficient control * @buf: the buffer to write to the given control - * @len: the length of the buffer + * @len: the length of the buffer in bytes * * Must be called with pwr_lock held. * @@ -808,7 +808,7 @@ static int cs_dsp_coeff_read_ctrl_raw(struct cs_dsp_coeff_ctl *ctl, void *buf, s * cs_dsp_coeff_read_ctrl() - Reads the given coefficient control into the given buffer * @ctl: pointer to coefficient control * @buf: the buffer to store to the given control - * @len: the length of the buffer + * @len: the length of the buffer in bytes * * Must be called with pwr_lock held. * diff --git a/include/linux/firmware/cirrus/cs_dsp.h b/include/linux/firmware/cirrus/cs_dsp.h index 0bf849baeaa5..1ad1b173417a 100644 --- a/include/linux/firmware/cirrus/cs_dsp.h +++ b/include/linux/firmware/cirrus/cs_dsp.h @@ -76,8 +76,8 @@ struct cs_dsp_alg_region { * @enabled: Flag indicating whether control is enabled * @list: List node for internal use * @cache: Cached value of the control - * @offset: Offset of control within alg_region - * @len: Length of the cached value + * @offset: Offset of control within alg_region in words + * @len: Length of the cached value in bytes * @set: Flag indicating the value has been written by the user * @flags: Bitfield of WMFW_CTL_FLAG_ control flags defined in wmfw.h * @type: One of the WMFW_CTL_TYPE_ control types defined in wmfw.h -- cgit v1.2.3 From f444da38ac924748de696c393327a44c4b8d727e Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 17 Nov 2021 13:22:59 +0000 Subject: firmware: cs_dsp: Add offset to cs_dsp read/write Provide a mechanism to access only part of a control through the cs_dsp interface. Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20211117132300.1290-9-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/firmware/cirrus/cs_dsp.c | 44 +++++++++++++++++++++------------- include/linux/firmware/cirrus/cs_dsp.h | 6 +++-- sound/soc/codecs/wm_adsp.c | 14 +++++------ 3 files changed, 39 insertions(+), 25 deletions(-) diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index d1bcade2efe2..5fe08de91ecd 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -616,7 +616,8 @@ static void cs_dsp_halo_show_fw_status(struct cs_dsp *dsp) offs[0], offs[1], offs[2], offs[3]); } -static int cs_dsp_coeff_base_reg(struct cs_dsp_coeff_ctl *ctl, unsigned int *reg) +static int cs_dsp_coeff_base_reg(struct cs_dsp_coeff_ctl *ctl, unsigned int *reg, + unsigned int off) { const struct cs_dsp_alg_region *alg_region = &ctl->alg_region; struct cs_dsp *dsp = ctl->dsp; @@ -629,7 +630,7 @@ static int cs_dsp_coeff_base_reg(struct cs_dsp_coeff_ctl *ctl, unsigned int *reg return -EINVAL; } - *reg = dsp->ops->region_to_reg(mem, ctl->alg_region.base + ctl->offset); + *reg = dsp->ops->region_to_reg(mem, ctl->alg_region.base + ctl->offset + off); return 0; } @@ -658,7 +659,7 @@ int cs_dsp_coeff_write_acked_control(struct cs_dsp_coeff_ctl *ctl, unsigned int if (!dsp->running) return -EPERM; - ret = cs_dsp_coeff_base_reg(ctl, ®); + ret = cs_dsp_coeff_base_reg(ctl, ®, 0); if (ret) return ret; @@ -712,14 +713,14 @@ int cs_dsp_coeff_write_acked_control(struct cs_dsp_coeff_ctl *ctl, unsigned int EXPORT_SYMBOL_GPL(cs_dsp_coeff_write_acked_control); static int cs_dsp_coeff_write_ctrl_raw(struct cs_dsp_coeff_ctl *ctl, - const void *buf, size_t len) + unsigned int off, const void *buf, size_t len) { struct cs_dsp *dsp = ctl->dsp; void *scratch; int ret; unsigned int reg; - ret = cs_dsp_coeff_base_reg(ctl, ®); + ret = cs_dsp_coeff_base_reg(ctl, ®, off); if (ret) return ret; @@ -745,6 +746,7 @@ static int cs_dsp_coeff_write_ctrl_raw(struct cs_dsp_coeff_ctl *ctl, /** * cs_dsp_coeff_write_ctrl() - Writes the given buffer to the given coefficient control * @ctl: pointer to coefficient control + * @off: word offset at which data should be written * @buf: the buffer to write to the given control * @len: the length of the buffer in bytes * @@ -752,7 +754,8 @@ static int cs_dsp_coeff_write_ctrl_raw(struct cs_dsp_coeff_ctl *ctl, * * Return: Zero for success, a negative number on error. */ -int cs_dsp_coeff_write_ctrl(struct cs_dsp_coeff_ctl *ctl, const void *buf, size_t len) +int cs_dsp_coeff_write_ctrl(struct cs_dsp_coeff_ctl *ctl, + unsigned int off, const void *buf, size_t len) { int ret = 0; @@ -761,27 +764,31 @@ int cs_dsp_coeff_write_ctrl(struct cs_dsp_coeff_ctl *ctl, const void *buf, size_ if (!ctl) return -ENOENT; + if (len + off * sizeof(u32) > ctl->len) + return -EINVAL; + if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) ret = -EPERM; else if (buf != ctl->cache) - memcpy(ctl->cache, buf, len); + memcpy(ctl->cache + off * sizeof(u32), buf, len); ctl->set = 1; if (ctl->enabled && ctl->dsp->running) - ret = cs_dsp_coeff_write_ctrl_raw(ctl, buf, len); + ret = cs_dsp_coeff_write_ctrl_raw(ctl, off, buf, len); return ret; } EXPORT_SYMBOL_GPL(cs_dsp_coeff_write_ctrl); -static int cs_dsp_coeff_read_ctrl_raw(struct cs_dsp_coeff_ctl *ctl, void *buf, size_t len) +static int cs_dsp_coeff_read_ctrl_raw(struct cs_dsp_coeff_ctl *ctl, + unsigned int off, void *buf, size_t len) { struct cs_dsp *dsp = ctl->dsp; void *scratch; int ret; unsigned int reg; - ret = cs_dsp_coeff_base_reg(ctl, ®); + ret = cs_dsp_coeff_base_reg(ctl, ®, off); if (ret) return ret; @@ -807,6 +814,7 @@ static int cs_dsp_coeff_read_ctrl_raw(struct cs_dsp_coeff_ctl *ctl, void *buf, s /** * cs_dsp_coeff_read_ctrl() - Reads the given coefficient control into the given buffer * @ctl: pointer to coefficient control + * @off: word offset at which data should be read * @buf: the buffer to store to the given control * @len: the length of the buffer in bytes * @@ -814,7 +822,8 @@ static int cs_dsp_coeff_read_ctrl_raw(struct cs_dsp_coeff_ctl *ctl, void *buf, s * * Return: Zero for success, a negative number on error. */ -int cs_dsp_coeff_read_ctrl(struct cs_dsp_coeff_ctl *ctl, void *buf, size_t len) +int cs_dsp_coeff_read_ctrl(struct cs_dsp_coeff_ctl *ctl, + unsigned int off, void *buf, size_t len) { int ret = 0; @@ -823,17 +832,20 @@ int cs_dsp_coeff_read_ctrl(struct cs_dsp_coeff_ctl *ctl, void *buf, size_t len) if (!ctl) return -ENOENT; + if (len + off * sizeof(u32) > ctl->len) + return -EINVAL; + if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { if (ctl->enabled && ctl->dsp->running) - return cs_dsp_coeff_read_ctrl_raw(ctl, buf, len); + return cs_dsp_coeff_read_ctrl_raw(ctl, off, buf, len); else return -EPERM; } else { if (!ctl->flags && ctl->enabled && ctl->dsp->running) - ret = cs_dsp_coeff_read_ctrl_raw(ctl, ctl->cache, ctl->len); + ret = cs_dsp_coeff_read_ctrl_raw(ctl, 0, ctl->cache, ctl->len); if (buf != ctl->cache) - memcpy(buf, ctl->cache, len); + memcpy(buf, ctl->cache + off * sizeof(u32), len); } return ret; @@ -857,7 +869,7 @@ static int cs_dsp_coeff_init_control_caches(struct cs_dsp *dsp) * created so we don't need to do anything. */ if (!ctl->flags || (ctl->flags & WMFW_CTL_FLAG_READABLE)) { - ret = cs_dsp_coeff_read_ctrl_raw(ctl, ctl->cache, ctl->len); + ret = cs_dsp_coeff_read_ctrl_raw(ctl, 0, ctl->cache, ctl->len); if (ret < 0) return ret; } @@ -875,7 +887,7 @@ static int cs_dsp_coeff_sync_controls(struct cs_dsp *dsp) if (!ctl->enabled) continue; if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) { - ret = cs_dsp_coeff_write_ctrl_raw(ctl, ctl->cache, + ret = cs_dsp_coeff_write_ctrl_raw(ctl, 0, ctl->cache, ctl->len); if (ret < 0) return ret; diff --git a/include/linux/firmware/cirrus/cs_dsp.h b/include/linux/firmware/cirrus/cs_dsp.h index 1ad1b173417a..38b4da3ddfe4 100644 --- a/include/linux/firmware/cirrus/cs_dsp.h +++ b/include/linux/firmware/cirrus/cs_dsp.h @@ -232,8 +232,10 @@ void cs_dsp_init_debugfs(struct cs_dsp *dsp, struct dentry *debugfs_root); void cs_dsp_cleanup_debugfs(struct cs_dsp *dsp); int cs_dsp_coeff_write_acked_control(struct cs_dsp_coeff_ctl *ctl, unsigned int event_id); -int cs_dsp_coeff_write_ctrl(struct cs_dsp_coeff_ctl *ctl, const void *buf, size_t len); -int cs_dsp_coeff_read_ctrl(struct cs_dsp_coeff_ctl *ctl, void *buf, size_t len); +int cs_dsp_coeff_write_ctrl(struct cs_dsp_coeff_ctl *ctl, unsigned int off, + const void *buf, size_t len); +int cs_dsp_coeff_read_ctrl(struct cs_dsp_coeff_ctl *ctl, unsigned int off, + void *buf, size_t len); struct cs_dsp_coeff_ctl *cs_dsp_get_ctl(struct cs_dsp *dsp, const char *name, int type, unsigned int alg); diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 404717e30f44..f084b093cff6 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -401,7 +401,7 @@ static int wm_coeff_put(struct snd_kcontrol *kctl, int ret = 0; mutex_lock(&cs_ctl->dsp->pwr_lock); - ret = cs_dsp_coeff_write_ctrl(cs_ctl, p, cs_ctl->len); + ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, p, cs_ctl->len); mutex_unlock(&cs_ctl->dsp->pwr_lock); return ret; @@ -421,7 +421,7 @@ static int wm_coeff_tlv_put(struct snd_kcontrol *kctl, if (copy_from_user(cs_ctl->cache, bytes, size)) ret = -EFAULT; else - ret = cs_dsp_coeff_write_ctrl(cs_ctl, cs_ctl->cache, size); + ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, cs_ctl->cache, size); mutex_unlock(&cs_ctl->dsp->pwr_lock); @@ -464,7 +464,7 @@ static int wm_coeff_get(struct snd_kcontrol *kctl, int ret; mutex_lock(&cs_ctl->dsp->pwr_lock); - ret = cs_dsp_coeff_read_ctrl(cs_ctl, p, cs_ctl->len); + ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, p, cs_ctl->len); mutex_unlock(&cs_ctl->dsp->pwr_lock); return ret; @@ -481,7 +481,7 @@ static int wm_coeff_tlv_get(struct snd_kcontrol *kctl, mutex_lock(&cs_ctl->dsp->pwr_lock); - ret = cs_dsp_coeff_read_ctrl(cs_ctl, cs_ctl->cache, size); + ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, cs_ctl->cache, size); if (!ret && copy_to_user(bytes, cs_ctl->cache, size)) ret = -EFAULT; @@ -684,7 +684,7 @@ int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type, if (len > cs_ctl->len) return -EINVAL; - ret = cs_dsp_coeff_write_ctrl(cs_ctl, buf, len); + ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, buf, len); if (ret) return ret; @@ -723,7 +723,7 @@ int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type, if (len > cs_ctl->len) return -EINVAL; - return cs_dsp_coeff_read_ctrl(cs_ctl, buf, len); + return cs_dsp_coeff_read_ctrl(cs_ctl, 0, buf, len); } EXPORT_SYMBOL_GPL(wm_adsp_read_ctl); @@ -1432,7 +1432,7 @@ static int wm_adsp_buffer_parse_coeff(struct cs_dsp_coeff_ctl *cs_ctl) int ret, i; for (i = 0; i < 5; ++i) { - ret = cs_dsp_coeff_read_ctrl(cs_ctl, &coeff_v1, sizeof(coeff_v1)); + ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, &coeff_v1, sizeof(coeff_v1)); if (ret < 0) return ret; -- cgit v1.2.3 From 5c903f64ce97172d63f7591cfa9e37cba58867b2 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 17 Nov 2021 13:23:00 +0000 Subject: firmware: cs_dsp: Allow creation of event controls Some firmwares contain controls intended to convey firmware state back to the host. Whilst more infrastructure will probably be needed for these in time, as a first step allow creation of the controls, so said firmwares arn't completely rejected. Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20211117132300.1290-10-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/firmware/cirrus/cs_dsp.c | 1 + include/linux/firmware/cirrus/wmfw.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index 5fe08de91ecd..3814cbba0a54 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -1177,6 +1177,7 @@ static int cs_dsp_parse_coeff(struct cs_dsp *dsp, return -EINVAL; break; case WMFW_CTL_TYPE_HOSTEVENT: + case WMFW_CTL_TYPE_FWEVENT: ret = cs_dsp_check_coeff_flags(dsp, &coeff_blk, WMFW_CTL_FLAG_SYS | WMFW_CTL_FLAG_VOLATILE | diff --git a/include/linux/firmware/cirrus/wmfw.h b/include/linux/firmware/cirrus/wmfw.h index a19bf7c6fc8b..74e5a4f6c13a 100644 --- a/include/linux/firmware/cirrus/wmfw.h +++ b/include/linux/firmware/cirrus/wmfw.h @@ -29,6 +29,7 @@ #define WMFW_CTL_TYPE_ACKED 0x1000 /* acked control */ #define WMFW_CTL_TYPE_HOSTEVENT 0x1001 /* event control */ #define WMFW_CTL_TYPE_HOST_BUFFER 0x1002 /* host buffer pointer */ +#define WMFW_CTL_TYPE_FWEVENT 0x1004 /* firmware event control */ struct wmfw_header { char magic[4]; -- cgit v1.2.3 From 32d7e03d26fd93187c87ed0fbf59ec7023a61404 Mon Sep 17 00:00:00 2001 From: YC Hung Date: Thu, 18 Nov 2021 12:07:42 +0200 Subject: ASoC: SOF: mediatek: Add mt8195 hardware support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch initialize to support SOF on Mediatek mt8195 platform. MT8195 has four Cortex A78 cores paired with four Cortex A55 cores. It also has Cadence HiFi-4 DSP single core. There are shared DRAM and mailbox interrupt between AP and DSP to use for IPC communication. Signed-off-by: YC Hung Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Reviewed-by: Kai Vehmanen Reviewed-by: Guennadi Liakhovetski Reviewed-by: Daniel Baluta Signed-off-by: Daniel Baluta Link: https://lore.kernel.org/r/20211118100749.54628-2-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/Kconfig | 1 + sound/soc/sof/Makefile | 1 + sound/soc/sof/mediatek/Kconfig | 33 ++++ sound/soc/sof/mediatek/Makefile | 2 + sound/soc/sof/mediatek/adsp_helper.h | 49 +++++ sound/soc/sof/mediatek/mediatek-ops.h | 8 + sound/soc/sof/mediatek/mt8195/Makefile | 3 + sound/soc/sof/mediatek/mt8195/mt8195.c | 323 +++++++++++++++++++++++++++++++++ sound/soc/sof/mediatek/mt8195/mt8195.h | 155 ++++++++++++++++ 9 files changed, 575 insertions(+) create mode 100644 sound/soc/sof/mediatek/Kconfig create mode 100644 sound/soc/sof/mediatek/Makefile create mode 100644 sound/soc/sof/mediatek/adsp_helper.h create mode 100644 sound/soc/sof/mediatek/mediatek-ops.h create mode 100644 sound/soc/sof/mediatek/mt8195/Makefile create mode 100644 sound/soc/sof/mediatek/mt8195/mt8195.c create mode 100644 sound/soc/sof/mediatek/mt8195/mt8195.h diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index 0e5473e899cd..b0cdabcfdde7 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -236,6 +236,7 @@ config SND_SOC_SOF_PROBE_WORK_QUEUE source "sound/soc/sof/amd/Kconfig" source "sound/soc/sof/imx/Kconfig" source "sound/soc/sof/intel/Kconfig" +source "sound/soc/sof/mediatek/Kconfig" source "sound/soc/sof/xtensa/Kconfig" endif diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index 81ad8cb666e3..964b429146be 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -24,3 +24,4 @@ obj-$(CONFIG_SND_SOC_SOF_INTEL_TOPLEVEL) += intel/ obj-$(CONFIG_SND_SOC_SOF_IMX_TOPLEVEL) += imx/ obj-$(CONFIG_SND_SOC_SOF_AMD_TOPLEVEL) += amd/ obj-$(CONFIG_SND_SOC_SOF_XTENSA) += xtensa/ +obj-$(CONFIG_SND_SOC_SOF_MTK_TOPLEVEL) += mediatek/ diff --git a/sound/soc/sof/mediatek/Kconfig b/sound/soc/sof/mediatek/Kconfig new file mode 100644 index 000000000000..aeacf0e5bfbb --- /dev/null +++ b/sound/soc/sof/mediatek/Kconfig @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) + +config SND_SOC_SOF_MTK_TOPLEVEL + bool "SOF support for MTK audio DSPs" + depends on ARM64 || COMPILE_TEST + depends on SND_SOC_SOF_OF + help + This adds support for Sound Open Firmware for Mediatek platforms. + It is top level for all mediatek platforms. + Say Y if you have such a device. + If unsure select "N". + +if SND_SOC_SOF_MTK_TOPLEVEL +config SND_SOC_SOF_MTK_COMMON + tristate + select SND_SOC_SOF_OF_DEV + select SND_SOC_SOF + select SND_SOC_SOF_XTENSA + select SND_SOC_SOF_COMPRESS + help + This option is not user-selectable but automagically handled by + 'select' statements at a higher level + +config SND_SOC_SOF_MT8195 + tristate "SOF support for MT8195 audio DSP" + select SND_SOC_SOF_MTK_COMMON + help + This adds support for Sound Open Firmware for Mediatek platforms + using the mt8195 processors. + Say Y if you have such a device. + If unsure select "N". + +endif ## SND_SOC_SOF_MTK_TOPLEVEL diff --git a/sound/soc/sof/mediatek/Makefile b/sound/soc/sof/mediatek/Makefile new file mode 100644 index 000000000000..e8ec6da981de --- /dev/null +++ b/sound/soc/sof/mediatek/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +obj-$(CONFIG_SND_SOC_SOF_MT8195) += mt8195/ diff --git a/sound/soc/sof/mediatek/adsp_helper.h b/sound/soc/sof/mediatek/adsp_helper.h new file mode 100644 index 000000000000..346953dd22db --- /dev/null +++ b/sound/soc/sof/mediatek/adsp_helper.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Copyright (c) 2021 MediaTek Corporation. All rights reserved. + */ + +#ifndef __MTK_ADSP_HELPER_H__ +#define __MTK_ADSP_HELPER_H__ + +/* + * Global important adsp data structure. + */ +#define DSP_MBOX_NUM 3 + +struct mtk_adsp_chip_info { + phys_addr_t pa_sram; + phys_addr_t pa_dram; /* adsp dram physical base */ + phys_addr_t pa_shared_dram; /* adsp dram physical base */ + phys_addr_t pa_cfgreg; + phys_addr_t pa_mboxreg[DSP_MBOX_NUM]; + u32 sramsize; + u32 dramsize; + u32 cfgregsize; + void __iomem *va_sram; /* corresponding to pa_sram */ + void __iomem *va_dram; /* corresponding to pa_dram */ + void __iomem *va_cfgreg; + void __iomem *va_mboxreg[DSP_MBOX_NUM]; + void __iomem *shared_sram; /* part of va_sram */ + void __iomem *shared_dram; /* part of va_dram */ + phys_addr_t adsp_bootup_addr; + int dram_offset; /*dram offset between system and dsp view*/ +}; + +struct adsp_priv { + struct device *dev; + struct snd_sof_dev *sdev; + + /* DSP IPC handler */ + struct mbox_controller *adsp_mbox; + + struct mtk_adsp_chip_info *adsp; + + u32 (*ap2adsp_addr)(u32 addr, void *data); + u32 (*adsp2ap_addr)(u32 addr, void *data); + + void *private_data; +}; + +#endif diff --git a/sound/soc/sof/mediatek/mediatek-ops.h b/sound/soc/sof/mediatek/mediatek-ops.h new file mode 100644 index 000000000000..e0ffa69ecb0c --- /dev/null +++ b/sound/soc/sof/mediatek/mediatek-ops.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ + +#ifndef __MEDIATEK_OPS_H__ +#define __MEDIATEK_OPS_H__ + +extern const struct snd_sof_dsp_ops sof_mt8195_ops; + +#endif diff --git a/sound/soc/sof/mediatek/mt8195/Makefile b/sound/soc/sof/mediatek/mt8195/Makefile new file mode 100644 index 000000000000..dd2b6e4affc9 --- /dev/null +++ b/sound/soc/sof/mediatek/mt8195/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +snd-sof-mt8195-objs := mt8195.o +obj-$(CONFIG_SND_SOC_SOF_MT8195) += snd-sof-mt8195.o diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.c b/sound/soc/sof/mediatek/mt8195/mt8195.c new file mode 100644 index 000000000000..966b8660e21c --- /dev/null +++ b/sound/soc/sof/mediatek/mt8195/mt8195.c @@ -0,0 +1,323 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// Copyright(c) 2021 Mediatek Inc. All rights reserved. +// +// Author: YC Hung +// + +/* + * Hardware interface for audio DSP on mt8195 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "../../ops.h" +#include "../../sof-audio.h" +#include "../adsp_helper.h" +#include "../mediatek-ops.h" +#include "mt8195.h" + +static int platform_parse_resource(struct platform_device *pdev, void *data) +{ + struct resource *mmio; + struct resource res; + struct device_node *mem_region; + struct device *dev = &pdev->dev; + struct mtk_adsp_chip_info *adsp = data; + int ret; + + mem_region = of_parse_phandle(dev->of_node, "memory-region", 0); + if (!mem_region) { + dev_err(dev, "no dma memory-region phandle\n"); + return -ENODEV; + } + + ret = of_address_to_resource(mem_region, 0, &res); + if (ret) { + dev_err(dev, "of_address_to_resource dma failed\n"); + return ret; + } + + dev_dbg(dev, "DMA pbase=0x%llx, size=0x%llx\n", + (phys_addr_t)res.start, resource_size(&res)); + + ret = of_reserved_mem_device_init(dev); + if (ret) { + dev_err(dev, "of_reserved_mem_device_init failed\n"); + return ret; + } + + mem_region = of_parse_phandle(dev->of_node, "memory-region", 1); + if (!mem_region) { + dev_err(dev, "no memory-region sysmem phandle\n"); + return -ENODEV; + } + + ret = of_address_to_resource(mem_region, 0, &res); + if (ret) { + dev_err(dev, "of_address_to_resource sysmem failed\n"); + return ret; + } + + adsp->pa_dram = (phys_addr_t)res.start; + adsp->dramsize = resource_size(&res); + if (adsp->pa_dram & DRAM_REMAP_MASK) { + dev_err(dev, "adsp memory(%#x) is not 4K-aligned\n", + (u32)adsp->pa_dram); + return -EINVAL; + } + + if (adsp->dramsize < TOTAL_SIZE_SHARED_DRAM_FROM_TAIL) { + dev_err(dev, "adsp memory(%#x) is not enough for share\n", + adsp->dramsize); + return -EINVAL; + } + + dev_dbg(dev, "dram pbase=%pa, dramsize=%#x\n", + &adsp->pa_dram, adsp->dramsize); + + /* Parse CFG base */ + mmio = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg"); + if (!mmio) { + dev_err(dev, "no ADSP-CFG register resource\n"); + return -ENXIO; + } + /* remap for DSP register accessing */ + adsp->va_cfgreg = devm_ioremap_resource(dev, mmio); + if (IS_ERR(adsp->va_cfgreg)) + return PTR_ERR(adsp->va_cfgreg); + + adsp->pa_cfgreg = (phys_addr_t)mmio->start; + adsp->cfgregsize = resource_size(mmio); + + dev_dbg(dev, "cfgreg-vbase=%p, cfgregsize=%#x\n", + adsp->va_cfgreg, adsp->cfgregsize); + + /* Parse SRAM */ + mmio = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram"); + if (!mmio) { + dev_err(dev, "no SRAM resource\n"); + return -ENXIO; + } + + adsp->pa_sram = (phys_addr_t)mmio->start; + adsp->sramsize = resource_size(mmio); + if (adsp->sramsize < TOTAL_SIZE_SHARED_SRAM_FROM_TAIL) { + dev_err(dev, "adsp SRAM(%#x) is not enough for share\n", + adsp->sramsize); + return -EINVAL; + } + + dev_dbg(dev, "sram pbase=%pa,%#x\n", &adsp->pa_sram, adsp->sramsize); + + return ret; +} + +static int adsp_sram_power_on(struct device *dev, bool on) +{ + void __iomem *va_dspsysreg; + u32 srampool_con; + + va_dspsysreg = ioremap(ADSP_SRAM_POOL_CON, 0x4); + if (!va_dspsysreg) { + dev_err(dev, "failed to ioremap sram pool base %#x\n", + ADSP_SRAM_POOL_CON); + return -ENOMEM; + } + + srampool_con = readl(va_dspsysreg); + if (on) + writel(srampool_con & ~DSP_SRAM_POOL_PD_MASK, va_dspsysreg); + else + writel(srampool_con | DSP_SRAM_POOL_PD_MASK, va_dspsysreg); + + iounmap(va_dspsysreg); + return 0; +} + +/* Init the basic DSP DRAM address */ +static int adsp_memory_remap_init(struct device *dev, struct mtk_adsp_chip_info *adsp) +{ + void __iomem *vaddr_emi_map; + int offset; + + if (!adsp) + return -ENXIO; + + vaddr_emi_map = devm_ioremap(dev, DSP_EMI_MAP_ADDR, 0x4); + if (!vaddr_emi_map) { + dev_err(dev, "failed to ioremap emi map base %#x\n", + DSP_EMI_MAP_ADDR); + return -ENOMEM; + } + + offset = adsp->pa_dram - DRAM_PHYS_BASE_FROM_DSP_VIEW; + adsp->dram_offset = offset; + offset >>= DRAM_REMAP_SHIFT; + dev_dbg(dev, "adsp->pa_dram %llx, offset %#x\n", adsp->pa_dram, offset); + writel(offset, vaddr_emi_map); + if (offset != readl(vaddr_emi_map)) { + dev_err(dev, "write emi map fail : %#x\n", readl(vaddr_emi_map)); + return -EIO; + } + + return 0; +} + +static int adsp_shared_base_ioremap(struct platform_device *pdev, void *data) +{ + struct device *dev = &pdev->dev; + struct mtk_adsp_chip_info *adsp = data; + u32 shared_size; + + /* remap shared-dram base to be non-cachable */ + shared_size = TOTAL_SIZE_SHARED_DRAM_FROM_TAIL; + adsp->pa_shared_dram = adsp->pa_dram + adsp->dramsize - shared_size; + if (adsp->va_dram) { + adsp->shared_dram = adsp->va_dram + DSP_DRAM_SIZE - shared_size; + } else { + adsp->shared_dram = devm_ioremap(dev, adsp->pa_shared_dram, + shared_size); + if (!adsp->shared_dram) { + dev_err(dev, "ioremap failed for shared DRAM\n"); + return -ENOMEM; + } + } + dev_dbg(dev, "shared-dram vbase=%p, phy addr :%llx, size=%#x\n", + adsp->shared_dram, adsp->pa_shared_dram, shared_size); + + return 0; +} + +static int mt8195_dsp_probe(struct snd_sof_dev *sdev) +{ + struct platform_device *pdev = container_of(sdev->dev, struct platform_device, dev); + struct adsp_priv *priv; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + sdev->pdata->hw_pdata = priv; + priv->dev = sdev->dev; + priv->sdev = sdev; + + priv->adsp = devm_kzalloc(&pdev->dev, sizeof(struct mtk_adsp_chip_info), GFP_KERNEL); + if (!priv->adsp) + return -ENOMEM; + + ret = platform_parse_resource(pdev, priv->adsp); + if (ret) + return ret; + + ret = adsp_sram_power_on(sdev->dev, true); + if (ret) { + dev_err(sdev->dev, "adsp_sram_power_on fail!\n"); + return ret; + } + + ret = adsp_memory_remap_init(&pdev->dev, priv->adsp); + if (ret) { + dev_err(sdev->dev, "adsp_memory_remap_init fail!\n"); + goto err_adsp_sram_power_off; + } + + sdev->bar[SOF_FW_BLK_TYPE_IRAM] = devm_ioremap(sdev->dev, + priv->adsp->pa_sram, + priv->adsp->sramsize); + if (!sdev->bar[SOF_FW_BLK_TYPE_IRAM]) { + dev_err(sdev->dev, "failed to ioremap base %pa size %#x\n", + &priv->adsp->pa_sram, priv->adsp->sramsize); + ret = -EINVAL; + goto err_adsp_sram_power_off; + } + + sdev->bar[SOF_FW_BLK_TYPE_SRAM] = devm_ioremap_wc(sdev->dev, + priv->adsp->pa_dram, + priv->adsp->dramsize); + if (!sdev->bar[SOF_FW_BLK_TYPE_SRAM]) { + dev_err(sdev->dev, "failed to ioremap base %pa size %#x\n", + &priv->adsp->pa_dram, priv->adsp->dramsize); + ret = -EINVAL; + goto err_adsp_sram_power_off; + } + priv->adsp->va_dram = sdev->bar[SOF_FW_BLK_TYPE_SRAM]; + + ret = adsp_shared_base_ioremap(pdev, priv->adsp); + if (ret) { + dev_err(sdev->dev, "adsp_shared_base_ioremap fail!\n"); + goto err_adsp_sram_power_off; + } + + sdev->bar[DSP_REG_BAR] = priv->adsp->va_cfgreg; + sdev->bar[DSP_MBOX0_BAR] = priv->adsp->va_mboxreg[0]; + sdev->bar[DSP_MBOX1_BAR] = priv->adsp->va_mboxreg[1]; + sdev->bar[DSP_MBOX2_BAR] = priv->adsp->va_mboxreg[2]; + + sdev->mmio_bar = SOF_FW_BLK_TYPE_SRAM; + sdev->mailbox_bar = SOF_FW_BLK_TYPE_SRAM; + + return 0; + +err_adsp_sram_power_off: + adsp_sram_power_on(&pdev->dev, false); + + return ret; +} + +static int mt8195_dsp_remove(struct snd_sof_dev *sdev) +{ + struct platform_device *pdev = container_of(sdev->dev, struct platform_device, dev); + + return adsp_sram_power_on(&pdev->dev, false); +} + +/* on mt8195 there is 1 to 1 match between type and BAR idx */ +static int mt8195_get_bar_index(struct snd_sof_dev *sdev, u32 type) +{ + return type; +} + +/* mt8195 ops */ +const struct snd_sof_dsp_ops sof_mt8195_ops = { + /* probe and remove */ + .probe = mt8195_dsp_probe, + .remove = mt8195_dsp_remove, + + /* Block IO */ + .block_read = sof_block_read, + .block_write = sof_block_write, + + /* Register IO */ + .write = sof_io_write, + .read = sof_io_read, + .write64 = sof_io_write64, + .read64 = sof_io_read64, + + /* misc */ + .get_bar_index = mt8195_get_bar_index, + + /* Firmware ops */ + .dsp_arch_ops = &sof_xtensa_arch_ops, + + /* ALSA HW info flags */ + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, +}; +EXPORT_SYMBOL(sof_mt8195_ops); + +MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.h b/sound/soc/sof/mediatek/mt8195/mt8195.h new file mode 100644 index 000000000000..48cbbb5aacb5 --- /dev/null +++ b/sound/soc/sof/mediatek/mt8195/mt8195.h @@ -0,0 +1,155 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Copyright (c) 2021 MediaTek Corporation. All rights reserved. + * + * Header file for the mt8195 DSP register definition + */ + +#ifndef __MT8195_H +#define __MT8195_H + +struct mtk_adsp_chip_info; + +#define DSP_REG_BASE 0x10803000 +#define SCP_CFGREG_BASE 0x10724000 +#define DSP_SYSAO_BASE 0x1080C000 + +/***************************************************************************** + * R E G I S T E R TABLE + *****************************************************************************/ +#define DSP_JTAGMUX 0x0000 +#define DSP_ALTRESETVEC 0x0004 +#define DSP_PDEBUGDATA 0x0008 +#define DSP_PDEBUGBUS0 0x000c +#define PDEBUG_ENABLE BIT(0) +#define DSP_PDEBUGBUS1 0x0010 +#define DSP_PDEBUGINST 0x0014 +#define DSP_PDEBUGLS0STAT 0x0018 +#define DSP_PDEBUGLS1STAT 0x001c +#define DSP_PDEBUGPC 0x0020 +#define DSP_RESET_SW 0x0024 /*reset sw*/ +#define ADSP_BRESET_SW BIT(0) +#define ADSP_DRESET_SW BIT(1) +#define ADSP_RUNSTALL BIT(3) +#define STATVECTOR_SEL BIT(4) +#define DSP_PFAULTBUS 0x0028 +#define DSP_PFAULTINFO 0x002c +#define DSP_GPR00 0x0030 +#define DSP_GPR01 0x0034 +#define DSP_GPR02 0x0038 +#define DSP_GPR03 0x003c +#define DSP_GPR04 0x0040 +#define DSP_GPR05 0x0044 +#define DSP_GPR06 0x0048 +#define DSP_GPR07 0x004c +#define DSP_GPR08 0x0050 +#define DSP_GPR09 0x0054 +#define DSP_GPR0A 0x0058 +#define DSP_GPR0B 0x005c +#define DSP_GPR0C 0x0060 +#define DSP_GPR0D 0x0064 +#define DSP_GPR0E 0x0068 +#define DSP_GPR0F 0x006c +#define DSP_GPR10 0x0070 +#define DSP_GPR11 0x0074 +#define DSP_GPR12 0x0078 +#define DSP_GPR13 0x007c +#define DSP_GPR14 0x0080 +#define DSP_GPR15 0x0084 +#define DSP_GPR16 0x0088 +#define DSP_GPR17 0x008c +#define DSP_GPR18 0x0090 +#define DSP_GPR19 0x0094 +#define DSP_GPR1A 0x0098 +#define DSP_GPR1B 0x009c +#define DSP_GPR1C 0x00a0 +#define DSP_GPR1D 0x00a4 +#define DSP_GPR1E 0x00a8 +#define DSP_GPR1F 0x00ac +#define DSP_TCM_OFFSET 0x00b0 /* not used */ +#define DSP_DDR_OFFSET 0x00b4 /* not used */ +#define DSP_INTFDSP 0x00d0 +#define DSP_INTFDSP_CLR 0x00d4 +#define DSP_SRAM_PD_SW1 0x00d8 +#define DSP_SRAM_PD_SW2 0x00dc +#define DSP_OCD 0x00e0 +#define DSP_RG_DSP_IRQ_POL 0x00f0 /* not used */ +#define DSP_DSP_IRQ_EN 0x00f4 /* not used */ +#define DSP_DSP_IRQ_LEVEL 0x00f8 /* not used */ +#define DSP_DSP_IRQ_STATUS 0x00fc /* not used */ +#define DSP_RG_INT2CIRQ 0x0114 +#define DSP_RG_INT_POL_CTL0 0x0120 +#define DSP_RG_INT_EN_CTL0 0x0130 +#define DSP_RG_INT_LV_CTL0 0x0140 +#define DSP_RG_INT_STATUS0 0x0150 +#define DSP_PDEBUGSTATUS0 0x0200 +#define DSP_PDEBUGSTATUS1 0x0204 +#define DSP_PDEBUGSTATUS2 0x0208 +#define DSP_PDEBUGSTATUS3 0x020c +#define DSP_PDEBUGSTATUS4 0x0210 +#define DSP_PDEBUGSTATUS5 0x0214 +#define DSP_PDEBUGSTATUS6 0x0218 +#define DSP_PDEBUGSTATUS7 0x021c +#define DSP_DSP2PSRAM_PRIORITY 0x0220 /* not used */ +#define DSP_AUDIO_DSP2SPM_INT 0x0224 +#define DSP_AUDIO_DSP2SPM_INT_ACK 0x0228 +#define DSP_AUDIO_DSP_DEBUG_SEL 0x022C +#define DSP_AUDIO_DSP_EMI_BASE_ADDR 0x02E0 /* not used */ +#define DSP_AUDIO_DSP_SHARED_IRAM 0x02E4 +#define DSP_AUDIO_DSP_CKCTRL_P2P_CK_CON 0x02F0 +#define DSP_RG_SEMAPHORE00 0x0300 +#define DSP_RG_SEMAPHORE01 0x0304 +#define DSP_RG_SEMAPHORE02 0x0308 +#define DSP_RG_SEMAPHORE03 0x030C +#define DSP_RG_SEMAPHORE04 0x0310 +#define DSP_RG_SEMAPHORE05 0x0314 +#define DSP_RG_SEMAPHORE06 0x0318 +#define DSP_RG_SEMAPHORE07 0x031C +#define DSP_RESERVED_0 0x03F0 +#define DSP_RESERVED_1 0x03F4 + +/* dsp wdt */ +#define DSP_WDT_MODE 0x0400 + +/* dsp mbox */ +#define DSP_MBOX_IN_CMD 0x00 +#define DSP_MBOX_IN_CMD_CLR 0x04 +#define DSP_MBOX_OUT_CMD 0x1c +#define DSP_MBOX_OUT_CMD_CLR 0x20 +#define DSP_MBOX_IN_MSG0 0x08 +#define DSP_MBOX_IN_MSG1 0x0C +#define DSP_MBOX_OUT_MSG0 0x24 +#define DSP_MBOX_OUT_MSG1 0x28 + +/*dsp sys ao*/ +#define ADSP_SRAM_POOL_CON (DSP_SYSAO_BASE + 0x30) +#define DSP_SRAM_POOL_PD_MASK 0xf +#define DSP_EMI_MAP_ADDR (DSP_SYSAO_BASE + 0x81c) + +/* DSP memories */ +#define MBOX_OFFSET 0x800000 /* DRAM */ +#define MBOX_SIZE 0x1000 /* consistent with which in memory.h of sof fw */ +#define DSP_DRAM_SIZE 0x1000000 /* 16M */ + +#define DSP_REG_BAR 4 +#define DSP_MBOX0_BAR 5 +#define DSP_MBOX1_BAR 6 +#define DSP_MBOX2_BAR 7 + +#define TOTAL_SIZE_SHARED_SRAM_FROM_TAIL 0x0 + +#define SIZE_SHARED_DRAM_DL 0x40000 /*Shared buffer for Downlink*/ +#define SIZE_SHARED_DRAM_UL 0x40000 /*Shared buffer for Uplink*/ + +#define TOTAL_SIZE_SHARED_DRAM_FROM_TAIL \ + (SIZE_SHARED_DRAM_DL + SIZE_SHARED_DRAM_UL) + +#define SRAM_PHYS_BASE_FROM_DSP_VIEW 0x40000000 /* MT8195 DSP view */ +#define DRAM_PHYS_BASE_FROM_DSP_VIEW 0x60000000 /* MT8195 DSP view */ + +/*remap dram between AP and DSP view, 4KB aligned*/ +#define DRAM_REMAP_SHIFT 12 +#define DRAM_REMAP_MASK (BIT(DRAM_REMAP_SHIFT) - 1) + +#endif -- cgit v1.2.3 From e6feefa541f309afed8aa54431681261bc57bcde Mon Sep 17 00:00:00 2001 From: YC Hung Date: Thu, 18 Nov 2021 12:07:43 +0200 Subject: ASoC: SOF: tokens: add token for Mediatek AFE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the definition for Mediatek audio front end(AFE) tokens,include AFE sampling rate, channels, and format. Signed-off-by: YC Hung Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Reviewed-by: Kai Vehmanen Reviewed-by: Guennadi Liakhovetski Reviewed-by: Daniel Baluta Signed-off-by: Daniel Baluta Link: https://lore.kernel.org/r/20211118100749.54628-3-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- include/uapi/sound/sof/tokens.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h index 02b71a8deea4..b72fa385bebf 100644 --- a/include/uapi/sound/sof/tokens.h +++ b/include/uapi/sound/sof/tokens.h @@ -140,4 +140,9 @@ #define SOF_TKN_INTEL_HDA_RATE 1500 #define SOF_TKN_INTEL_HDA_CH 1501 +/* AFE */ +#define SOF_TKN_MEDIATEK_AFE_RATE 1600 +#define SOF_TKN_MEDIATEK_AFE_CH 1601 +#define SOF_TKN_MEDIATEK_AFE_FORMAT 1602 + #endif -- cgit v1.2.3 From b72bfcffcfc11858a8fc92998733372606db485e Mon Sep 17 00:00:00 2001 From: YC Hung Date: Thu, 18 Nov 2021 12:07:44 +0200 Subject: ASoC: SOF: topology: Add support for Mediatek AFE DAI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add new sof dai and config to pass topology file configuration to SOF firmware running on Mediatek platform DSP core. Add mediatek audio front end(AFE) to the list of supported sof_dais Signed-off-by: YC Hung Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Reviewed-by: Kai Vehmanen Reviewed-by: Guennadi Liakhovetski Reviewed-by: Daniel Baluta Signed-off-by: Daniel Baluta Link: https://lore.kernel.org/r/20211118100749.54628-4-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- include/sound/sof/dai-mediatek.h | 23 ++++++++++++++++ include/sound/sof/dai.h | 3 ++ sound/soc/sof/pcm.c | 12 ++++++++ sound/soc/sof/topology.c | 59 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 97 insertions(+) create mode 100644 include/sound/sof/dai-mediatek.h diff --git a/include/sound/sof/dai-mediatek.h b/include/sound/sof/dai-mediatek.h new file mode 100644 index 000000000000..62dd4720558d --- /dev/null +++ b/include/sound/sof/dai-mediatek.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * Copyright(c) 2021 Mediatek Corporation. All rights reserved. + * + * Author: Bo Pan + */ + +#ifndef __INCLUDE_SOUND_SOF_DAI_MEDIATEK_H__ +#define __INCLUDE_SOUND_SOF_DAI_MEDIATEK_H__ + +#include + +struct sof_ipc_dai_mtk_afe_params { + struct sof_ipc_hdr hdr; + u32 channels; + u32 rate; + u32 format; + u32 stream_id; + u32 reserved[4]; /* reserve for future */ +} __packed; + +#endif + diff --git a/include/sound/sof/dai.h b/include/sound/sof/dai.h index 3782127a7095..5132bc60f54b 100644 --- a/include/sound/sof/dai.h +++ b/include/sound/sof/dai.h @@ -13,6 +13,7 @@ #include #include #include +#include /* * DAI Configuration. @@ -70,6 +71,7 @@ enum sof_ipc_dai_type { SOF_DAI_AMD_BT, /**< AMD ACP BT*/ SOF_DAI_AMD_SP, /**< AMD ACP SP */ SOF_DAI_AMD_DMIC, /**< AMD ACP DMIC */ + SOF_DAI_MEDIATEK_AFE, /**< Mediatek AFE */ }; /* general purpose DAI configuration */ @@ -97,6 +99,7 @@ struct sof_ipc_dai_config { struct sof_ipc_dai_acp_params acpbt; struct sof_ipc_dai_acp_params acpsp; struct sof_ipc_dai_acp_params acpdmic; + struct sof_ipc_dai_mtk_afe_params afe; }; } __packed; diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 8d313c9862cb..31dd79b794f1 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -808,6 +808,18 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa channels->min = dai->dai_config->esai.tdm_slots; channels->max = dai->dai_config->esai.tdm_slots; + dev_dbg(component->dev, + "rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, + "channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; + case SOF_DAI_MEDIATEK_AFE: + rate->min = dai->dai_config->afe.rate; + rate->max = dai->dai_config->afe.rate; + channels->min = dai->dai_config->afe.channels; + channels->max = dai->dai_config->afe.channels; + dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max); dev_dbg(component->dev, diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 72e671c15a34..10caf2b1a33c 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -379,6 +379,7 @@ static const struct sof_dai_types sof_dais[] = { {"ACP", SOF_DAI_AMD_BT}, {"ACPSP", SOF_DAI_AMD_SP}, {"ACPDMIC", SOF_DAI_AMD_DMIC}, + {"AFE", SOF_DAI_MEDIATEK_AFE}, }; static enum sof_ipc_dai_type find_dai(const char *name) @@ -806,6 +807,19 @@ static const struct sof_topology_token led_tokens[] = { get_token_u32, offsetof(struct snd_sof_led_control, direction), 0}, }; +/* AFE */ +static const struct sof_topology_token afe_tokens[] = { + {SOF_TKN_MEDIATEK_AFE_RATE, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_mtk_afe_params, rate), 0}, + {SOF_TKN_MEDIATEK_AFE_CH, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_mtk_afe_params, channels), 0}, + {SOF_TKN_MEDIATEK_AFE_FORMAT, + SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_format, + offsetof(struct sof_ipc_dai_mtk_afe_params, format), 0}, +}; + static int sof_parse_uuid_tokens(struct snd_soc_component *scomp, void *object, const struct sof_topology_token *tokens, @@ -3091,6 +3105,48 @@ static int sof_link_acp_sp_load(struct snd_soc_component *scomp, int index, return ret; } +static int sof_link_afe_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg, + struct snd_soc_tplg_hw_config *hw_config, + struct sof_ipc_dai_config *config) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &cfg->priv; + struct snd_soc_dai *dai; + u32 size = sizeof(*config); + int ret; + + config->hdr.size = size; + + /* get any bespoke DAI tokens */ + ret = sof_parse_tokens(scomp, &config->afe, afe_tokens, + ARRAY_SIZE(afe_tokens), private->array, + le32_to_cpu(private->size)); + if (ret != 0) { + dev_err(scomp->dev, "parse afe tokens failed %d\n", + le32_to_cpu(private->size)); + return ret; + } + + dev_dbg(scomp->dev, "AFE config rate %d channels %d format:%d\n", + config->afe.rate, config->afe.channels, config->afe.format); + + dai = snd_soc_find_dai(link->cpus); + if (!dai) { + dev_err(scomp->dev, "%s: failed to find dai %s", __func__, link->cpus->dai_name); + return -EINVAL; + } + + config->afe.stream_id = DMA_CHAN_INVALID; + + ret = sof_set_dai_config(sdev, size, link, config); + if (ret < 0) + dev_err(scomp->dev, "failed to process afe dai link %s", link->name); + + return ret; +} + static int sof_link_dmic_load(struct snd_soc_component *scomp, int index, struct snd_soc_dai_link *link, struct snd_soc_tplg_link_config *cfg, @@ -3386,6 +3442,9 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, ret = sof_link_acp_dmic_load(scomp, index, link, cfg, hw_config + curr_conf, config); break; + case SOF_DAI_MEDIATEK_AFE: + ret = sof_link_afe_load(scomp, index, link, cfg, hw_config + curr_conf, config); + break; default: dev_err(scomp->dev, "error: invalid DAI type %d\n", common_config.type); ret = -EINVAL; -- cgit v1.2.3 From b7f6503830cd8f3f7076635409460861b5ff6310 Mon Sep 17 00:00:00 2001 From: YC Hung Date: Thu, 18 Nov 2021 12:07:45 +0200 Subject: ASoC: SOF: mediatek: Add fw loader and mt8195 dsp ops to load firmware MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add mt8195-loader module with ops callback to load and run firmware on mt8195 platform. Signed-off-by: YC Hung Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Reviewed-by: Kai Vehmanen Reviewed-by: Guennadi Liakhovetski Reviewed-by: Daniel Baluta Signed-off-by: Daniel Baluta Link: https://lore.kernel.org/r/20211118100749.54628-5-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/mediatek/mt8195/Makefile | 2 +- sound/soc/sof/mediatek/mt8195/mt8195-loader.c | 56 +++++++++++++++++++++++++++ sound/soc/sof/mediatek/mt8195/mt8195.c | 19 +++++++++ sound/soc/sof/mediatek/mt8195/mt8195.h | 3 ++ 4 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 sound/soc/sof/mediatek/mt8195/mt8195-loader.c diff --git a/sound/soc/sof/mediatek/mt8195/Makefile b/sound/soc/sof/mediatek/mt8195/Makefile index dd2b6e4affc9..66cdc0e7bf3c 100644 --- a/sound/soc/sof/mediatek/mt8195/Makefile +++ b/sound/soc/sof/mediatek/mt8195/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) -snd-sof-mt8195-objs := mt8195.o +snd-sof-mt8195-objs := mt8195.o mt8195-loader.o obj-$(CONFIG_SND_SOC_SOF_MT8195) += snd-sof-mt8195.o diff --git a/sound/soc/sof/mediatek/mt8195/mt8195-loader.c b/sound/soc/sof/mediatek/mt8195/mt8195-loader.c new file mode 100644 index 000000000000..ed18d6379e92 --- /dev/null +++ b/sound/soc/sof/mediatek/mt8195/mt8195-loader.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// Copyright (c) 2021 Mediatek Corporation. All rights reserved. +// +// Author: YC Hung +// +// Hardware interface for mt8195 DSP code loader + +#include +#include "mt8195.h" +#include "../../ops.h" + +void sof_hifixdsp_boot_sequence(struct snd_sof_dev *sdev, u32 boot_addr) +{ + /* ADSP bootup base */ + snd_sof_dsp_write(sdev, DSP_REG_BAR, DSP_ALTRESETVEC, boot_addr); + + /* pull high RunStall (set bit3 to 1) */ + snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, DSP_RESET_SW, + ADSP_RUNSTALL, ADSP_RUNSTALL); + + /* pull high StatVectorSel to use AltResetVec (set bit4 to 1) */ + snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, DSP_RESET_SW, + DSP_RESET_SW, DSP_RESET_SW); + + /* toggle DReset & BReset */ + /* pull high DReset & BReset */ + snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, DSP_RESET_SW, + ADSP_BRESET_SW | ADSP_DRESET_SW, + ADSP_BRESET_SW | ADSP_DRESET_SW); + + /* pull low DReset & BReset */ + snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, DSP_RESET_SW, + ADSP_BRESET_SW | ADSP_DRESET_SW, + 0); + + /* Enable PDebug */ + snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, DSP_PDEBUGBUS0, + PDEBUG_ENABLE, + PDEBUG_ENABLE); + + /* release RunStall (set bit3 to 0) */ + snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, DSP_RESET_SW, + ADSP_RUNSTALL, 0); +} + +void sof_hifixdsp_shutdown(struct snd_sof_dev *sdev) +{ + /* Clear to 0 firstly */ + snd_sof_dsp_write(sdev, DSP_REG_BAR, DSP_RESET_SW, 0x0); + + /* RUN_STALL pull high again to reset */ + snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, DSP_RESET_SW, + ADSP_RUNSTALL, ADSP_RUNSTALL); +} + diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.c b/sound/soc/sof/mediatek/mt8195/mt8195.c index 966b8660e21c..88da6c2de070 100644 --- a/sound/soc/sof/mediatek/mt8195/mt8195.c +++ b/sound/soc/sof/mediatek/mt8195/mt8195.c @@ -198,6 +198,17 @@ static int adsp_shared_base_ioremap(struct platform_device *pdev, void *data) return 0; } +static int mt8195_run(struct snd_sof_dev *sdev) +{ + u32 adsp_bootup_addr; + + adsp_bootup_addr = SRAM_PHYS_BASE_FROM_DSP_VIEW; + dev_dbg(sdev->dev, "HIFIxDSP boot from base : 0x%08X\n", adsp_bootup_addr); + sof_hifixdsp_boot_sequence(sdev, adsp_bootup_addr); + + return 0; +} + static int mt8195_dsp_probe(struct snd_sof_dev *sdev) { struct platform_device *pdev = container_of(sdev->dev, struct platform_device, dev); @@ -294,6 +305,9 @@ const struct snd_sof_dsp_ops sof_mt8195_ops = { .probe = mt8195_dsp_probe, .remove = mt8195_dsp_remove, + /* DSP core boot */ + .run = mt8195_run, + /* Block IO */ .block_read = sof_block_read, .block_write = sof_block_write, @@ -307,6 +321,11 @@ const struct snd_sof_dsp_ops sof_mt8195_ops = { /* misc */ .get_bar_index = mt8195_get_bar_index, + /* module loading */ + .load_module = snd_sof_parse_module_memcpy, + /* firmware loading */ + .load_firmware = snd_sof_load_firmware_memcpy, + /* Firmware ops */ .dsp_arch_ops = &sof_xtensa_arch_ops, diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.h b/sound/soc/sof/mediatek/mt8195/mt8195.h index 48cbbb5aacb5..929424182357 100644 --- a/sound/soc/sof/mediatek/mt8195/mt8195.h +++ b/sound/soc/sof/mediatek/mt8195/mt8195.h @@ -10,6 +10,7 @@ #define __MT8195_H struct mtk_adsp_chip_info; +struct snd_sof_dev; #define DSP_REG_BASE 0x10803000 #define SCP_CFGREG_BASE 0x10724000 @@ -152,4 +153,6 @@ struct mtk_adsp_chip_info; #define DRAM_REMAP_SHIFT 12 #define DRAM_REMAP_MASK (BIT(DRAM_REMAP_SHIFT) - 1) +void sof_hifixdsp_boot_sequence(struct snd_sof_dev *sdev, u32 boot_addr); +void sof_hifixdsp_shutdown(struct snd_sof_dev *sdev); #endif -- cgit v1.2.3 From 24281bc2bf1884e665dfbcd17aaaabbc5872e501 Mon Sep 17 00:00:00 2001 From: YC Hung Date: Thu, 18 Nov 2021 12:07:46 +0200 Subject: ASoC: SOF: Add mt8195 device descriptor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add SOF device and DT descriptor for Mediatek mt8195 platform. Signed-off-by: YC Hung Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Reviewed-by: Kai Vehmanen Reviewed-by: Guennadi Liakhovetski Reviewed-by: Daniel Baluta Signed-off-by: Daniel Baluta Link: https://lore.kernel.org/r/20211118100749.54628-6-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/Kconfig | 2 +- sound/soc/sof/sof-of-dev.c | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index b0cdabcfdde7..ac34c330cf0c 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -42,7 +42,7 @@ config SND_SOC_SOF_OF depends on OF || COMPILE_TEST help This adds support for Device Tree enumeration. This option is - required to enable i.MX8 devices. + required to enable i.MX8 or Mediatek devices. Say Y if you need this option. If unsure select "N". config SND_SOC_SOF_OF_DEV diff --git a/sound/soc/sof/sof-of-dev.c b/sound/soc/sof/sof-of-dev.c index 885430a42226..412cbb824b84 100644 --- a/sound/soc/sof/sof-of-dev.c +++ b/sound/soc/sof/sof-of-dev.c @@ -13,6 +13,7 @@ #include "ops.h" #include "imx/imx-ops.h" +#include "mediatek/mediatek-ops.h" static char *fw_path; module_param(fw_path, charp, 0444); @@ -50,6 +51,15 @@ static struct sof_dev_desc sof_of_imx8mp_desc = { .ops = &sof_imx8m_ops, }; #endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_MT8195) +static const struct sof_dev_desc sof_of_mt8195_desc = { + .default_fw_path = "mediatek/sof", + .default_tplg_path = "mediatek/sof-tplg", + .default_fw_filename = "sof-mt8195.ri", + .nocodec_tplg_filename = "sof-mt8195-nocodec.tplg", + .ops = &sof_mt8195_ops, +}; +#endif static const struct dev_pm_ops sof_of_pm = { .prepare = snd_sof_prepare, @@ -130,6 +140,9 @@ static const struct of_device_id sof_of_ids[] = { #endif #if IS_ENABLED(CONFIG_SND_SOC_SOF_IMX8M) { .compatible = "fsl,imx8mp-dsp", .data = &sof_of_imx8mp_desc}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_MT8195) + { .compatible = "mediatek,mt8195-dsp", .data = &sof_of_mt8195_desc}, #endif { } }; -- cgit v1.2.3 From 24d75049c5ed5193bd12ce0d43c355c4ef74a7fa Mon Sep 17 00:00:00 2001 From: YC Hung Date: Thu, 18 Nov 2021 12:07:47 +0200 Subject: ASoC: SOF: mediatek: Add dai driver dsp ops callback for mt8195 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add dsp ops callback to register AFE DL2/DL3/UL4/UL5 sof dai's with ALSA Signed-off-by: YC Hung Reviewed-by: Péter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Daniel Baluta Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Reviewed-by: Guennadi Liakhovetski Signed-off-by: Daniel Baluta Link: https://lore.kernel.org/r/20211118100749.54628-7-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/mediatek/mt8195/mt8195.c | 35 ++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.c b/sound/soc/sof/mediatek/mt8195/mt8195.c index 88da6c2de070..99075598a35a 100644 --- a/sound/soc/sof/mediatek/mt8195/mt8195.c +++ b/sound/soc/sof/mediatek/mt8195/mt8195.c @@ -299,6 +299,37 @@ static int mt8195_get_bar_index(struct snd_sof_dev *sdev, u32 type) return type; } +static struct snd_soc_dai_driver mt8195_dai[] = { +{ + .name = "SOF_DL2", + .playback = { + .channels_min = 1, + .channels_max = 2, + }, +}, +{ + .name = "SOF_DL3", + .playback = { + .channels_min = 1, + .channels_max = 2, + }, +}, +{ + .name = "SOF_UL4", + .capture = { + .channels_min = 1, + .channels_max = 2, + }, +}, +{ + .name = "SOF_UL5", + .capture = { + .channels_min = 1, + .channels_max = 2, + }, +}, +}; + /* mt8195 ops */ const struct snd_sof_dsp_ops sof_mt8195_ops = { /* probe and remove */ @@ -329,6 +360,10 @@ const struct snd_sof_dsp_ops sof_mt8195_ops = { /* Firmware ops */ .dsp_arch_ops = &sof_xtensa_arch_ops, + /* DAI drivers */ + .drv = mt8195_dai, + .num_drv = ARRAY_SIZE(mt8195_dai), + /* ALSA HW info flags */ .hw_info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | -- cgit v1.2.3 From 424d6d1a9a51b7e6ab397132700a237082d64cf4 Mon Sep 17 00:00:00 2001 From: YC Hung Date: Thu, 18 Nov 2021 12:07:48 +0200 Subject: ASoC: SOF: mediatek: Add mt8195 dsp clock support Add adsp clock on/off support on mt8195 platform. Signed-off-by: YC Hung Reviewed-by: Pierre-Louis Bossart Reviewed-by: Daniel Baluta Signed-off-by: Daniel Baluta Link: https://lore.kernel.org/r/20211118100749.54628-8-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/mediatek/adsp_helper.h | 2 +- sound/soc/sof/mediatek/mt8195/Makefile | 2 +- sound/soc/sof/mediatek/mt8195/mt8195-clk.c | 158 +++++++++++++++++++++++++++++ sound/soc/sof/mediatek/mt8195/mt8195-clk.h | 28 +++++ sound/soc/sof/mediatek/mt8195/mt8195.c | 22 +++- 5 files changed, 208 insertions(+), 4 deletions(-) create mode 100644 sound/soc/sof/mediatek/mt8195/mt8195-clk.c create mode 100644 sound/soc/sof/mediatek/mt8195/mt8195-clk.h diff --git a/sound/soc/sof/mediatek/adsp_helper.h b/sound/soc/sof/mediatek/adsp_helper.h index 346953dd22db..6734e2c0c6b1 100644 --- a/sound/soc/sof/mediatek/adsp_helper.h +++ b/sound/soc/sof/mediatek/adsp_helper.h @@ -39,7 +39,7 @@ struct adsp_priv { struct mbox_controller *adsp_mbox; struct mtk_adsp_chip_info *adsp; - + struct clk **clk; u32 (*ap2adsp_addr)(u32 addr, void *data); u32 (*adsp2ap_addr)(u32 addr, void *data); diff --git a/sound/soc/sof/mediatek/mt8195/Makefile b/sound/soc/sof/mediatek/mt8195/Makefile index 66cdc0e7bf3c..afc4f21fccc5 100644 --- a/sound/soc/sof/mediatek/mt8195/Makefile +++ b/sound/soc/sof/mediatek/mt8195/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) -snd-sof-mt8195-objs := mt8195.o mt8195-loader.o +snd-sof-mt8195-objs := mt8195.o mt8195-clk.o mt8195-loader.o obj-$(CONFIG_SND_SOC_SOF_MT8195) += snd-sof-mt8195.o diff --git a/sound/soc/sof/mediatek/mt8195/mt8195-clk.c b/sound/soc/sof/mediatek/mt8195/mt8195-clk.c new file mode 100644 index 000000000000..6bcb4b9b00fb --- /dev/null +++ b/sound/soc/sof/mediatek/mt8195/mt8195-clk.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// Copyright(c) 2021 Mediatek Corporation. All rights reserved. +// +// Author: YC Hung +// +// Hardware interface for mt8195 DSP clock + +#include +#include +#include +#include "mt8195.h" +#include "mt8195-clk.h" +#include "../adsp_helper.h" +#include "../../sof-audio.h" + +static const char *adsp_clks[ADSP_CLK_MAX] = { + [CLK_TOP_ADSP] = "adsp_sel", + [CLK_TOP_CLK26M] = "clk26m_ck", + [CLK_TOP_AUDIO_LOCAL_BUS] = "audio_local_bus", + [CLK_TOP_MAINPLL_D7_D2] = "mainpll_d7_d2", + [CLK_SCP_ADSP_AUDIODSP] = "scp_adsp_audiodsp", + [CLK_TOP_AUDIO_H] = "audio_h", +}; + +int mt8195_adsp_init_clock(struct snd_sof_dev *sdev) +{ + struct device *dev = sdev->dev; + struct adsp_priv *priv = sdev->pdata->hw_pdata; + int i; + + priv->clk = devm_kcalloc(dev, ADSP_CLK_MAX, sizeof(*priv->clk), GFP_KERNEL); + + if (!priv->clk) + return -ENOMEM; + + for (i = 0; i < ADSP_CLK_MAX; i++) { + priv->clk[i] = devm_clk_get(dev, adsp_clks[i]); + if (IS_ERR(priv->clk[i])) + return PTR_ERR(priv->clk[i]); + } + + return 0; +} + +static int adsp_enable_all_clock(struct snd_sof_dev *sdev) +{ + struct device *dev = sdev->dev; + struct adsp_priv *priv = sdev->pdata->hw_pdata; + int ret; + + ret = clk_prepare_enable(priv->clk[CLK_TOP_MAINPLL_D7_D2]); + if (ret) { + dev_err(dev, "%s clk_prepare_enable(mainpll_d7_d2) fail %d\n", + __func__, ret); + return ret; + } + + ret = clk_prepare_enable(priv->clk[CLK_TOP_ADSP]); + if (ret) { + dev_err(dev, "%s clk_prepare_enable(adsp_sel) fail %d\n", + __func__, ret); + goto disable_mainpll_d7_d2_clk; + } + + ret = clk_prepare_enable(priv->clk[CLK_TOP_AUDIO_LOCAL_BUS]); + if (ret) { + dev_err(dev, "%s clk_prepare_enable(audio_local_bus) fail %d\n", + __func__, ret); + goto disable_dsp_sel_clk; + } + + ret = clk_prepare_enable(priv->clk[CLK_SCP_ADSP_AUDIODSP]); + if (ret) { + dev_err(dev, "%s clk_prepare_enable(scp_adsp_audiodsp) fail %d\n", + __func__, ret); + goto disable_audio_local_bus_clk; + } + + ret = clk_prepare_enable(priv->clk[CLK_TOP_AUDIO_H]); + if (ret) { + dev_err(dev, "%s clk_prepare_enable(audio_h) fail %d\n", + __func__, ret); + goto disable_scp_adsp_audiodsp_clk; + } + + return 0; + +disable_scp_adsp_audiodsp_clk: + clk_disable_unprepare(priv->clk[CLK_SCP_ADSP_AUDIODSP]); +disable_audio_local_bus_clk: + clk_disable_unprepare(priv->clk[CLK_TOP_AUDIO_LOCAL_BUS]); +disable_dsp_sel_clk: + clk_disable_unprepare(priv->clk[CLK_TOP_ADSP]); +disable_mainpll_d7_d2_clk: + clk_disable_unprepare(priv->clk[CLK_TOP_MAINPLL_D7_D2]); + + return ret; +} + +static void adsp_disable_all_clock(struct snd_sof_dev *sdev) +{ + struct adsp_priv *priv = sdev->pdata->hw_pdata; + + clk_disable_unprepare(priv->clk[CLK_TOP_AUDIO_H]); + clk_disable_unprepare(priv->clk[CLK_SCP_ADSP_AUDIODSP]); + clk_disable_unprepare(priv->clk[CLK_TOP_AUDIO_LOCAL_BUS]); + clk_disable_unprepare(priv->clk[CLK_TOP_ADSP]); + clk_disable_unprepare(priv->clk[CLK_TOP_MAINPLL_D7_D2]); +} + +static int adsp_default_clk_init(struct snd_sof_dev *sdev, bool enable) +{ + struct device *dev = sdev->dev; + struct adsp_priv *priv = sdev->pdata->hw_pdata; + int ret; + + dev_dbg(dev, "%s: %s\n", __func__, enable ? "on" : "off"); + + if (enable) { + ret = clk_set_parent(priv->clk[CLK_TOP_ADSP], + priv->clk[CLK_TOP_CLK26M]); + if (ret) { + dev_err(dev, "failed to set dsp_sel to clk26m: %d\n", ret); + return ret; + } + + ret = clk_set_parent(priv->clk[CLK_TOP_AUDIO_LOCAL_BUS], + priv->clk[CLK_TOP_MAINPLL_D7_D2]); + if (ret) { + dev_err(dev, "set audio_local_bus failed %d\n", ret); + return ret; + } + + ret = adsp_enable_all_clock(sdev); + if (ret) { + dev_err(dev, "failed to adsp_enable_clock: %d\n", ret); + return ret; + } + } else { + adsp_disable_all_clock(sdev); + } + + return 0; +} + +int adsp_clock_on(struct snd_sof_dev *sdev) +{ + /* Open ADSP clock */ + return adsp_default_clk_init(sdev, 1); +} + +int adsp_clock_off(struct snd_sof_dev *sdev) +{ + /* Close ADSP clock */ + return adsp_default_clk_init(sdev, 0); +} + diff --git a/sound/soc/sof/mediatek/mt8195/mt8195-clk.h b/sound/soc/sof/mediatek/mt8195/mt8195-clk.h new file mode 100644 index 000000000000..9cc0573d5cd2 --- /dev/null +++ b/sound/soc/sof/mediatek/mt8195/mt8195-clk.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Copyright (c) 2021 MediaTek Corporation. All rights reserved. + * + * Header file for the mt8195 DSP clock definition + */ + +#ifndef __MT8195_CLK_H +#define __MT8195_CLK_H + +struct snd_sof_dev; + +/*DSP clock*/ +enum adsp_clk_id { + CLK_TOP_ADSP, + CLK_TOP_CLK26M, + CLK_TOP_AUDIO_LOCAL_BUS, + CLK_TOP_MAINPLL_D7_D2, + CLK_SCP_ADSP_AUDIODSP, + CLK_TOP_AUDIO_H, + ADSP_CLK_MAX +}; + +int mt8195_adsp_init_clock(struct snd_sof_dev *sdev); +int adsp_clock_on(struct snd_sof_dev *sdev); +int adsp_clock_off(struct snd_sof_dev *sdev); +#endif diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.c b/sound/soc/sof/mediatek/mt8195/mt8195.c index 99075598a35a..5bfae9379ac8 100644 --- a/sound/soc/sof/mediatek/mt8195/mt8195.c +++ b/sound/soc/sof/mediatek/mt8195/mt8195.c @@ -25,6 +25,7 @@ #include "../adsp_helper.h" #include "../mediatek-ops.h" #include "mt8195.h" +#include "mt8195-clk.h" static int platform_parse_resource(struct platform_device *pdev, void *data) { @@ -231,10 +232,22 @@ static int mt8195_dsp_probe(struct snd_sof_dev *sdev) if (ret) return ret; + ret = mt8195_adsp_init_clock(sdev); + if (ret) { + dev_err(sdev->dev, "mt8195_adsp_init_clock failed\n"); + return -EINVAL; + } + + ret = adsp_clock_on(sdev); + if (ret) { + dev_err(sdev->dev, "adsp_clock_on fail!\n"); + return -EINVAL; + } + ret = adsp_sram_power_on(sdev->dev, true); if (ret) { dev_err(sdev->dev, "adsp_sram_power_on fail!\n"); - return ret; + goto exit_clk_disable; } ret = adsp_memory_remap_init(&pdev->dev, priv->adsp); @@ -282,6 +295,8 @@ static int mt8195_dsp_probe(struct snd_sof_dev *sdev) err_adsp_sram_power_off: adsp_sram_power_on(&pdev->dev, false); +exit_clk_disable: + adsp_clock_off(sdev); return ret; } @@ -290,7 +305,10 @@ static int mt8195_dsp_remove(struct snd_sof_dev *sdev) { struct platform_device *pdev = container_of(sdev->dev, struct platform_device, dev); - return adsp_sram_power_on(&pdev->dev, false); + adsp_sram_power_on(&pdev->dev, false); + adsp_clock_off(sdev); + + return 0; } /* on mt8195 there is 1 to 1 match between type and BAR idx */ -- cgit v1.2.3 From 163fa3a5927e1d8f948ea1fc16c897944933a06a Mon Sep 17 00:00:00 2001 From: YC Hung Date: Thu, 18 Nov 2021 12:07:49 +0200 Subject: ASoC: SOF: mediatek: Add DSP system PM callback for mt8195 Add DSP system PM callback for suspend and resume Signed-off-by: YC Hung Reviewed-by: Pierre-Louis Bossart Reviewed-by: Daniel Baluta Signed-off-by: Daniel Baluta Link: https://lore.kernel.org/r/20211118100749.54628-9-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/mediatek/mt8195/mt8195.c | 42 ++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.c b/sound/soc/sof/mediatek/mt8195/mt8195.c index 5bfae9379ac8..40e5a25875a6 100644 --- a/sound/soc/sof/mediatek/mt8195/mt8195.c +++ b/sound/soc/sof/mediatek/mt8195/mt8195.c @@ -311,6 +311,44 @@ static int mt8195_dsp_remove(struct snd_sof_dev *sdev) return 0; } +static int mt8195_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state) +{ + struct platform_device *pdev = container_of(sdev->dev, struct platform_device, dev); + int ret; + + /* stall and reset dsp */ + sof_hifixdsp_shutdown(sdev); + + /* power down adsp sram */ + ret = adsp_sram_power_on(&pdev->dev, false); + if (ret) { + dev_err(sdev->dev, "adsp_sram_power_off fail!\n"); + return ret; + } + + /* turn off adsp clock */ + return adsp_clock_off(sdev); +} + +static int mt8195_dsp_resume(struct snd_sof_dev *sdev) +{ + int ret; + + /* turn on adsp clock */ + ret = adsp_clock_on(sdev); + if (ret) { + dev_err(sdev->dev, "adsp_clock_on fail!\n"); + return ret; + } + + /* power on adsp sram */ + ret = adsp_sram_power_on(sdev->dev, true); + if (ret) + dev_err(sdev->dev, "adsp_sram_power_on fail!\n"); + + return ret; +} + /* on mt8195 there is 1 to 1 match between type and BAR idx */ static int mt8195_get_bar_index(struct snd_sof_dev *sdev, u32 type) { @@ -382,6 +420,10 @@ const struct snd_sof_dsp_ops sof_mt8195_ops = { .drv = mt8195_dai, .num_drv = ARRAY_SIZE(mt8195_dai), + /* PM */ + .suspend = mt8195_dsp_suspend, + .resume = mt8195_dsp_resume, + /* ALSA HW info flags */ .hw_info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | -- cgit v1.2.3 From b38892b5b85ae54b7b867313996f967122ede42e Mon Sep 17 00:00:00 2001 From: Srinivasa Rao Mandadapu Date: Thu, 18 Nov 2021 16:50:11 +0530 Subject: ASoC: codecs: MBHC: Remove useless condition check Remove redundant conditional check and clean code in special headset support functions. Signed-off-by: Srinivasa Rao Mandadapu Co-developed-by: Venkata Prasad Potturu Signed-off-by: Venkata Prasad Potturu Link: https://lore.kernel.org/r/1637234411-554-1-git-send-email-srivasam@codeaurora.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd-mbhc-v2.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c index 934194b155d5..7488a150a138 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.c +++ b/sound/soc/codecs/wcd-mbhc-v2.c @@ -1055,12 +1055,8 @@ static bool wcd_mbhc_check_for_spl_headset(struct wcd_mbhc *mbhc) hs_threshold = wcd_mbhc_get_spl_hs_thres(mbhc); hph_threshold = wcd_mbhc_adc_get_hph_thres(mbhc); - if (output_mv > hs_threshold || output_mv < hph_threshold) { - if (mbhc->force_linein == true) - is_spl_hs = false; - } else { + if (!(output_mv > hs_threshold || output_mv < hph_threshold)) is_spl_hs = true; - } /* Back MIC_BIAS2 to 1.8v if the type is not special headset */ if (!is_spl_hs) { @@ -1149,13 +1145,13 @@ correct_plug_type: plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv); is_pa_on = wcd_mbhc_read_field(mbhc, WCD_MBHC_HPH_PA_EN); - if ((output_mv > hs_threshold) && (!is_spl_hs)) { + if (output_mv > hs_threshold && !is_spl_hs) { is_spl_hs = wcd_mbhc_check_for_spl_headset(mbhc); output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P); if (is_spl_hs) { - hs_threshold = (hs_threshold * wcd_mbhc_get_micbias(mbhc)) / - micbias_mv; + hs_threshold *= wcd_mbhc_get_micbias(mbhc); + hs_threshold /= micbias_mv; } } @@ -1185,7 +1181,7 @@ correct_plug_type: } /* cable is extension cable */ - if (output_mv > hs_threshold || mbhc->force_linein == true) + if (output_mv > hs_threshold || mbhc->force_linein) plug_type = MBHC_PLUG_TYPE_HIGH_HPH; } -- cgit v1.2.3 From de2f29c4394efa64c3a5ba1b15302eb558ed4c56 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 19 Nov 2021 17:27:30 +0100 Subject: ALSA: hda: Remove redundant runtime PM calls The previous fix for more comprehensive runtime PM calls turned out to be not good as hoped; a few calls including pm_runtime_enable() and pm_runtime_disable() are rather utterly superfluous for PCI devices, even triggering a kernel error message. Better to drop those calls. Note that the problem we wanted to solve with that commit seems irrelevant with the fix itself; the original bug (a GPF at azx_remove()) was likely a regression by the recent PCI core cleanup, and the buggy PCI change has been already reverted. So basically we were scratching a wrong surface. OTOH, making the runtime PM calls symmetric for both probe and remove is more consistent, and maybe that's a sensible outcome. Fixes: 4f66a9ef37d3 ("ALSA: hda: intel: More comprehensive PM runtime setup for controller driver") Reported-by: Heiner Kallweit Link: https://lore.kernel.org/r/d9d76980-966a-e031-70d1-3254ba5be5eb@gmail.com Link: https://lore.kernel.org/r/20211119162730.24423-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 45e85180048c..221afacbc7fd 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1349,8 +1349,6 @@ static void azx_free(struct azx *chip) if (azx_has_pm_runtime(chip) && chip->running) { pm_runtime_get_noresume(&pci->dev); - pm_runtime_disable(&pci->dev); - pm_runtime_set_suspended(&pci->dev); pm_runtime_forbid(&pci->dev); pm_runtime_dont_use_autosuspend(&pci->dev); } @@ -2328,8 +2326,6 @@ static int azx_probe_continue(struct azx *chip) if (azx_has_pm_runtime(chip)) { pm_runtime_use_autosuspend(&pci->dev); pm_runtime_allow(&pci->dev); - pm_runtime_set_active(&pci->dev); - pm_runtime_enable(&pci->dev); pm_runtime_put_autosuspend(&pci->dev); } -- cgit v1.2.3 From 8253aa4700b37cef1ca3bbda0d986349357608d3 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Fri, 19 Nov 2021 11:43:15 +0200 Subject: ASoC: SOF: imx: Add code to manage DSP related clocks We need at least 3 clocks in order to power up and access DSP core registers found on i.MX8QM, i.MX8QXP and i.MX8MP platforms. Add code to request these clocks and enable them at probe. Next patches will add PM support which will only activate clocks when DSP is used. DSP clocks are already documented in Documentation/devicetree/bindings/dsp/fsl,dsp.yaml We choose to add: * imx8_parse_clocks * imx8_enable_clocks * imx8_disable_clocks wrappers because in the future DSP will need to take care about the clocks of other related Audio IPs (e.g SAI, ESAI). Signed-off-by: Daniel Baluta Reviewed-by: Paul Olaru Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20211119094319.81674-2-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/imx/imx-common.c | 24 ++++++++++++++++++++++++ sound/soc/sof/imx/imx-common.h | 11 +++++++++++ sound/soc/sof/imx/imx8.c | 23 +++++++++++++++++++++++ sound/soc/sof/imx/imx8m.c | 23 +++++++++++++++++++++++ 4 files changed, 81 insertions(+) diff --git a/sound/soc/sof/imx/imx-common.c b/sound/soc/sof/imx/imx-common.c index 8826ef94f04a..9371e9062cb1 100644 --- a/sound/soc/sof/imx/imx-common.c +++ b/sound/soc/sof/imx/imx-common.c @@ -74,4 +74,28 @@ void imx8_dump(struct snd_sof_dev *sdev, u32 flags) } EXPORT_SYMBOL(imx8_dump); +int imx8_parse_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks) +{ + int ret; + + ret = devm_clk_bulk_get(sdev->dev, clks->num_dsp_clks, clks->dsp_clks); + if (ret) + dev_err(sdev->dev, "Failed to request DSP clocks\n"); + + return ret; +} +EXPORT_SYMBOL(imx8_parse_clocks); + +int imx8_enable_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks) +{ + return clk_bulk_prepare_enable(clks->num_dsp_clks, clks->dsp_clks); +} +EXPORT_SYMBOL(imx8_enable_clocks); + +void imx8_disable_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks) +{ + clk_bulk_disable_unprepare(clks->num_dsp_clks, clks->dsp_clks); +} +EXPORT_SYMBOL(imx8_disable_clocks); + MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/imx/imx-common.h b/sound/soc/sof/imx/imx-common.h index 1cc7d6704182..ec4b3a5c7496 100644 --- a/sound/soc/sof/imx/imx-common.h +++ b/sound/soc/sof/imx/imx-common.h @@ -3,6 +3,8 @@ #ifndef __IMX_COMMON_H__ #define __IMX_COMMON_H__ +#include + #define EXCEPT_MAX_HDR_SIZE 0x400 #define IMX8_STACK_DUMP_SIZE 32 @@ -13,4 +15,13 @@ void imx8_get_registers(struct snd_sof_dev *sdev, void imx8_dump(struct snd_sof_dev *sdev, u32 flags); +struct imx_clocks { + struct clk_bulk_data *dsp_clks; + int num_dsp_clks; +}; + +int imx8_parse_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks); +int imx8_enable_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks); +void imx8_disable_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks); + #endif diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c index 0aeb44d0acc7..32f852cbba30 100644 --- a/sound/soc/sof/imx/imx8.c +++ b/sound/soc/sof/imx/imx8.c @@ -41,6 +41,13 @@ #define MBOX_OFFSET 0x800000 #define MBOX_SIZE 0x1000 +/* DSP clocks */ +static struct clk_bulk_data imx8_dsp_clks[] = { + { .id = "ipg" }, + { .id = "ocram" }, + { .id = "core" }, +}; + struct imx8_priv { struct device *dev; struct snd_sof_dev *sdev; @@ -57,6 +64,7 @@ struct imx8_priv { struct device **pd_dev; struct device_link **link; + struct imx_clocks *clks; }; static int imx8_get_mailbox_offset(struct snd_sof_dev *sdev) @@ -188,6 +196,10 @@ static int imx8_probe(struct snd_sof_dev *sdev) if (!priv) return -ENOMEM; + priv->clks = devm_kzalloc(&pdev->dev, sizeof(*priv->clks), GFP_KERNEL); + if (!priv->clks) + return -ENOMEM; + sdev->pdata->hw_pdata = priv; priv->dev = sdev->dev; priv->sdev = sdev; @@ -300,6 +312,16 @@ static int imx8_probe(struct snd_sof_dev *sdev) /* set default mailbox offset for FW ready message */ sdev->dsp_box.offset = MBOX_OFFSET; + /* init clocks info */ + priv->clks->dsp_clks = imx8_dsp_clks; + priv->clks->num_dsp_clks = ARRAY_SIZE(imx8_dsp_clks); + + ret = imx8_parse_clocks(sdev, priv->clks); + if (ret < 0) + goto exit_pdev_unregister; + + imx8_enable_clocks(sdev, priv->clks); + return 0; exit_pdev_unregister: @@ -318,6 +340,7 @@ static int imx8_remove(struct snd_sof_dev *sdev) struct imx8_priv *priv = sdev->pdata->hw_pdata; int i; + imx8_disable_clocks(sdev, priv->clks); platform_device_unregister(priv->ipc_dev); for (i = 0; i < priv->num_domains; i++) { diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c index f454a5d0a87e..ab40c0bdf796 100644 --- a/sound/soc/sof/imx/imx8m.c +++ b/sound/soc/sof/imx/imx8m.c @@ -23,6 +23,12 @@ #define MBOX_OFFSET 0x800000 #define MBOX_SIZE 0x1000 +static struct clk_bulk_data imx8m_dsp_clks[] = { + { .id = "ipg" }, + { .id = "ocram" }, + { .id = "core" }, +}; + struct imx8m_priv { struct device *dev; struct snd_sof_dev *sdev; @@ -30,6 +36,8 @@ struct imx8m_priv { /* DSP IPC handler */ struct imx_dsp_ipc *dsp_ipc; struct platform_device *ipc_dev; + + struct imx_clocks *clks; }; static int imx8m_get_mailbox_offset(struct snd_sof_dev *sdev) @@ -108,6 +116,10 @@ static int imx8m_probe(struct snd_sof_dev *sdev) if (!priv) return -ENOMEM; + priv->clks = devm_kzalloc(&pdev->dev, sizeof(*priv->clks), GFP_KERNEL); + if (!priv->clks) + return -ENOMEM; + sdev->pdata->hw_pdata = priv; priv->dev = sdev->dev; priv->sdev = sdev; @@ -175,6 +187,16 @@ static int imx8m_probe(struct snd_sof_dev *sdev) /* set default mailbox offset for FW ready message */ sdev->dsp_box.offset = MBOX_OFFSET; + /* init clocks info */ + priv->clks->dsp_clks = imx8m_dsp_clks; + priv->clks->num_dsp_clks = ARRAY_SIZE(imx8m_dsp_clks); + + ret = imx8_parse_clocks(sdev, priv->clks); + if (ret < 0) + goto exit_pdev_unregister; + + imx8_enable_clocks(sdev, priv->clks); + return 0; exit_pdev_unregister: @@ -186,6 +208,7 @@ static int imx8m_remove(struct snd_sof_dev *sdev) { struct imx8m_priv *priv = sdev->pdata->hw_pdata; + imx8_disable_clocks(sdev, priv->clks); platform_device_unregister(priv->ipc_dev); return 0; -- cgit v1.2.3 From 6fc8515806dfd5b7d3198c189b51e7624aadafdc Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Fri, 19 Nov 2021 11:43:16 +0200 Subject: ASoC: SOF: imx8: Add runtime PM / System PM support Handle clocks and mailbox channels at runtime suspend/resume in order to save power. DSP runtime PM uses a timeout of 2s. If device is idle for 2s system will enter runtime suspend. Because SOF state machine assumes that even if the DSP wasn't previously active at a System resume, will re-load the firmware we need to make sure that all needed resources are active. Kernel core will take care of enabling the PD, we need to make sure that we request the MU channels. Signed-off-by: Daniel Baluta Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20211119094319.81674-3-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/imx/imx8.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 115 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c index 32f852cbba30..c4755c88d492 100644 --- a/sound/soc/sof/imx/imx8.c +++ b/sound/soc/sof/imx/imx8.c @@ -320,7 +320,9 @@ static int imx8_probe(struct snd_sof_dev *sdev) if (ret < 0) goto exit_pdev_unregister; - imx8_enable_clocks(sdev, priv->clks); + ret = imx8_enable_clocks(sdev, priv->clks); + if (ret < 0) + goto exit_pdev_unregister; return 0; @@ -364,6 +366,92 @@ static int imx8_get_bar_index(struct snd_sof_dev *sdev, u32 type) } } +static void imx8_suspend(struct snd_sof_dev *sdev) +{ + int i; + struct imx8_priv *priv = (struct imx8_priv *)sdev->pdata->hw_pdata; + + for (i = 0; i < DSP_MU_CHAN_NUM; i++) + imx_dsp_free_channel(priv->dsp_ipc, i); + + imx8_disable_clocks(sdev, priv->clks); +} + +static int imx8_resume(struct snd_sof_dev *sdev) +{ + struct imx8_priv *priv = (struct imx8_priv *)sdev->pdata->hw_pdata; + int ret; + int i; + + ret = imx8_enable_clocks(sdev, priv->clks); + if (ret < 0) + return ret; + + for (i = 0; i < DSP_MU_CHAN_NUM; i++) + imx_dsp_request_channel(priv->dsp_ipc, i); + + return 0; +} + +static int imx8_dsp_runtime_resume(struct snd_sof_dev *sdev) +{ + int ret; + const struct sof_dsp_power_state target_dsp_state = { + .state = SOF_DSP_PM_D0, + }; + + ret = imx8_resume(sdev); + if (ret < 0) + return ret; + + return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); +} + +static int imx8_dsp_runtime_suspend(struct snd_sof_dev *sdev) +{ + const struct sof_dsp_power_state target_dsp_state = { + .state = SOF_DSP_PM_D3, + }; + + imx8_suspend(sdev); + + return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); +} + +static int imx8_dsp_suspend(struct snd_sof_dev *sdev, unsigned int target_state) +{ + const struct sof_dsp_power_state target_dsp_state = { + .state = target_state, + }; + + if (!pm_runtime_suspended(sdev->dev)) + imx8_suspend(sdev); + + return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); +} + +static int imx8_dsp_resume(struct snd_sof_dev *sdev) +{ + int ret; + const struct sof_dsp_power_state target_dsp_state = { + .state = SOF_DSP_PM_D0, + }; + + ret = imx8_resume(sdev); + if (ret < 0) + return ret; + + if (pm_runtime_suspended(sdev->dev)) { + pm_runtime_disable(sdev->dev); + pm_runtime_set_active(sdev->dev); + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_enable(sdev->dev); + pm_runtime_idle(sdev->dev); + } + + return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); +} + static struct snd_soc_dai_driver imx8_dai[] = { { .name = "esai0", @@ -389,6 +477,14 @@ static struct snd_soc_dai_driver imx8_dai[] = { }, }; +static int imx8_dsp_set_power_state(struct snd_sof_dev *sdev, + const struct sof_dsp_power_state *target_state) +{ + sdev->dsp_power_state = *target_state; + + return 0; +} + /* i.MX8 ops */ struct snd_sof_dsp_ops sof_imx8_ops = { /* probe and remove */ @@ -441,6 +537,15 @@ struct snd_sof_dsp_ops sof_imx8_ops = { SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, + + /* PM */ + .runtime_suspend = imx8_dsp_runtime_suspend, + .runtime_resume = imx8_dsp_runtime_resume, + + .suspend = imx8_dsp_suspend, + .resume = imx8_dsp_resume, + + .set_power_state = imx8_dsp_set_power_state, }; EXPORT_SYMBOL(sof_imx8_ops); @@ -490,6 +595,15 @@ struct snd_sof_dsp_ops sof_imx8x_ops = { .drv = imx8_dai, .num_drv = ARRAY_SIZE(imx8_dai), + /* PM */ + .runtime_suspend = imx8_dsp_runtime_suspend, + .runtime_resume = imx8_dsp_runtime_resume, + + .suspend = imx8_dsp_suspend, + .resume = imx8_dsp_resume, + + .set_power_state = imx8_dsp_set_power_state, + /* ALSA HW info flags */ .hw_info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | -- cgit v1.2.3 From a73b493d8e1b37acad686c15321d2eaab45567ce Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Fri, 19 Nov 2021 11:43:17 +0200 Subject: ASoC: SOF: imx8m: Add runtime PM / System PM support We make use of common imx8m_suspend / imx8m_resume functions for both system PM and runtime PM. imx8m_suspend: - frees the MU channels - disables the clocks imx8m_resume - enables the clocks - requests the MU channels On i.MX8MP there is no dedicated functionality to put the DSP in reset. The only way of doing this is to POWER DOWN the Audiomix domain. We are able to do this because turning off the clocks and freeing the channels makes the Audiomix to have no users thus PM kernel core turns it down. SOF core will not call system PM suspend handler if the DSP is already down, but at resume it will call the system PM resume. So, we need to keep track of the state via snd_sof_dsp_set_power_state Few insights on how SOF core handles the PM: - SOF core uses PM runtime autosuspend (with a timeout of 2 secs) - at probe, SOF core boots the DSP and lets the PM runtime suspend to turn it off, if there is no activity - when someone opens the ALSA sound card (aplay/arecord, etc) ALSA core calls PM runtime resume to turn on the DSP - when the ALSA sound card is closed SOF core make use of PM subsystem to call PM runtime suspend and thus turning off the DSP. Signed-off-by: Daniel Baluta Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20211119094319.81674-4-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/imx/imx8m.c | 106 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 105 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c index ab40c0bdf796..b050d4cf9cd5 100644 --- a/sound/soc/sof/imx/imx8m.c +++ b/sound/soc/sof/imx/imx8m.c @@ -195,7 +195,9 @@ static int imx8m_probe(struct snd_sof_dev *sdev) if (ret < 0) goto exit_pdev_unregister; - imx8_enable_clocks(sdev, priv->clks); + ret = imx8_enable_clocks(sdev, priv->clks); + if (ret < 0) + goto exit_pdev_unregister; return 0; @@ -252,6 +254,100 @@ static struct snd_soc_dai_driver imx8m_dai[] = { }, }; +static int imx8m_dsp_set_power_state(struct snd_sof_dev *sdev, + const struct sof_dsp_power_state *target_state) +{ + sdev->dsp_power_state = *target_state; + + return 0; +} + +static int imx8m_resume(struct snd_sof_dev *sdev) +{ + struct imx8m_priv *priv = (struct imx8m_priv *)sdev->pdata->hw_pdata; + int ret; + int i; + + ret = imx8_enable_clocks(sdev, priv->clks); + if (ret < 0) + return ret; + + for (i = 0; i < DSP_MU_CHAN_NUM; i++) + imx_dsp_request_channel(priv->dsp_ipc, i); + + return 0; +} + +static void imx8m_suspend(struct snd_sof_dev *sdev) +{ + struct imx8m_priv *priv = (struct imx8m_priv *)sdev->pdata->hw_pdata; + int i; + + for (i = 0; i < DSP_MU_CHAN_NUM; i++) + imx_dsp_free_channel(priv->dsp_ipc, i); + + imx8_disable_clocks(sdev, priv->clks); +} + +static int imx8m_dsp_runtime_resume(struct snd_sof_dev *sdev) +{ + int ret; + const struct sof_dsp_power_state target_dsp_state = { + .state = SOF_DSP_PM_D0, + }; + + ret = imx8m_resume(sdev); + if (ret < 0) + return ret; + + return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); +} + +static int imx8m_dsp_runtime_suspend(struct snd_sof_dev *sdev) +{ + const struct sof_dsp_power_state target_dsp_state = { + .state = SOF_DSP_PM_D3, + }; + + imx8m_suspend(sdev); + + return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); +} + +static int imx8m_dsp_resume(struct snd_sof_dev *sdev) +{ + int ret; + const struct sof_dsp_power_state target_dsp_state = { + .state = SOF_DSP_PM_D0, + }; + + ret = imx8m_resume(sdev); + if (ret < 0) + return ret; + + if (pm_runtime_suspended(sdev->dev)) { + pm_runtime_disable(sdev->dev); + pm_runtime_set_active(sdev->dev); + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_enable(sdev->dev); + pm_runtime_idle(sdev->dev); + } + + return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); +} + +static int imx8m_dsp_suspend(struct snd_sof_dev *sdev, unsigned int target_state) +{ + const struct sof_dsp_power_state target_dsp_state = { + .state = target_state, + }; + + if (!pm_runtime_suspended(sdev->dev)) + imx8m_suspend(sdev); + + return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); +} + /* i.MX8 ops */ struct snd_sof_dsp_ops sof_imx8m_ops = { /* probe and remove */ @@ -297,6 +393,14 @@ struct snd_sof_dsp_ops sof_imx8m_ops = { .drv = imx8m_dai, .num_drv = ARRAY_SIZE(imx8m_dai), + .suspend = imx8m_dsp_suspend, + .resume = imx8m_dsp_resume, + + .runtime_suspend = imx8m_dsp_runtime_suspend, + .runtime_resume = imx8m_dsp_runtime_resume, + + .set_power_state = imx8m_dsp_set_power_state, + .hw_info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | -- cgit v1.2.3 From 9ba23717b2927071ddb49f3d6719244e3fe8f4c9 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Fri, 19 Nov 2021 11:43:18 +0200 Subject: ASoC: SOF: imx8m: Implement DSP start On i.MX8M DSP is controlled via a set of registers from Audio MIX. This patches gets a reference (via regmap) to Audio Mix registers and implements DSP start. Signed-off-by: Daniel Baluta Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20211119094319.81674-5-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/imx/imx8m.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c index b050d4cf9cd5..9972ca8e6ec6 100644 --- a/sound/soc/sof/imx/imx8m.c +++ b/sound/soc/sof/imx/imx8m.c @@ -6,10 +6,13 @@ // // Hardware interface for audio DSP on i.MX8M +#include #include +#include #include #include #include +#include #include #include @@ -29,6 +32,14 @@ static struct clk_bulk_data imx8m_dsp_clks[] = { { .id = "core" }, }; +/* DSP audio mix registers */ +#define AudioDSP_REG0 0x100 +#define AudioDSP_REG1 0x104 +#define AudioDSP_REG2 0x108 +#define AudioDSP_REG3 0x10c + +#define AudioDSP_REG2_RUNSTALL BIT(5) + struct imx8m_priv { struct device *dev; struct snd_sof_dev *sdev; @@ -38,6 +49,8 @@ struct imx8m_priv { struct platform_device *ipc_dev; struct imx_clocks *clks; + + struct regmap *regmap; }; static int imx8m_get_mailbox_offset(struct snd_sof_dev *sdev) @@ -96,7 +109,10 @@ static int imx8m_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) */ static int imx8m_run(struct snd_sof_dev *sdev) { - /* TODO: start DSP using Audio MIX bits */ + struct imx8m_priv *priv = (struct imx8m_priv *)sdev->pdata->hw_pdata; + + regmap_update_bits(priv->regmap, AudioDSP_REG2, AudioDSP_REG2_RUNSTALL, 0); + return 0; } @@ -187,6 +203,13 @@ static int imx8m_probe(struct snd_sof_dev *sdev) /* set default mailbox offset for FW ready message */ sdev->dsp_box.offset = MBOX_OFFSET; + priv->regmap = syscon_regmap_lookup_by_compatible("fsl,dsp-ctrl"); + if (IS_ERR(priv->regmap)) { + dev_err(sdev->dev, "cannot find dsp-ctrl registers"); + ret = PTR_ERR(priv->regmap); + goto exit_pdev_unregister; + } + /* init clocks info */ priv->clks->dsp_clks = imx8m_dsp_clks; priv->clks->num_dsp_clks = ARRAY_SIZE(imx8m_dsp_clks); -- cgit v1.2.3 From 3bf4cd8b747a222f0f454f3220199c99f1c03da6 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Fri, 19 Nov 2021 11:43:19 +0200 Subject: ASoC: SOF: imx8m: Implement reset callback Resume common flow (System PM / Runtime PM) is like this: sof_resume -> specific device resume -> snd_sof_load_firmware -> snd_sof_dsp_reset (1) -> load_modules() -> snd_sof_run_firmware (2) We need to implement dsp_reset callback (1) that will actually reset the DSP but keep it stalled. In order to implement this we do the following: -> put DSP into reset (assert CoreReset bit from PWRCTL) -> stall the DSP using RunStall bit from AudioDSP_REG2 mix -> take DSP out of reset (de-assert CoreReset bit from PWRCTL) At this moment the DSP is taken out of reset and Stalled! This means that we can load the firmware and then start the DSP (2). Until now we resetted the DSP by turning down the Audiomix PD. This doesn't work for Runtime PM if another IP is keeping Audiomix PD up. By introducing dsp_reset() we no longer rely on turning off the audiomix to reset the DSP. Signed-off-by: Daniel Baluta Reviewed-by: Pierre-Louis Bossart Reviewed-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211119094319.81674-6-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/imx/imx8m.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c index 9972ca8e6ec6..8f24c6db7f5b 100644 --- a/sound/soc/sof/imx/imx8m.c +++ b/sound/soc/sof/imx/imx8m.c @@ -32,6 +32,12 @@ static struct clk_bulk_data imx8m_dsp_clks[] = { { .id = "core" }, }; +/* DAP registers */ +#define IMX8M_DAP_DEBUG 0x28800000 +#define IMX8M_DAP_DEBUG_SIZE (64 * 1024) +#define IMX8M_DAP_PWRCTL (0x4000 + 0x3020) +#define IMX8M_PWRCTL_CORERESET BIT(16) + /* DSP audio mix registers */ #define AudioDSP_REG0 0x100 #define AudioDSP_REG1 0x104 @@ -50,6 +56,7 @@ struct imx8m_priv { struct imx_clocks *clks; + void __iomem *dap; struct regmap *regmap; }; @@ -116,6 +123,30 @@ static int imx8m_run(struct snd_sof_dev *sdev) return 0; } +static int imx8m_reset(struct snd_sof_dev *sdev) +{ + struct imx8m_priv *priv = (struct imx8m_priv *)sdev->pdata->hw_pdata; + u32 pwrctl; + + /* put DSP into reset and stall */ + pwrctl = readl(priv->dap + IMX8M_DAP_PWRCTL); + pwrctl |= IMX8M_PWRCTL_CORERESET; + writel(pwrctl, priv->dap + IMX8M_DAP_PWRCTL); + + /* keep reset asserted for 10 cycles */ + usleep_range(1, 2); + + regmap_update_bits(priv->regmap, AudioDSP_REG2, + AudioDSP_REG2_RUNSTALL, AudioDSP_REG2_RUNSTALL); + + /* take the DSP out of reset and keep stalled for FW loading */ + pwrctl = readl(priv->dap + IMX8M_DAP_PWRCTL); + pwrctl &= ~IMX8M_PWRCTL_CORERESET; + writel(pwrctl, priv->dap + IMX8M_DAP_PWRCTL); + + return 0; +} + static int imx8m_probe(struct snd_sof_dev *sdev) { struct platform_device *pdev = @@ -168,6 +199,13 @@ static int imx8m_probe(struct snd_sof_dev *sdev) goto exit_pdev_unregister; } + priv->dap = devm_ioremap(sdev->dev, IMX8M_DAP_DEBUG, IMX8M_DAP_DEBUG_SIZE); + if (!priv->dap) { + dev_err(sdev->dev, "error: failed to map DAP debug memory area"); + ret = -ENODEV; + goto exit_pdev_unregister; + } + sdev->bar[SOF_FW_BLK_TYPE_IRAM] = devm_ioremap(sdev->dev, base, size); if (!sdev->bar[SOF_FW_BLK_TYPE_IRAM]) { dev_err(sdev->dev, "failed to ioremap base 0x%x size 0x%x\n", @@ -378,6 +416,7 @@ struct snd_sof_dsp_ops sof_imx8m_ops = { .remove = imx8m_remove, /* DSP core boot */ .run = imx8m_run, + .reset = imx8m_reset, /* Block IO */ .block_read = sof_block_read, -- cgit v1.2.3 From 81ed6770ba67358b07e96a277206f6c742737dab Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Fri, 19 Nov 2021 21:26:12 +0200 Subject: ASoC: SOF: Intel: hda: expose get_chip_info() expose get_chip_info(). Signed-off-by: Ranjani Sridharan Reviewed-by: Guennadi Liakhovetski Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211119192621.4096077-2-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda.c | 11 ----------- sound/soc/sof/intel/shim.h | 7 +++++++ 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 568d351b7a4e..1ebf8db488b8 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -127,17 +127,6 @@ int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w) return sof_widget_free(sdev, swidget); } -static const struct sof_intel_dsp_desc - *get_chip_info(struct snd_sof_pdata *pdata) -{ - const struct sof_dev_desc *desc = pdata->desc; - const struct sof_intel_dsp_desc *chip_info; - - chip_info = desc->chip_info; - - return chip_info; -} - #if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) /* diff --git a/sound/soc/sof/intel/shim.h b/sound/soc/sof/intel/shim.h index e9f7d4d7fcce..08c53cb41ea7 100644 --- a/sound/soc/sof/intel/shim.h +++ b/sound/soc/sof/intel/shim.h @@ -177,4 +177,11 @@ struct sof_intel_stream { size_t posn_offset; }; +static inline const struct sof_intel_dsp_desc *get_chip_info(struct snd_sof_pdata *pdata) +{ + const struct sof_dev_desc *desc = pdata->desc; + + return desc->chip_info; +} + #endif -- cgit v1.2.3 From 5974f6843203f0061d9df05c32262a10359740a6 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Fri, 19 Nov 2021 21:26:13 +0200 Subject: ASoC: SOF: Introduce num_cores and ref count per core Add two fields num_cores and dsp_cores_ref_count to struct snd_sof_dev. These will be used to maintain the ref count for each core to determine when it should be powered up or down. Signed-off-by: Ranjani Sridharan Reviewed-by: Guennadi Liakhovetski Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211119192621.4096077-3-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/imx/imx8.c | 1 + sound/soc/sof/imx/imx8m.c | 1 + sound/soc/sof/intel/bdw.c | 9 +++++++++ sound/soc/sof/intel/byt.c | 9 +++++++++ sound/soc/sof/intel/hda.c | 2 ++ sound/soc/sof/intel/pci-tng.c | 9 +++++++++ sound/soc/sof/sof-priv.h | 15 +++++++++++++++ 7 files changed, 46 insertions(+) diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c index 0aeb44d0acc7..2d0448b3c8c3 100644 --- a/sound/soc/sof/imx/imx8.c +++ b/sound/soc/sof/imx/imx8.c @@ -188,6 +188,7 @@ static int imx8_probe(struct snd_sof_dev *sdev) if (!priv) return -ENOMEM; + sdev->num_cores = 1; sdev->pdata->hw_pdata = priv; priv->dev = sdev->dev; priv->sdev = sdev; diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c index f454a5d0a87e..c94422323d67 100644 --- a/sound/soc/sof/imx/imx8m.c +++ b/sound/soc/sof/imx/imx8m.c @@ -108,6 +108,7 @@ static int imx8m_probe(struct snd_sof_dev *sdev) if (!priv) return -ENOMEM; + sdev->num_cores = 1; sdev->pdata->hw_pdata = priv; priv->dev = sdev->dev; priv->sdev = sdev; diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c index 156006bed017..1a8a39a878fd 100644 --- a/sound/soc/sof/intel/bdw.c +++ b/sound/soc/sof/intel/bdw.c @@ -412,10 +412,19 @@ static int bdw_probe(struct snd_sof_dev *sdev) const struct sof_dev_desc *desc = pdata->desc; struct platform_device *pdev = container_of(sdev->dev, struct platform_device, dev); + const struct sof_intel_dsp_desc *chip; struct resource *mmio; u32 base, size; int ret; + chip = get_chip_info(sdev->pdata); + if (!chip) { + dev_err(sdev->dev, "error: no such device supported\n"); + return -EIO; + } + + sdev->num_cores = chip->cores_num; + /* LPE base */ mmio = platform_get_resource(pdev, IORESOURCE_MEM, desc->resindex_lpe_base); diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c index e2fa08f1ae74..dcfeaedb8fd5 100644 --- a/sound/soc/sof/intel/byt.c +++ b/sound/soc/sof/intel/byt.c @@ -113,10 +113,19 @@ static int byt_acpi_probe(struct snd_sof_dev *sdev) const struct sof_dev_desc *desc = pdata->desc; struct platform_device *pdev = container_of(sdev->dev, struct platform_device, dev); + const struct sof_intel_dsp_desc *chip; struct resource *mmio; u32 base, size; int ret; + chip = get_chip_info(sdev->pdata); + if (!chip) { + dev_err(sdev->dev, "error: no such device supported\n"); + return -EIO; + } + + sdev->num_cores = chip->cores_num; + /* DSP DMA can only access low 31 bits of host memory */ ret = dma_coerce_mask_and_coherent(sdev->dev, DMA_BIT_MASK(31)); if (ret < 0) { diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 1ebf8db488b8..3c69e8fcd43b 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -897,6 +897,8 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) goto err; } + sdev->num_cores = chip->cores_num; + hdev = devm_kzalloc(sdev->dev, sizeof(*hdev), GFP_KERNEL); if (!hdev) return -ENOMEM; diff --git a/sound/soc/sof/intel/pci-tng.c b/sound/soc/sof/intel/pci-tng.c index 18eb41b8a8f4..f8c841caa362 100644 --- a/sound/soc/sof/intel/pci-tng.c +++ b/sound/soc/sof/intel/pci-tng.c @@ -55,9 +55,18 @@ static int tangier_pci_probe(struct snd_sof_dev *sdev) struct snd_sof_pdata *pdata = sdev->pdata; const struct sof_dev_desc *desc = pdata->desc; struct pci_dev *pci = to_pci_dev(sdev->dev); + const struct sof_intel_dsp_desc *chip; u32 base, size; int ret; + chip = get_chip_info(sdev->pdata); + if (!chip) { + dev_err(sdev->dev, "error: no such device supported\n"); + return -EIO; + } + + sdev->num_cores = chip->cores_num; + /* DSP DMA can only access low 31 bits of host memory */ ret = dma_coerce_mask_and_coherent(&pci->dev, DMA_BIT_MASK(31)); if (ret < 0) { diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 9a8af76b2f8b..a56f3c8b483f 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -71,6 +71,9 @@ extern int sof_core_debug; /* So far the primary core on all DSPs has ID 0 */ #define SOF_DSP_PRIMARY_CORE 0 +/* max number of DSP cores */ +#define SOF_MAX_DSP_NUM_CORES 8 + /* DSP power state */ enum sof_dsp_power_states { SOF_DSP_PM_D0, @@ -477,6 +480,18 @@ struct snd_sof_dev { bool msi_enabled; + /* DSP core context */ + u32 num_cores; + + /* + * ref count per core that will be modified during system suspend/resume and during pcm + * hw_params/hw_free. This doesn't need to be protected with a mutex because pcm + * hw_params/hw_free are already protected by the PCM mutex in the ALSA framework in + * sound/core/ when streams are active and during system suspend/resume, streams are + * already suspended. + */ + int dsp_core_ref_count[SOF_MAX_DSP_NUM_CORES]; + void *private; /* core does not touch this */ }; -- cgit v1.2.3 From c414d5df9d05471aa47f50fca7fa4412daca7ac7 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Fri, 19 Nov 2021 21:26:14 +0200 Subject: ASoC: SOF: Add ops for core_get and core_put Add ops to get/put a core that will be used to power up/down a core along with incrementing/decrementing its ref_count. Signed-off-by: Ranjani Sridharan Reviewed-by: Guennadi Liakhovetski Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211119192621.4096077-4-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ops.h | 63 ++++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/sof/sof-priv.h | 2 ++ 2 files changed, 65 insertions(+) diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 09bf38fdfb8a..61dc2768b000 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -103,6 +103,69 @@ static inline int snd_sof_dsp_core_power_down(struct snd_sof_dev *sdev, return ret; } +static inline int snd_sof_dsp_core_get(struct snd_sof_dev *sdev, int core) +{ + if (core > sdev->num_cores - 1) { + dev_err(sdev->dev, "invalid core id: %d for num_cores: %d\n", core, + sdev->num_cores); + return -EINVAL; + } + + if (sof_ops(sdev)->core_get) { + int ret; + + /* if current ref_count is > 0, increment it and return */ + if (sdev->dsp_core_ref_count[core] > 0) { + sdev->dsp_core_ref_count[core]++; + return 0; + } + + /* power up the core */ + ret = sof_ops(sdev)->core_get(sdev, core); + if (ret < 0) + return ret; + + /* increment ref_count */ + sdev->dsp_core_ref_count[core]++; + + /* and update enabled_cores_mask */ + sdev->enabled_cores_mask |= BIT(core); + + dev_dbg(sdev->dev, "Core %d powered up\n", core); + } + + return 0; +} + +static inline int snd_sof_dsp_core_put(struct snd_sof_dev *sdev, int core) +{ + if (core > sdev->num_cores - 1) { + dev_err(sdev->dev, "invalid core id: %d for num_cores: %d\n", core, + sdev->num_cores); + return -EINVAL; + } + + if (sof_ops(sdev)->core_put) { + int ret; + + /* decrement ref_count and return if it is > 0 */ + if (--(sdev->dsp_core_ref_count[core]) > 0) + return 0; + + /* power down the core */ + ret = sof_ops(sdev)->core_put(sdev, core); + if (ret < 0) + return ret; + + /* and update enabled_cores_mask */ + sdev->enabled_cores_mask &= ~BIT(core); + + dev_dbg(sdev->dev, "Core %d powered down\n", core); + } + + return 0; +} + /* pre/post fw load */ static inline int snd_sof_dsp_pre_fw_run(struct snd_sof_dev *sdev) { diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index a56f3c8b483f..f7c86a72ac10 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -134,6 +134,8 @@ struct snd_sof_dsp_ops { unsigned int core_mask); /* optional */ int (*core_power_down)(struct snd_sof_dev *sof_dev, unsigned int core_mask); /* optional */ + int (*core_get)(struct snd_sof_dev *sof_dev, int core); /* optional */ + int (*core_put)(struct snd_sof_dev *sof_dev, int core); /* optional */ /* * Register IO: only used by respective drivers themselves, -- cgit v1.2.3 From 41dd63cccb42ec26f555cbb2495d85828a4b0e96 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Fri, 19 Nov 2021 21:26:15 +0200 Subject: ASoC: SOF: Intel: TGL: set core_get/put ops Set core_get/put() ops for TGL. When core_get() is requested for a core, its ref_count is incremented and the PM_CORE_ENABLE IPC sent to the firmware to power up the core if the current ref_count is 1. Conversely, the ref_count is decremented in core_put() and an IPC is sent to the DSP to power off the core if the ref_count is 0. Signed-off-by: Ranjani Sridharan Reviewed-by: Guennadi Liakhovetski Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211119192621.4096077-5-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/tgl.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c index 48da8e7a67bc..51011b0b8c11 100644 --- a/sound/soc/sof/intel/tgl.c +++ b/sound/soc/sof/intel/tgl.c @@ -20,6 +20,46 @@ static const struct snd_sof_debugfs_map tgl_dsp_debugfs[] = { {"dsp", HDA_DSP_BAR, 0, 0x10000, SOF_DEBUGFS_ACCESS_ALWAYS}, }; +static int tgl_dsp_core_get(struct snd_sof_dev *sdev, int core) +{ + struct sof_ipc_pm_core_config pm_core_config = { + .hdr = { + .cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CORE_ENABLE, + .size = sizeof(pm_core_config), + }, + .enable_mask = sdev->enabled_cores_mask | BIT(core), + }; + + /* power up primary core if not already powered up and return */ + if (core == SOF_DSP_PRIMARY_CORE) + return hda_dsp_enable_core(sdev, BIT(core)); + + /* notify DSP for secondary cores */ + return sof_ipc_tx_message(sdev->ipc, pm_core_config.hdr.cmd, + &pm_core_config, sizeof(pm_core_config), + &pm_core_config, sizeof(pm_core_config)); +} + +static int tgl_dsp_core_put(struct snd_sof_dev *sdev, int core) +{ + struct sof_ipc_pm_core_config pm_core_config = { + .hdr = { + .cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CORE_ENABLE, + .size = sizeof(pm_core_config), + }, + .enable_mask = sdev->enabled_cores_mask & ~BIT(core), + }; + + /* power down primary core and return */ + if (core == SOF_DSP_PRIMARY_CORE) + return hda_dsp_core_reset_power_down(sdev, BIT(core)); + + /* notify DSP for secondary cores */ + return sof_ipc_tx_message(sdev->ipc, pm_core_config.hdr.cmd, + &pm_core_config, sizeof(pm_core_config), + &pm_core_config, sizeof(pm_core_config)); +} + /* Tigerlake ops */ const struct snd_sof_dsp_ops sof_tgl_ops = { /* probe/remove/shutdown */ @@ -96,6 +136,8 @@ const struct snd_sof_dsp_ops sof_tgl_ops = { /* dsp core power up/down */ .core_power_up = hda_dsp_enable_core, .core_power_down = hda_dsp_core_reset_power_down, + .core_get = tgl_dsp_core_get, + .core_put = tgl_dsp_core_put, /* firmware run */ .run = hda_dsp_cl_boot_firmware_iccmax, -- cgit v1.2.3 From 9cdcbc9f6788661fb02fb2340032a5c8115aaf9b Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Fri, 19 Nov 2021 21:26:16 +0200 Subject: ASoC: SOF: Intel: CNL/ICL/APL: set core_get/core_put ops Set core_get/put ops for CNL/ICL platforms. These platforms do not support enabling/disabling secondary cores dynamically. So skip sending the IPC to power off the cores in the core_put op. Signed-off-by: Ranjani Sridharan Reviewed-by: Guennadi Liakhovetski Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211119192621.4096077-6-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/apl.c | 1 + sound/soc/sof/intel/cnl.c | 1 + sound/soc/sof/intel/hda-dsp.c | 44 +++++++++++++++++++++++++++++++++++++++++++ sound/soc/sof/intel/hda.h | 1 + sound/soc/sof/intel/icl.c | 1 + 5 files changed, 48 insertions(+) diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index 917f78cf6daf..569668b2186f 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -104,6 +104,7 @@ const struct snd_sof_dsp_ops sof_apl_ops = { /* dsp core power up/down */ .core_power_up = hda_dsp_enable_core, .core_power_down = hda_dsp_core_reset_power_down, + .core_get = hda_dsp_core_get, /* trace callback */ .trace_init = hda_dsp_trace_init, diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 3957e2b3db32..be6b6500b907 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -306,6 +306,7 @@ const struct snd_sof_dsp_ops sof_cnl_ops = { /* dsp core power up/down */ .core_power_up = hda_dsp_enable_core, .core_power_down = hda_dsp_core_reset_power_down, + .core_get = hda_dsp_core_get, /* firmware run */ .run = hda_dsp_cl_boot_firmware, diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index 287dc0eb6686..b2f6dcd1c23d 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -962,3 +962,47 @@ void hda_dsp_d0i3_work(struct work_struct *work) "error: failed to set DSP state %d substate %d\n", target_state.state, target_state.substate); } + +int hda_dsp_core_get(struct snd_sof_dev *sdev, int core) +{ + struct sof_ipc_pm_core_config pm_core_config = { + .hdr = { + .cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CORE_ENABLE, + .size = sizeof(pm_core_config), + }, + .enable_mask = sdev->enabled_cores_mask | BIT(core), + }; + int ret, ret1; + + /* power up core */ + ret = hda_dsp_enable_core(sdev, BIT(core)); + if (ret < 0) { + dev_err(sdev->dev, "failed to power up core %d with err: %d\n", + core, ret); + return ret; + } + + /* No need to send IPC for primary core or if FW boot is not complete */ + if (sdev->fw_state != SOF_FW_BOOT_COMPLETE || core == SOF_DSP_PRIMARY_CORE) + return 0; + + /* Now notify DSP for secondary cores */ + ret = sof_ipc_tx_message(sdev->ipc, pm_core_config.hdr.cmd, + &pm_core_config, sizeof(pm_core_config), + &pm_core_config, sizeof(pm_core_config)); + if (ret < 0) { + dev_err(sdev->dev, "failed to enable secondary core '%d' failed with %d\n", + core, ret); + goto power_down; + } + + return ret; + +power_down: + /* power down core if it is host managed and return the original error if this fails too */ + ret1 = hda_dsp_core_reset_power_down(sdev, BIT(core)); + if (ret1 < 0) + dev_err(sdev->dev, "failed to power down core: %d with err: %d\n", core, ret1); + + return ret; +} diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 1195018a1f4f..646f5d4dc882 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -496,6 +496,7 @@ int hda_dsp_core_run(struct snd_sof_dev *sdev, unsigned int core_mask); int hda_dsp_enable_core(struct snd_sof_dev *sdev, unsigned int core_mask); int hda_dsp_core_reset_power_down(struct snd_sof_dev *sdev, unsigned int core_mask); +int hda_dsp_core_get(struct snd_sof_dev *sdev, int core); void hda_dsp_ipc_int_enable(struct snd_sof_dev *sdev); void hda_dsp_ipc_int_disable(struct snd_sof_dev *sdev); diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c index 0b2cc331d55b..e3472868f49a 100644 --- a/sound/soc/sof/intel/icl.c +++ b/sound/soc/sof/intel/icl.c @@ -100,6 +100,7 @@ const struct snd_sof_dsp_ops sof_icl_ops = { /* dsp core power up/down */ .core_power_up = hda_dsp_enable_core, .core_power_down = hda_dsp_core_reset_power_down, + .core_get = hda_dsp_core_get, /* firmware run */ .run = hda_dsp_cl_boot_firmware_iccmax, -- cgit v1.2.3 From 7cc7b9ba21d4978d19f0e3edc2b00d44c9d66ff6 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Fri, 19 Nov 2021 21:26:17 +0200 Subject: ASoC: SOF: topology: remove sof_load_pipeline_ipc() Remove the function sof_load_pipeline_ipc() and directly send the IPC instead. The pipeline core is already enabled with the call to sof_pipeline_core_enable() in sof_widget_setup(). Signed-off-by: Ranjani Sridharan Reviewed-by: Guennadi Liakhovetski Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211119192621.4096077-7-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-audio.c | 3 ++- sound/soc/sof/sof-audio.h | 4 ---- sound/soc/sof/topology.c | 17 ----------------- 3 files changed, 2 insertions(+), 22 deletions(-) diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 7cbe757c1fe2..a019355e0bcf 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -203,7 +203,8 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) break; case snd_soc_dapm_scheduler: pipeline = swidget->private; - ret = sof_load_pipeline_ipc(sdev, pipeline, &r); + ret = sof_ipc_tx_message(sdev->ipc, pipeline->hdr.cmd, pipeline, + sizeof(*pipeline), &r, sizeof(r)); break; default: hdr = swidget->private; diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 05e98e231b85..6c591b7a531c 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -184,10 +184,6 @@ void snd_sof_control_notify(struct snd_sof_dev *sdev, int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file); int snd_sof_complete_pipeline(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); - -int sof_load_pipeline_ipc(struct snd_sof_dev *sdev, - struct sof_ipc_pipe_new *pipeline, - struct sof_ipc_comp_reply *r); int sof_pipeline_core_enable(struct snd_sof_dev *sdev, const struct snd_sof_widget *swidget); diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 10caf2b1a33c..3a49d7910326 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1707,23 +1707,6 @@ err: /* * Pipeline Topology */ -int sof_load_pipeline_ipc(struct snd_sof_dev *sdev, - struct sof_ipc_pipe_new *pipeline, - struct sof_ipc_comp_reply *r) -{ - int ret = sof_core_enable(sdev, pipeline->core); - - if (ret < 0) - return ret; - - ret = sof_ipc_tx_message(sdev->ipc, pipeline->hdr.cmd, pipeline, - sizeof(*pipeline), r, sizeof(*r)); - if (ret < 0) - dev_err(sdev->dev, "error: load pipeline ipc failure\n"); - - return ret; -} - static int sof_widget_load_pipeline(struct snd_soc_component *scomp, int index, struct snd_sof_widget *swidget, struct snd_soc_tplg_dapm_widget *tw) -- cgit v1.2.3 From b2ebcf42a48f4560862bb811f3268767d17ebdcd Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Fri, 19 Nov 2021 21:26:18 +0200 Subject: ASoC: SOF: free widgets in sof_tear_down_pipelines() for static pipelines Free widgets for static pipelines in sof_tear_down_pipelines(). But this feature is unavailable in older firmware with ABI < 3.19. Just reset widget use_count's for this case. This would ensure that the secondary cores enabled required for topology setup are powered down properly before the primary core is powered off during system suspend. Signed-off-by: Ranjani Sridharan Reviewed-by: Guennadi Liakhovetski Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211119192621.4096077-8-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-audio.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index a019355e0bcf..669d5c924f6b 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -665,11 +665,12 @@ int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify) } /* - * This function doesn't free widgets during suspend. It only resets the set up status for all - * routes and use_count for all widgets. + * For older firmware, this function doesn't free widgets for static pipelines during suspend. + * It only resets use_count for all widgets. */ int sof_tear_down_pipelines(struct snd_sof_dev *sdev, bool verify) { + struct sof_ipc_fw_version *v = &sdev->fw_ready.version; struct snd_sof_widget *swidget; struct snd_sof_route *sroute; int ret; @@ -681,8 +682,14 @@ int sof_tear_down_pipelines(struct snd_sof_dev *sdev, bool verify) * loading the sound card unavailable to open PCMs. */ list_for_each_entry_reverse(swidget, &sdev->widget_list, list) { - if (!verify) { + if (swidget->dynamic_pipeline_widget) + continue; + + /* Do not free widgets for static pipelines with FW ABI older than 3.19 */ + if (!verify && !swidget->dynamic_pipeline_widget && + v->abi_version < SOF_ABI_VER(3, 19, 0)) { swidget->use_count = 0; + swidget->complete = 0; continue; } -- cgit v1.2.3 From d416519982cb1d25358f558a4e68d9d254c9ca53 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Fri, 19 Nov 2021 21:26:19 +0200 Subject: ASoC: SOF: hda: don't use the core op for power up/power down The core_power_up/down() ops will be deprecated. Use the HDA platform-specific functions for powering up/down the cores during probe/suspend/remove. The enabled_cores_mask and the core ref_count's are manually updated in each of these functions. Signed-off-by: Ranjani Sridharan Reviewed-by: Guennadi Liakhovetski Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211119192621.4096077-9-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-dsp.c | 8 ++++++-- sound/soc/sof/intel/hda-loader.c | 24 +++++++++++++++++------- sound/soc/sof/intel/hda.c | 4 ++-- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index b2f6dcd1c23d..916a257ea96b 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -614,7 +614,7 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend) #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) struct hdac_bus *bus = sof_to_bus(sdev); #endif - int ret; + int ret, j; hda_sdw_int_enable(sdev, false); @@ -629,13 +629,17 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend) #endif /* power down DSP */ - ret = snd_sof_dsp_core_power_down(sdev, chip->host_managed_cores_mask); + ret = hda_dsp_core_reset_power_down(sdev, chip->host_managed_cores_mask); if (ret < 0) { dev_err(sdev->dev, "error: failed to power down core during suspend\n"); return ret; } + /* reset ref counts for all cores */ + for (j = 0; j < chip->cores_num; j++) + sdev->dsp_core_ref_count[j] = 0; + /* disable ppcap interrupt */ hda_dsp_ctrl_ppcap_enable(sdev, false); hda_dsp_ctrl_ppcap_int_enable(sdev, false); diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index abad6d0ceb83..40201e5ac201 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -88,12 +88,13 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag) struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; const struct sof_intel_dsp_desc *chip = hda->desc; unsigned int status; - u32 flags; + unsigned long mask; + u32 flags, j; int ret; int i; /* step 1: power up corex */ - ret = snd_sof_dsp_core_power_up(sdev, chip->host_managed_cores_mask); + ret = hda_dsp_enable_core(sdev, chip->host_managed_cores_mask); if (ret < 0) { if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS) dev_err(sdev->dev, "error: dsp core 0/1 power up failed\n"); @@ -148,8 +149,8 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag) chip->ipc_ack_mask); /* step 5: power down cores that are no longer needed */ - ret = snd_sof_dsp_core_power_down(sdev, chip->host_managed_cores_mask & - ~(chip->init_core_mask)); + ret = hda_dsp_core_reset_power_down(sdev, chip->host_managed_cores_mask & + ~(chip->init_core_mask)); if (ret < 0) { if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS) dev_err(sdev->dev, @@ -168,8 +169,14 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag) HDA_DSP_REG_POLL_INTERVAL_US, chip->rom_init_timeout * USEC_PER_MSEC); - if (!ret) + if (!ret) { + /* set enabled cores mask and increment ref count for cores in init_core_mask */ + sdev->enabled_cores_mask |= chip->init_core_mask; + mask = sdev->enabled_cores_mask; + for_each_set_bit(j, &mask, SOF_MAX_DSP_NUM_CORES) + sdev->dsp_core_ref_count[j]++; return 0; + } if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS) dev_err(sdev->dev, @@ -184,7 +191,7 @@ err: flags &= ~SOF_DBG_DUMP_OPTIONAL; snd_sof_dsp_dbg_dump(sdev, flags); - snd_sof_dsp_core_power_down(sdev, chip->host_managed_cores_mask); + hda_dsp_core_reset_power_down(sdev, chip->host_managed_cores_mask); return ret; } @@ -501,12 +508,15 @@ int hda_dsp_post_fw_run_icl(struct snd_sof_dev *sdev) * the host whereas on TGL it is handled by the firmware. */ if (!hda->clk_config_lpro) { - ret = snd_sof_dsp_core_power_up(sdev, BIT(3)); + ret = hda_dsp_enable_core(sdev, BIT(3)); if (ret < 0) { dev_err(sdev->dev, "error: dsp core power up failed on core 3\n"); return ret; } + sdev->enabled_cores_mask |= BIT(3); + sdev->dsp_core_ref_count[3]++; + snd_sof_dsp_stall(sdev, BIT(3)); } diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 3c69e8fcd43b..1e1e9659ea86 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -1034,9 +1034,9 @@ err: int hda_dsp_remove(struct snd_sof_dev *sdev) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + const struct sof_intel_dsp_desc *chip = hda->desc; struct hdac_bus *bus = sof_to_bus(sdev); struct pci_dev *pci = to_pci_dev(sdev->dev); - const struct sof_intel_dsp_desc *chip = hda->desc; /* cancel any attempt for DSP D0I3 */ cancel_delayed_work_sync(&hda->d0i3_work); @@ -1061,7 +1061,7 @@ int hda_dsp_remove(struct snd_sof_dev *sdev) /* disable cores */ if (chip) - snd_sof_dsp_core_power_down(sdev, chip->host_managed_cores_mask); + hda_dsp_core_reset_power_down(sdev, chip->host_managed_cores_mask); /* disable DSP */ snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, -- cgit v1.2.3 From 9ea807488cdaef83da702d4a02d54138b88f4377 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Fri, 19 Nov 2021 21:26:20 +0200 Subject: ASoC: SOF: add support for dynamic pipelines with multi-core This patch adds support for dynamic pipelines with multi-core by using the platform-specific core_get/put() ops to power up/down a core when a widget is set up/freed. Along with this, a few redundant functions are removed: 1. sof_pipeline_core_enable() is no longer needed as the pipeline core will be set up when the pipeline widget is set up 2. sof_core_enable() is replaced with snd_sof_core_get() 4. core_power_up/down() DSP ops are deprecated and replaced with core get/put ops. 5. Core power down in sof_widget_unload() during topology removal is also removed as it is not really needed. For dynamic pipelines, the cores will be powered off when they are not used. For static pipelines, the cores will be powered off in the device remove callback. Signed-off-by: Ranjani Sridharan Reviewed-by: Guennadi Liakhovetski Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211119192621.4096077-10-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/apl.c | 4 +-- sound/soc/sof/intel/cnl.c | 4 +-- sound/soc/sof/intel/icl.c | 4 +-- sound/soc/sof/intel/tgl.c | 4 +-- sound/soc/sof/ops.h | 32 +----------------- sound/soc/sof/sof-audio.c | 67 ++++++++++++++++++++++++++++++-------- sound/soc/sof/sof-audio.h | 2 -- sound/soc/sof/sof-priv.h | 4 --- sound/soc/sof/topology.c | 83 ----------------------------------------------- 9 files changed, 59 insertions(+), 145 deletions(-) diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index 569668b2186f..1baf0fddeb3d 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -101,9 +101,7 @@ const struct snd_sof_dsp_ops sof_apl_ops = { /* parse platform specific extended manifest */ .parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data, - /* dsp core power up/down */ - .core_power_up = hda_dsp_enable_core, - .core_power_down = hda_dsp_core_reset_power_down, + /* dsp core get/put */ .core_get = hda_dsp_core_get, /* trace callback */ diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index be6b6500b907..d455272bfc8e 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -303,9 +303,7 @@ const struct snd_sof_dsp_ops sof_cnl_ops = { /* parse platform specific extended manifest */ .parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data, - /* dsp core power up/down */ - .core_power_up = hda_dsp_enable_core, - .core_power_down = hda_dsp_core_reset_power_down, + /* dsp core get/put */ .core_get = hda_dsp_core_get, /* firmware run */ diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c index e3472868f49a..6c5422157ec8 100644 --- a/sound/soc/sof/intel/icl.c +++ b/sound/soc/sof/intel/icl.c @@ -97,9 +97,7 @@ const struct snd_sof_dsp_ops sof_icl_ops = { /* parse platform specific extended manifest */ .parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data, - /* dsp core power up/down */ - .core_power_up = hda_dsp_enable_core, - .core_power_down = hda_dsp_core_reset_power_down, + /* dsp core get/put */ .core_get = hda_dsp_core_get, /* firmware run */ diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c index 51011b0b8c11..237e92e790b7 100644 --- a/sound/soc/sof/intel/tgl.c +++ b/sound/soc/sof/intel/tgl.c @@ -133,9 +133,7 @@ const struct snd_sof_dsp_ops sof_tgl_ops = { /* parse platform specific extended manifest */ .parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data, - /* dsp core power up/down */ - .core_power_up = hda_dsp_enable_core, - .core_power_down = hda_dsp_core_reset_power_down, + /* dsp core get/put */ .core_get = tgl_dsp_core_get, .core_put = tgl_dsp_core_put, diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 61dc2768b000..a0648a13e3eb 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -72,37 +72,7 @@ static inline int snd_sof_dsp_reset(struct snd_sof_dev *sdev) return 0; } -/* dsp core power up/power down */ -static inline int snd_sof_dsp_core_power_up(struct snd_sof_dev *sdev, - unsigned int core_mask) -{ - int ret = 0; - - core_mask &= ~sdev->enabled_cores_mask; - if (sof_ops(sdev)->core_power_up && core_mask) { - ret = sof_ops(sdev)->core_power_up(sdev, core_mask); - if (!ret) - sdev->enabled_cores_mask |= core_mask; - } - - return ret; -} - -static inline int snd_sof_dsp_core_power_down(struct snd_sof_dev *sdev, - unsigned int core_mask) -{ - int ret = 0; - - core_mask &= sdev->enabled_cores_mask; - if (sof_ops(sdev)->core_power_down && core_mask) { - ret = sof_ops(sdev)->core_power_down(sdev, core_mask); - if (!ret) - sdev->enabled_cores_mask &= ~core_mask; - } - - return ret; -} - +/* dsp core get/put */ static inline int snd_sof_dsp_core_get(struct snd_sof_dev *sdev, int core) { if (core > sdev->num_cores - 1) { diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 669d5c924f6b..0f2566f7c094 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -106,7 +106,7 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) .id = swidget->comp_id, }; struct sof_ipc_reply reply; - int ret; + int ret, ret1, core; if (!swidget->private) return 0; @@ -115,10 +115,17 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) if (--swidget->use_count) return 0; + core = swidget->core; + switch (swidget->id) { case snd_soc_dapm_scheduler: + { + const struct sof_ipc_pipe_new *pipeline = swidget->private; + + core = pipeline->core; ipc_free.hdr.cmd |= SOF_IPC_TPLG_PIPE_FREE; break; + } case snd_soc_dapm_buffer: ipc_free.hdr.cmd |= SOF_IPC_TPLG_BUFFER_FREE; break; @@ -127,20 +134,32 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) break; } + /* continue to disable core even if IPC fails */ ret = sof_ipc_tx_message(sdev->ipc, ipc_free.hdr.cmd, &ipc_free, sizeof(ipc_free), &reply, sizeof(reply)); - if (ret < 0) { + if (ret < 0) dev_err(sdev->dev, "error: failed to free widget %s\n", swidget->widget->name); - swidget->use_count++; - return ret; + + /* + * disable widget core. continue to route setup status and complete flag + * even if this fails and return the appropriate error + */ + ret1 = snd_sof_dsp_core_put(sdev, core); + if (ret1 < 0) { + dev_err(sdev->dev, "error: failed to disable target core: %d for widget %s\n", + core, swidget->widget->name); + if (!ret) + ret = ret1; } /* reset route setup status for all routes that contain this widget */ sof_reset_route_setup_status(sdev, swidget); swidget->complete = 0; - dev_dbg(sdev->dev, "widget %s freed\n", swidget->widget->name); - return 0; + if (!ret) + dev_dbg(sdev->dev, "widget %s freed\n", swidget->widget->name); + + return ret; } EXPORT_SYMBOL(sof_widget_free); @@ -153,6 +172,7 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) struct snd_sof_dai *dai; size_t ipc_size; int ret; + int core; /* skip if there is no private data */ if (!swidget->private) @@ -162,10 +182,18 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) if (++swidget->use_count > 1) return 0; - ret = sof_pipeline_core_enable(sdev, swidget); + /* set core ID */ + core = swidget->core; + if (swidget->id == snd_soc_dapm_scheduler) { + pipeline = swidget->private; + core = pipeline->core; + } + + /* enable widget core */ + ret = snd_sof_dsp_core_get(sdev, core); if (ret < 0) { - dev_err(sdev->dev, "error: failed to enable target core: %d for widget %s\n", - ret, swidget->widget->name); + dev_err(sdev->dev, "error: failed to enable target core for widget %s\n", + swidget->widget->name); goto use_count_dec; } @@ -174,8 +202,10 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) case snd_soc_dapm_dai_out: ipc_size = sizeof(struct sof_ipc_comp_dai) + sizeof(struct sof_ipc_comp_ext); comp = kzalloc(ipc_size, GFP_KERNEL); - if (!comp) - return -ENOMEM; + if (!comp) { + ret = -ENOMEM; + goto core_put; + } dai = swidget->private; dai->configured = false; @@ -190,13 +220,18 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) if (ret < 0) { dev_err(sdev->dev, "error: failed to load widget %s\n", swidget->widget->name); - goto use_count_dec; + goto core_put; } ret = sof_dai_config_setup(sdev, dai); if (ret < 0) { dev_err(sdev->dev, "error: failed to load dai config for DAI %s\n", swidget->widget->name); + + /* + * widget use_count and core ref_count will both be decremented by + * sof_widget_free() + */ sof_widget_free(sdev, swidget); return ret; } @@ -214,7 +249,7 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) } if (ret < 0) { dev_err(sdev->dev, "error: failed to load widget %s\n", swidget->widget->name); - goto use_count_dec; + goto core_put; } /* restore kcontrols for widget */ @@ -222,6 +257,10 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) if (ret < 0) { dev_err(sdev->dev, "error: failed to restore kcontrols for widget %s\n", swidget->widget->name); + /* + * widget use_count and core ref_count will both be decremented by + * sof_widget_free() + */ sof_widget_free(sdev, swidget); return ret; } @@ -230,6 +269,8 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) return 0; +core_put: + snd_sof_dsp_core_put(sdev, core); use_count_dec: swidget->use_count--; return ret; diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 6c591b7a531c..389d56ac3aba 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -184,8 +184,6 @@ void snd_sof_control_notify(struct snd_sof_dev *sdev, int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file); int snd_sof_complete_pipeline(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); -int sof_pipeline_core_enable(struct snd_sof_dev *sdev, - const struct snd_sof_widget *swidget); /* * Stream IPC diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index f7c86a72ac10..a9c5197617f1 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -130,10 +130,6 @@ struct snd_sof_dsp_ops { int (*run)(struct snd_sof_dev *sof_dev); /* mandatory */ int (*stall)(struct snd_sof_dev *sof_dev, unsigned int core_mask); /* optional */ int (*reset)(struct snd_sof_dev *sof_dev); /* optional */ - int (*core_power_up)(struct snd_sof_dev *sof_dev, - unsigned int core_mask); /* optional */ - int (*core_power_down)(struct snd_sof_dev *sof_dev, - unsigned int core_mask); /* optional */ int (*core_get)(struct snd_sof_dev *sof_dev, int core); /* optional */ int (*core_put)(struct snd_sof_dev *sof_dev, int core); /* optional */ diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 3a49d7910326..63948bb30710 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1346,69 +1346,6 @@ static int sof_control_unload(struct snd_soc_component *scomp, * DAI Topology */ -/* Static DSP core power management so far, should be extended in the future */ -static int sof_core_enable(struct snd_sof_dev *sdev, int core) -{ - struct sof_ipc_pm_core_config pm_core_config = { - .hdr = { - .cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CORE_ENABLE, - .size = sizeof(pm_core_config), - }, - .enable_mask = sdev->enabled_cores_mask | BIT(core), - }; - int ret; - - if (sdev->enabled_cores_mask & BIT(core)) - return 0; - - /* power up the core if it is host managed */ - ret = snd_sof_dsp_core_power_up(sdev, BIT(core)); - if (ret < 0) { - dev_err(sdev->dev, "error: %d powering up core %d\n", - ret, core); - return ret; - } - - /* Now notify DSP */ - ret = sof_ipc_tx_message(sdev->ipc, pm_core_config.hdr.cmd, - &pm_core_config, sizeof(pm_core_config), - &pm_core_config, sizeof(pm_core_config)); - if (ret < 0) { - dev_err(sdev->dev, "error: core %d enable ipc failure %d\n", - core, ret); - goto err; - } - return ret; -err: - /* power down core if it is host managed and return the original error if this fails too */ - if (snd_sof_dsp_core_power_down(sdev, BIT(core)) < 0) - dev_err(sdev->dev, "error: powering down core %d\n", core); - - return ret; -} - -int sof_pipeline_core_enable(struct snd_sof_dev *sdev, - const struct snd_sof_widget *swidget) -{ - const struct sof_ipc_pipe_new *pipeline; - int ret; - - if (swidget->id == snd_soc_dapm_scheduler) { - pipeline = swidget->private; - } else { - pipeline = snd_sof_pipeline_find(sdev, swidget->pipeline_id); - if (!pipeline) - return -ENOENT; - } - - /* First enable the pipeline core */ - ret = sof_core_enable(sdev, pipeline->core); - if (ret < 0) - return ret; - - return sof_core_enable(sdev, swidget->core); -} - static int sof_connect_dai_widget(struct snd_soc_component *scomp, struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tw, @@ -2485,10 +2422,8 @@ static int sof_route_unload(struct snd_soc_component *scomp, static int sof_widget_unload(struct snd_soc_component *scomp, struct snd_soc_dobj *dobj) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); const struct snd_kcontrol_new *kc; struct snd_soc_dapm_widget *widget; - struct sof_ipc_pipe_new *pipeline; struct snd_sof_control *scontrol; struct snd_sof_widget *swidget; struct soc_mixer_control *sm; @@ -2515,24 +2450,6 @@ static int sof_widget_unload(struct snd_soc_component *scomp, list_del(&dai->list); } break; - case snd_soc_dapm_scheduler: - - /* power down the pipeline schedule core */ - pipeline = swidget->private; - - /* - * Runtime PM should still function normally if topology loading fails and - * it's components are unloaded. Do not power down the primary core so that the - * CTX_SAVE IPC can succeed during runtime suspend. - */ - if (pipeline->core == SOF_DSP_PRIMARY_CORE) - break; - - ret = snd_sof_dsp_core_power_down(sdev, 1 << pipeline->core); - if (ret < 0) - dev_err(scomp->dev, "error: powering down pipeline schedule core %d\n", - pipeline->core); - break; default: break; } -- cgit v1.2.3 From 05827a1537f35221d84b8f5606f2f4c1371c69f3 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Fri, 19 Nov 2021 21:26:21 +0200 Subject: ASoC: SOF: Intel: hda: free DAI widget during stop and suspend To keep the widget use_counts balanced, free the DAI widget during suspend and also during the stop trigger. Signed-off-by: Ranjani Sridharan Reviewed-by: Guennadi Liakhovetski Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211119192621.4096077-11-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-dai.c | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 76579383d290..5c9ee6c49473 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -182,24 +182,6 @@ static struct sof_ipc_dai_config *hda_dai_update_config(struct snd_soc_dapm_widg return config; } -static int hda_link_config_ipc(struct sof_intel_hda_stream *hda_stream, - struct snd_soc_dapm_widget *w, int channel) -{ - struct snd_sof_dev *sdev = hda_stream->sdev; - struct sof_ipc_dai_config *config; - struct sof_ipc_reply reply; - - config = hda_dai_update_config(w, channel); - if (!config) { - dev_err(sdev->dev, "error: no config for DAI %s\n", w->name); - return -ENOENT; - } - - /* send DAI_CONFIG IPC */ - return sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size, - &reply, sizeof(reply)); -} - static int hda_link_dai_widget_update(struct sof_intel_hda_stream *hda_stream, struct snd_soc_dapm_widget *w, int channel, bool widget_setup) @@ -353,10 +335,9 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, w = dai->capture_widget; /* - * clear link DMA channel. It will be assigned when - * hw_params is set up again after resume. + * free DAI widget during stop/suspend to keep widget use_count's balanced. */ - ret = hda_link_config_ipc(hda_stream, w, DMA_CHAN_INVALID); + ret = hda_link_dai_widget_update(hda_stream, w, DMA_CHAN_INVALID, false); if (ret < 0) return ret; -- cgit v1.2.3 From 32a956a1fadfd7d3924ab8ada2b7754054375903 Mon Sep 17 00:00:00 2001 From: Olivier Moysan Date: Fri, 19 Nov 2021 11:47:50 +0100 Subject: ASoC: stm32: i2s: add pm_runtime support Enable support of pm_runtime on STM32 I2S driver to allow I2S power state monitoring. Signed-off-by: Olivier Moysan Link: https://lore.kernel.org/r/20211119104752.13564-2-olivier.moysan@foss.st.com Signed-off-by: Mark Brown --- sound/soc/stm/stm32_i2s.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c index 6254bacad6eb..68c5de040df8 100644 --- a/sound/soc/stm/stm32_i2s.c +++ b/sound/soc/stm/stm32_i2s.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -1113,6 +1114,7 @@ static int stm32_i2s_remove(struct platform_device *pdev) { snd_dmaengine_pcm_unregister(&pdev->dev); snd_soc_unregister_component(&pdev->dev); + pm_runtime_disable(&pdev->dev); return 0; } @@ -1150,6 +1152,8 @@ static int stm32_i2s_probe(struct platform_device *pdev) return PTR_ERR(i2s->regmap); } + pm_runtime_enable(&pdev->dev); + ret = snd_dmaengine_pcm_register(&pdev->dev, &stm32_i2s_pcm_config, 0); if (ret) { if (ret != -EPROBE_DEFER) -- cgit v1.2.3 From 98e500a12f934531b0d44eac6bc53c3d4b66aa74 Mon Sep 17 00:00:00 2001 From: Olivier Moysan Date: Fri, 19 Nov 2021 11:47:51 +0100 Subject: ASoC: stm32: dfsdm: add pm_runtime support for audio Enable support of pm_runtime on STM32 DFSDM audio driver to allow power state monitoring. Signed-off-by: Olivier Moysan Link: https://lore.kernel.org/r/20211119104752.13564-3-olivier.moysan@foss.st.com Signed-off-by: Mark Brown --- sound/soc/stm/stm32_adfsdm.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c index e6078f50e508..6ee714542b84 100644 --- a/sound/soc/stm/stm32_adfsdm.c +++ b/sound/soc/stm/stm32_adfsdm.c @@ -12,7 +12,7 @@ #include #include #include - +#include #include #include #include @@ -334,6 +334,8 @@ static int stm32_adfsdm_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, priv); + pm_runtime_enable(&pdev->dev); + ret = devm_snd_soc_register_component(&pdev->dev, &stm32_adfsdm_dai_component, &priv->dai_drv, 1); @@ -373,6 +375,7 @@ static int stm32_adfsdm_probe(struct platform_device *pdev) static int stm32_adfsdm_remove(struct platform_device *pdev) { snd_soc_unregister_component(&pdev->dev); + pm_runtime_disable(&pdev->dev); return 0; } -- cgit v1.2.3 From ac5e3efd55868d8c12a178123b24616a22db274d Mon Sep 17 00:00:00 2001 From: Olivier Moysan Date: Fri, 19 Nov 2021 11:47:52 +0100 Subject: ASoC: stm32: spdifrx: add pm_runtime support Enable support of pm_runtime on STM32 SPDIFRX driver to allow SPDIFRX power state monitoring. Signed-off-by: Olivier Moysan Link: https://lore.kernel.org/r/20211119104752.13564-4-olivier.moysan@foss.st.com Signed-off-by: Mark Brown --- sound/soc/stm/stm32_spdifrx.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c index 48145f553588..a9ccdc2c5867 100644 --- a/sound/soc/stm/stm32_spdifrx.c +++ b/sound/soc/stm/stm32_spdifrx.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -955,6 +956,7 @@ static int stm32_spdifrx_remove(struct platform_device *pdev) snd_dmaengine_pcm_unregister(&pdev->dev); snd_soc_unregister_component(&pdev->dev); + pm_runtime_disable(&pdev->dev); return 0; } @@ -1010,6 +1012,8 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) udelay(2); reset_control_deassert(rst); + pm_runtime_enable(&pdev->dev); + pcm_config = &stm32_spdifrx_pcm_config; ret = snd_dmaengine_pcm_register(&pdev->dev, pcm_config, 0); if (ret) { -- cgit v1.2.3 From 405e52f412b85b581899f5e1b82d25a7c8959d89 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 19 Nov 2021 17:13:27 -0600 Subject: ASoC: SOF: sof-pci-dev: use community key on all Up boards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are already 3 versions of the Up boards with support for the SOF community key (ApolloLake, WhiskyLake, TigerLake). Rather than continue to add quirks for each version, let's add a wildcard. For WHL and TGL, the authentication supports both the SOF community key and the firmware signed with the Intel production key. Given two choices, the community key is the preferred option to allow developers to sign their own firmware. The firmware signed with production key can still be selected if needed with a kernel module option (snd-sof-pci.fw_path="intel/sof") Tested-by: Péter Ujfalusi Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Link: https://lore.kernel.org/r/20211119231327.211946-1-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-pci-dev.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index bc9e70765678..b4bc4f887b43 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -64,17 +64,9 @@ static const struct dmi_system_id sof_tplg_table[] = { static const struct dmi_system_id community_key_platforms[] = { { - .ident = "Up Squared", + .ident = "Up boards", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "AAEON"), - DMI_MATCH(DMI_BOARD_NAME, "UP-APL01"), - } - }, - { - .ident = "Up Extreme", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "AAEON"), - DMI_MATCH(DMI_BOARD_NAME, "UP-WHL01"), } }, { -- cgit v1.2.3 From fdd535283779ec9f9c35fda352585c629121214f Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Fri, 19 Nov 2021 12:48:54 +0000 Subject: ASoC: cs42l42: Report initial jack state When a jack handler is registered in cs42l42_set_jack() the initial state should be reported if an attached headphone/headset has already been detected. The jack detect sequence takes around 1 second: typically long enough for the machine driver to probe and register the jack handler in time to receive the first report from the interrupt handler. So it is possible on some systems that the correct initial state was seen simply because of lucky timing. Modular builds were more likely to miss the reporting of the initial state. Signed-off-by: Richard Fitzgerald Fixes: 4ca239f33737 ("ASoC: cs42l42: Always enable TS_PLUG and TS_UNPLUG interrupts") Link: https://lore.kernel.org/r/20211119124854.58939-1-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l42.c | 22 ++++++++++++++++++++++ sound/soc/codecs/cs42l42.h | 2 ++ 2 files changed, 24 insertions(+) diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 0c4303547fd8..43d98bdb5b5b 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -549,8 +549,25 @@ static int cs42l42_set_jack(struct snd_soc_component *component, struct snd_soc_ { struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); + /* Prevent race with interrupt handler */ + mutex_lock(&cs42l42->jack_detect_mutex); cs42l42->jack = jk; + if (jk) { + switch (cs42l42->hs_type) { + case CS42L42_PLUG_CTIA: + case CS42L42_PLUG_OMTP: + snd_soc_jack_report(jk, SND_JACK_HEADSET, SND_JACK_HEADSET); + break; + case CS42L42_PLUG_HEADPHONE: + snd_soc_jack_report(jk, SND_JACK_HEADPHONE, SND_JACK_HEADPHONE); + break; + default: + break; + } + } + mutex_unlock(&cs42l42->jack_detect_mutex); + return 0; } @@ -1618,6 +1635,8 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data) CS42L42_M_DETECT_FT_MASK | CS42L42_M_HSBIAS_HIZ_MASK); + mutex_lock(&cs42l42->jack_detect_mutex); + /* Check auto-detect status */ if ((~masks[5]) & irq_params_table[5].mask) { if (stickies[5] & CS42L42_HSDET_AUTO_DONE_MASK) { @@ -1686,6 +1705,8 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data) } } + mutex_unlock(&cs42l42->jack_detect_mutex); + return IRQ_HANDLED; } @@ -2033,6 +2054,7 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client, cs42l42->dev = &i2c_client->dev; i2c_set_clientdata(i2c_client, cs42l42); + mutex_init(&cs42l42->jack_detect_mutex); cs42l42->regmap = devm_regmap_init_i2c(i2c_client, &cs42l42_regmap); if (IS_ERR(cs42l42->regmap)) { diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h index 75ade987d0db..9fff183dce8e 100644 --- a/sound/soc/codecs/cs42l42.h +++ b/sound/soc/codecs/cs42l42.h @@ -12,6 +12,7 @@ #ifndef __CS42L42_H__ #define __CS42L42_H__ +#include #include #define CS42L42_PAGE_REGISTER 0x00 /* Page Select Register */ @@ -841,6 +842,7 @@ struct cs42l42_private { struct gpio_desc *reset_gpio; struct completion pdn_done; struct snd_soc_jack *jack; + struct mutex jack_detect_mutex; int pll_config; int bclk; u32 sclk; -- cgit v1.2.3 From 7016fd940adf2f4d86032339b546c6ecd737062f Mon Sep 17 00:00:00 2001 From: Ariel D'Alessandro Date: Fri, 19 Nov 2021 12:32:44 -0300 Subject: ASoC: tlv320aic31xx: Fix typo in BCLK clock name Signed-off-by: Ariel D'Alessandro Link: https://lore.kernel.org/r/20211119153248.419802-2-ariel.dalessandro@collabora.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic31xx.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/tlv320aic31xx.h b/sound/soc/codecs/tlv320aic31xx.h index 2513922a0292..80d062578fb5 100644 --- a/sound/soc/codecs/tlv320aic31xx.h +++ b/sound/soc/codecs/tlv320aic31xx.h @@ -118,7 +118,7 @@ struct aic31xx_pdata { #define AIC31XX_PLL_CLKIN_MASK GENMASK(3, 2) #define AIC31XX_PLL_CLKIN_SHIFT (2) #define AIC31XX_PLL_CLKIN_MCLK 0x00 -#define AIC31XX_PLL_CLKIN_BCKL 0x01 +#define AIC31XX_PLL_CLKIN_BCLK 0x01 #define AIC31XX_PLL_CLKIN_GPIO1 0x02 #define AIC31XX_PLL_CLKIN_DIN 0x03 #define AIC31XX_CODEC_CLKIN_MASK GENMASK(1, 0) -- cgit v1.2.3 From 2664b24a8c51c21b24c2b37b7f10d6485c35b7c1 Mon Sep 17 00:00:00 2001 From: Ariel D'Alessandro Date: Fri, 19 Nov 2021 12:32:45 -0300 Subject: ASoC: tlv320aic31xx: Add support for pll_r coefficient When the clock used by the codec is BCLK, the operation parameters need to be calculated from input sample rate and format. Low frequency rates required different r multipliers, in order to achieve a higher PLL output frequency. Signed-off-by: Michael Trimarchi Signed-off-by: Ariel D'Alessandro Link: https://lore.kernel.org/r/20211119153248.419802-3-ariel.dalessandro@collabora.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic31xx.c | 71 ++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index 52d2c968b5c0..1aec03d834d0 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -180,6 +180,7 @@ struct aic31xx_priv { struct aic31xx_rate_divs { u32 mclk_p; u32 rate; + u8 pll_r; u8 pll_j; u16 pll_d; u16 dosr; @@ -192,51 +193,51 @@ struct aic31xx_rate_divs { /* ADC dividers can be disabled by configuring them to 0 */ static const struct aic31xx_rate_divs aic31xx_divs[] = { - /* mclk/p rate pll: j d dosr ndac mdac aors nadc madc */ + /* mclk/p rate pll: r j d dosr ndac mdac aors nadc madc */ /* 8k rate */ - {12000000, 8000, 8, 1920, 128, 48, 2, 128, 48, 2}, - {12000000, 8000, 8, 1920, 128, 32, 3, 128, 32, 3}, - {12500000, 8000, 7, 8643, 128, 48, 2, 128, 48, 2}, + {12000000, 8000, 1, 8, 1920, 128, 48, 2, 128, 48, 2}, + {12000000, 8000, 1, 8, 1920, 128, 32, 3, 128, 32, 3}, + {12500000, 8000, 1, 7, 8643, 128, 48, 2, 128, 48, 2}, /* 11.025k rate */ - {12000000, 11025, 7, 5264, 128, 32, 2, 128, 32, 2}, - {12000000, 11025, 8, 4672, 128, 24, 3, 128, 24, 3}, - {12500000, 11025, 7, 2253, 128, 32, 2, 128, 32, 2}, + {12000000, 11025, 1, 7, 5264, 128, 32, 2, 128, 32, 2}, + {12000000, 11025, 1, 8, 4672, 128, 24, 3, 128, 24, 3}, + {12500000, 11025, 1, 7, 2253, 128, 32, 2, 128, 32, 2}, /* 16k rate */ - {12000000, 16000, 8, 1920, 128, 24, 2, 128, 24, 2}, - {12000000, 16000, 8, 1920, 128, 16, 3, 128, 16, 3}, - {12500000, 16000, 7, 8643, 128, 24, 2, 128, 24, 2}, + {12000000, 16000, 1, 8, 1920, 128, 24, 2, 128, 24, 2}, + {12000000, 16000, 1, 8, 1920, 128, 16, 3, 128, 16, 3}, + {12500000, 16000, 1, 7, 8643, 128, 24, 2, 128, 24, 2}, /* 22.05k rate */ - {12000000, 22050, 7, 5264, 128, 16, 2, 128, 16, 2}, - {12000000, 22050, 8, 4672, 128, 12, 3, 128, 12, 3}, - {12500000, 22050, 7, 2253, 128, 16, 2, 128, 16, 2}, + {12000000, 22050, 1, 7, 5264, 128, 16, 2, 128, 16, 2}, + {12000000, 22050, 1, 8, 4672, 128, 12, 3, 128, 12, 3}, + {12500000, 22050, 1, 7, 2253, 128, 16, 2, 128, 16, 2}, /* 32k rate */ - {12000000, 32000, 8, 1920, 128, 12, 2, 128, 12, 2}, - {12000000, 32000, 8, 1920, 128, 8, 3, 128, 8, 3}, - {12500000, 32000, 7, 8643, 128, 12, 2, 128, 12, 2}, + {12000000, 32000, 1, 8, 1920, 128, 12, 2, 128, 12, 2}, + {12000000, 32000, 1, 8, 1920, 128, 8, 3, 128, 8, 3}, + {12500000, 32000, 1, 7, 8643, 128, 12, 2, 128, 12, 2}, /* 44.1k rate */ - {12000000, 44100, 7, 5264, 128, 8, 2, 128, 8, 2}, - {12000000, 44100, 8, 4672, 128, 6, 3, 128, 6, 3}, - {12500000, 44100, 7, 2253, 128, 8, 2, 128, 8, 2}, + {12000000, 44100, 1, 7, 5264, 128, 8, 2, 128, 8, 2}, + {12000000, 44100, 1, 8, 4672, 128, 6, 3, 128, 6, 3}, + {12500000, 44100, 1, 7, 2253, 128, 8, 2, 128, 8, 2}, /* 48k rate */ - {12000000, 48000, 8, 1920, 128, 8, 2, 128, 8, 2}, - {12000000, 48000, 7, 6800, 96, 5, 4, 96, 5, 4}, - {12500000, 48000, 7, 8643, 128, 8, 2, 128, 8, 2}, + {12000000, 48000, 1, 8, 1920, 128, 8, 2, 128, 8, 2}, + {12000000, 48000, 1, 7, 6800, 96, 5, 4, 96, 5, 4}, + {12500000, 48000, 1, 7, 8643, 128, 8, 2, 128, 8, 2}, /* 88.2k rate */ - {12000000, 88200, 7, 5264, 64, 8, 2, 64, 8, 2}, - {12000000, 88200, 8, 4672, 64, 6, 3, 64, 6, 3}, - {12500000, 88200, 7, 2253, 64, 8, 2, 64, 8, 2}, + {12000000, 88200, 1, 7, 5264, 64, 8, 2, 64, 8, 2}, + {12000000, 88200, 1, 8, 4672, 64, 6, 3, 64, 6, 3}, + {12500000, 88200, 1, 7, 2253, 64, 8, 2, 64, 8, 2}, /* 96k rate */ - {12000000, 96000, 8, 1920, 64, 8, 2, 64, 8, 2}, - {12000000, 96000, 7, 6800, 48, 5, 4, 48, 5, 4}, - {12500000, 96000, 7, 8643, 64, 8, 2, 64, 8, 2}, + {12000000, 96000, 1, 8, 1920, 64, 8, 2, 64, 8, 2}, + {12000000, 96000, 1, 7, 6800, 48, 5, 4, 48, 5, 4}, + {12500000, 96000, 1, 7, 8643, 64, 8, 2, 64, 8, 2}, /* 176.4k rate */ - {12000000, 176400, 7, 5264, 32, 8, 2, 32, 8, 2}, - {12000000, 176400, 8, 4672, 32, 6, 3, 32, 6, 3}, - {12500000, 176400, 7, 2253, 32, 8, 2, 32, 8, 2}, + {12000000, 176400, 1, 7, 5264, 32, 8, 2, 32, 8, 2}, + {12000000, 176400, 1, 8, 4672, 32, 6, 3, 32, 6, 3}, + {12500000, 176400, 1, 7, 2253, 32, 8, 2, 32, 8, 2}, /* 192k rate */ - {12000000, 192000, 8, 1920, 32, 8, 2, 32, 8, 2}, - {12000000, 192000, 7, 6800, 24, 5, 4, 24, 5, 4}, - {12500000, 192000, 7, 8643, 32, 8, 2, 32, 8, 2}, + {12000000, 192000, 1, 8, 1920, 32, 8, 2, 32, 8, 2}, + {12000000, 192000, 1, 7, 6800, 24, 5, 4, 24, 5, 4}, + {12500000, 192000, 1, 7, 8643, 32, 8, 2, 32, 8, 2}, }; static const char * const ldac_in_text[] = { @@ -888,7 +889,7 @@ static int aic31xx_setup_pll(struct snd_soc_component *component, /* PLL configuration */ snd_soc_component_update_bits(component, AIC31XX_PLLPR, AIC31XX_PLL_MASK, - (aic31xx->p_div << 4) | 0x01); + (aic31xx->p_div << 4) | aic31xx_divs[i].pll_r); snd_soc_component_write(component, AIC31XX_PLLJ, aic31xx_divs[i].pll_j); snd_soc_component_write(component, AIC31XX_PLLDMSB, -- cgit v1.2.3 From 6e6752a9c78738e27bde6da5cefa393b589276bb Mon Sep 17 00:00:00 2001 From: Ariel D'Alessandro Date: Fri, 19 Nov 2021 12:32:46 -0300 Subject: ASoC: tlv320aic31xx: Add divs for bclk as clk_in Add divisors for rates needed when the clk_in is set to BCLK. Signed-off-by: Michael Trimarchi Signed-off-by: Ariel D'Alessandro Link: https://lore.kernel.org/r/20211119153248.419802-4-ariel.dalessandro@collabora.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic31xx.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index 1aec03d834d0..e8307f0737f2 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -195,46 +195,66 @@ struct aic31xx_rate_divs { static const struct aic31xx_rate_divs aic31xx_divs[] = { /* mclk/p rate pll: r j d dosr ndac mdac aors nadc madc */ /* 8k rate */ + { 512000, 8000, 4, 48, 0, 128, 48, 2, 128, 48, 2}, {12000000, 8000, 1, 8, 1920, 128, 48, 2, 128, 48, 2}, {12000000, 8000, 1, 8, 1920, 128, 32, 3, 128, 32, 3}, {12500000, 8000, 1, 7, 8643, 128, 48, 2, 128, 48, 2}, /* 11.025k rate */ + { 705600, 11025, 3, 48, 0, 128, 24, 3, 128, 24, 3}, {12000000, 11025, 1, 7, 5264, 128, 32, 2, 128, 32, 2}, {12000000, 11025, 1, 8, 4672, 128, 24, 3, 128, 24, 3}, {12500000, 11025, 1, 7, 2253, 128, 32, 2, 128, 32, 2}, /* 16k rate */ + { 512000, 16000, 4, 48, 0, 128, 16, 3, 128, 16, 3}, + { 1024000, 16000, 2, 48, 0, 128, 16, 3, 128, 16, 3}, {12000000, 16000, 1, 8, 1920, 128, 24, 2, 128, 24, 2}, {12000000, 16000, 1, 8, 1920, 128, 16, 3, 128, 16, 3}, {12500000, 16000, 1, 7, 8643, 128, 24, 2, 128, 24, 2}, /* 22.05k rate */ + { 705600, 22050, 4, 36, 0, 128, 12, 3, 128, 12, 3}, + { 1411200, 22050, 2, 36, 0, 128, 12, 3, 128, 12, 3}, {12000000, 22050, 1, 7, 5264, 128, 16, 2, 128, 16, 2}, {12000000, 22050, 1, 8, 4672, 128, 12, 3, 128, 12, 3}, {12500000, 22050, 1, 7, 2253, 128, 16, 2, 128, 16, 2}, /* 32k rate */ + { 1024000, 32000, 2, 48, 0, 128, 12, 2, 128, 12, 2}, + { 2048000, 32000, 1, 48, 0, 128, 12, 2, 128, 12, 2}, {12000000, 32000, 1, 8, 1920, 128, 12, 2, 128, 12, 2}, {12000000, 32000, 1, 8, 1920, 128, 8, 3, 128, 8, 3}, {12500000, 32000, 1, 7, 8643, 128, 12, 2, 128, 12, 2}, /* 44.1k rate */ + { 1411200, 44100, 2, 32, 0, 128, 8, 2, 128, 8, 2}, + { 2822400, 44100, 1, 32, 0, 128, 8, 2, 128, 8, 2}, {12000000, 44100, 1, 7, 5264, 128, 8, 2, 128, 8, 2}, {12000000, 44100, 1, 8, 4672, 128, 6, 3, 128, 6, 3}, {12500000, 44100, 1, 7, 2253, 128, 8, 2, 128, 8, 2}, /* 48k rate */ + { 1536000, 48000, 2, 32, 0, 128, 8, 2, 128, 8, 2}, + { 3072000, 48000, 1, 32, 0, 128, 8, 2, 128, 8, 2}, {12000000, 48000, 1, 8, 1920, 128, 8, 2, 128, 8, 2}, {12000000, 48000, 1, 7, 6800, 96, 5, 4, 96, 5, 4}, {12500000, 48000, 1, 7, 8643, 128, 8, 2, 128, 8, 2}, /* 88.2k rate */ + { 2822400, 88200, 2, 16, 0, 64, 8, 2, 64, 8, 2}, + { 5644800, 88200, 1, 16, 0, 64, 8, 2, 64, 8, 2}, {12000000, 88200, 1, 7, 5264, 64, 8, 2, 64, 8, 2}, {12000000, 88200, 1, 8, 4672, 64, 6, 3, 64, 6, 3}, {12500000, 88200, 1, 7, 2253, 64, 8, 2, 64, 8, 2}, /* 96k rate */ + { 3072000, 96000, 2, 16, 0, 64, 8, 2, 64, 8, 2}, + { 6144000, 96000, 1, 16, 0, 64, 8, 2, 64, 8, 2}, {12000000, 96000, 1, 8, 1920, 64, 8, 2, 64, 8, 2}, {12000000, 96000, 1, 7, 6800, 48, 5, 4, 48, 5, 4}, {12500000, 96000, 1, 7, 8643, 64, 8, 2, 64, 8, 2}, /* 176.4k rate */ + { 5644800, 176400, 2, 8, 0, 32, 8, 2, 32, 8, 2}, + {11289600, 176400, 1, 8, 0, 32, 8, 2, 32, 8, 2}, {12000000, 176400, 1, 7, 5264, 32, 8, 2, 32, 8, 2}, {12000000, 176400, 1, 8, 4672, 32, 6, 3, 32, 6, 3}, {12500000, 176400, 1, 7, 2253, 32, 8, 2, 32, 8, 2}, /* 192k rate */ + { 6144000, 192000, 2, 8, 0, 32, 8, 2, 32, 8, 2}, + {12288000, 192000, 1, 8, 0, 32, 8, 2, 32, 8, 2}, {12000000, 192000, 1, 8, 1920, 32, 8, 2, 32, 8, 2}, {12000000, 192000, 1, 7, 6800, 24, 5, 4, 24, 5, 4}, {12500000, 192000, 1, 7, 8643, 32, 8, 2, 32, 8, 2}, -- cgit v1.2.3 From c5d22d5e12e776fee4e346dc098fe51d00c2f983 Mon Sep 17 00:00:00 2001 From: Ariel D'Alessandro Date: Fri, 19 Nov 2021 12:32:47 -0300 Subject: ASoC: tlv320aic31xx: Handle BCLK set as PLL input configuration If BCLK is used as PLL input, the sysclk is determined by the hw params. So it must be updated here to match the input frequency, based on sample rate, format and channels. Signed-off-by: Ariel D'Alessandro Signed-off-by: Michael Trimarchi Link: https://lore.kernel.org/r/20211119153248.419802-5-ariel.dalessandro@collabora.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic31xx.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index e8307f0737f2..4224b4b3cae6 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -169,6 +170,7 @@ struct aic31xx_priv { struct regulator_bulk_data supplies[AIC31XX_NUM_SUPPLIES]; struct aic31xx_disable_nb disable_nb[AIC31XX_NUM_SUPPLIES]; struct snd_soc_jack *jack; + u32 sysclk_id; unsigned int sysclk; u8 p_div; int rate_div_line; @@ -962,6 +964,7 @@ static int aic31xx_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; + struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component); u8 data = 0; dev_dbg(component->dev, "## %s: width %d rate %d\n", @@ -993,6 +996,16 @@ static int aic31xx_hw_params(struct snd_pcm_substream *substream, AIC31XX_IFACE1_DATALEN_MASK, data); + /* + * If BCLK is used as PLL input, the sysclk is determined by the hw + * params. So it must be updated here to match the input frequency. + */ + if (aic31xx->sysclk_id == AIC31XX_PLL_CLKIN_BCLK) { + aic31xx->sysclk = params_rate(params) * params_width(params) * + params_channels(params); + aic31xx->p_div = 1; + } + return aic31xx_setup_pll(component, params); } @@ -1177,6 +1190,7 @@ static int aic31xx_set_dai_sysclk(struct snd_soc_dai *codec_dai, snd_soc_component_update_bits(component, AIC31XX_CLKMUX, AIC31XX_PLL_CLKIN_MASK, clk_id << AIC31XX_PLL_CLKIN_SHIFT); + aic31xx->sysclk_id = clk_id; aic31xx->sysclk = freq; return 0; -- cgit v1.2.3 From 8c9b9cfb7724685ce705f511b882f30597596536 Mon Sep 17 00:00:00 2001 From: Ariel D'Alessandro Date: Fri, 19 Nov 2021 12:32:48 -0300 Subject: ASoC: fsl-asoc-card: Support fsl,imx-audio-tlv320aic31xx codec Add entry for fsl,imx-audio-tlv320aic31xx audio codec. This codec is configured to use BCLK as clock input. Signed-off-by: Michael Trimarchi Signed-off-by: Ariel D'Alessandro Link: https://lore.kernel.org/r/20211119153248.419802-6-ariel.dalessandro@collabora.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl-asoc-card.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 6e6494f9f399..90cbed496f98 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -26,6 +26,7 @@ #include "../codecs/wm8962.h" #include "../codecs/wm8960.h" #include "../codecs/wm8994.h" +#include "../codecs/tlv320aic31xx.h" #define CS427x_SYSCLK_MCLK 0 @@ -629,6 +630,16 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) } else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic32x4")) { codec_dai_name = "tlv320aic32x4-hifi"; priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; + } else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic31xx")) { + codec_dai_name = "tlv320dac31xx-hifi"; + priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; + priv->dai_link[1].dpcm_capture = 0; + priv->dai_link[2].dpcm_capture = 0; + priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT; + priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT; + priv->codec_priv.mclk_id = AIC31XX_PLL_CLKIN_BCLK; + priv->card.dapm_routes = audio_map_tx; + priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8962")) { codec_dai_name = "wm8962"; priv->codec_priv.mclk_id = WM8962_SYSCLK_MCLK; @@ -888,6 +899,7 @@ static const struct of_device_id fsl_asoc_card_dt_ids[] = { { .compatible = "fsl,imx-audio-cs42888", }, { .compatible = "fsl,imx-audio-cs427x", }, { .compatible = "fsl,imx-audio-tlv320aic32x4", }, + { .compatible = "fsl,imx-audio-tlv320aic31xx", }, { .compatible = "fsl,imx-audio-sgtl5000", }, { .compatible = "fsl,imx-audio-wm8962", }, { .compatible = "fsl,imx-audio-wm8960", }, -- cgit v1.2.3 From 7c72665c5667d4566e594ea362c50a6007b405fb Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 23 Nov 2021 18:02:47 +0100 Subject: ALSA: led: Use restricted type for iface assignment Fix a sparse warning that complains about the inconsistent type assignment for iface, which is a restricted type of snd_ctl_elem_iface_t. Fixes: a135dfb5de15 ("ALSA: led control - add sysfs kcontrol LED marking layer") Reported-by: kernel test robot Reviewed-by: Jaroslav Kysela Link: https://lore.kernel.org/r/202111201028.xduVYgH5-lkp@intel.com Link: https://lore.kernel.org/r/20211123170247.2962-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/core/control_led.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/core/control_led.c b/sound/core/control_led.c index a95332b2b90b..207828f30983 100644 --- a/sound/core/control_led.c +++ b/sound/core/control_led.c @@ -509,7 +509,7 @@ static char *parse_string(char *s, char *val, size_t val_size) return s; } -static char *parse_iface(char *s, unsigned int *val) +static char *parse_iface(char *s, snd_ctl_elem_iface_t *val) { if (!strncasecmp(s, "card", 4)) *val = SNDRV_CTL_ELEM_IFACE_CARD; -- cgit v1.2.3 From 0e888a74e52db369e19aec908131cf171079b306 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 19 Nov 2021 17:08:49 -0600 Subject: ALSA: pcm: unconditionally check if appl_ptr is in 0..boundary range In some cases, the appl_ptr passed by userspace is not checked before being used. This patch adds an unconditional check and returns an error code should the appl_ptr exceed the ALSA 'boundary'. Suggested-by: Takashi Iwai Reviewed-by: Takashi Iwai Reviewed-by: Ranjani Sridharan Reviewed-by: Kai Vehmanen Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20211119230852.206310-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/core/pcm_lib.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 4f4b4739f987..fdd992772b20 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -2133,6 +2133,9 @@ int pcm_lib_apply_appl_ptr(struct snd_pcm_substream *substream, if (old_appl_ptr == appl_ptr) return 0; + if (appl_ptr >= runtime->boundary) + return -EINVAL; + runtime->control->appl_ptr = appl_ptr; if (substream->ops->ack) { ret = substream->ops->ack(substream); -- cgit v1.2.3 From b456abe63f60ad93c83a526d33b71574bc32656c Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 19 Nov 2021 17:08:50 -0600 Subject: ALSA: pcm: introduce INFO_NO_REWINDS flag When the hardware can only deal with a monotonically increasing appl_ptr, this flag can be set. In case the application requests a rewind, be it with a snd_pcm_rewind() or with a direct change of a mmap'ed pointer followed by a SNDRV_PCM_IOCTL_SYNC_PTR, this patch checks if a rewind occurred and returns an error. Credits to Takashi Iwai for identifying the path with SYNC_PTR and suggesting the pointer checks. Suggested-by: Takashi Iwai Reviewed-by: Takashi Iwai Reviewed-by: Ranjani Sridharan Reviewed-by: Kai Vehmanen Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20211119230852.206310-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- include/uapi/sound/asound.h | 2 +- sound/core/pcm_lib.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index 5fbb79e30819..ff7e638221c5 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -300,7 +300,7 @@ typedef int __bitwise snd_pcm_subformat_t; #define SNDRV_PCM_INFO_HAS_LINK_ESTIMATED_ATIME 0x04000000 /* report estimated link audio time */ #define SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME 0x08000000 /* report synchronized audio/system time */ #define SNDRV_PCM_INFO_EXPLICIT_SYNC 0x10000000 /* needs explicit sync of pointers and data */ - +#define SNDRV_PCM_INFO_NO_REWINDS 0x20000000 /* hardware can only support monotonic changes of appl_ptr */ #define SNDRV_PCM_INFO_DRAIN_TRIGGER 0x40000000 /* internal kernel flag - trigger in drain */ #define SNDRV_PCM_INFO_FIFO_IN_FRAMES 0x80000000 /* internal kernel flag - FIFO size is in frames */ diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index fdd992772b20..f2090025236b 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -2128,6 +2128,7 @@ int pcm_lib_apply_appl_ptr(struct snd_pcm_substream *substream, { struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t old_appl_ptr = runtime->control->appl_ptr; + snd_pcm_sframes_t diff; int ret; if (old_appl_ptr == appl_ptr) @@ -2135,6 +2136,19 @@ int pcm_lib_apply_appl_ptr(struct snd_pcm_substream *substream, if (appl_ptr >= runtime->boundary) return -EINVAL; + /* + * check if a rewind is requested by the application + */ + if (substream->runtime->info & SNDRV_PCM_INFO_NO_REWINDS) { + diff = appl_ptr - old_appl_ptr; + if (diff >= 0) { + if (diff > runtime->buffer_size) + return -EINVAL; + } else { + if (runtime->boundary + diff > runtime->buffer_size) + return -EINVAL; + } + } runtime->control->appl_ptr = appl_ptr; if (substream->ops->ack) { -- cgit v1.2.3 From 4a39ea3f07f14f21a6b97e78c972f71fc5761d3a Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Fri, 19 Nov 2021 17:08:51 -0600 Subject: ASoC: SOF: pcm: add .ack callback support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the indirections required at the core level for platform-specific operations on ack. Note that on errors in the .ack the ALSA core will restore the previous appl_ptr. Reviewed-by: Péter Ujfalusi Reviewed-by: Kai Vehmanen Signed-off-by: Ranjani Sridharan Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20211119230852.206310-4-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ops.h | 10 ++++++++++ sound/soc/sof/pcm.c | 9 +++++++++ sound/soc/sof/sof-priv.h | 3 +++ 3 files changed, 22 insertions(+) diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index a0648a13e3eb..0226a53148c9 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -487,6 +487,16 @@ snd_sof_pcm_platform_pointer(struct snd_sof_dev *sdev, return 0; } +/* pcm ack */ +static inline int snd_sof_pcm_platform_ack(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream) +{ + if (sof_ops(sdev) && sof_ops(sdev)->pcm_ack) + return sof_ops(sdev)->pcm_ack(sdev, substream); + + return 0; +} + #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) static inline int snd_sof_probe_compr_assign(struct snd_sof_dev *sdev, diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 31dd79b794f1..98aa5a6579e3 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -917,6 +917,14 @@ static void sof_pcm_remove(struct snd_soc_component *component) snd_soc_tplg_component_remove(component); } +static int sof_pcm_ack(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + + return snd_sof_pcm_platform_ack(sdev, substream); +} + void snd_sof_new_platform_drv(struct snd_sof_dev *sdev) { struct snd_soc_component_driver *pd = &sdev->plat_drv; @@ -935,6 +943,7 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev) pd->hw_free = sof_pcm_hw_free; pd->trigger = sof_pcm_trigger; pd->pointer = sof_pcm_pointer; + pd->ack = sof_pcm_ack; #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) pd->compress_ops = &sof_probe_compressed_ops; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index a9c5197617f1..16caf5c74035 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -207,6 +207,9 @@ struct snd_sof_dsp_ops { snd_pcm_uframes_t (*pcm_pointer)(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); /* optional */ + /* pcm ack */ + int (*pcm_ack)(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); /* optional */ + #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) /* Except for probe_pointer, all probe ops are mandatory */ int (*probe_assign)(struct snd_sof_dev *sdev, -- cgit v1.2.3 From 6c26b5054ce2b822856e32f1840d13f777c6f295 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Fri, 19 Nov 2021 17:08:52 -0600 Subject: ASoC: SOF: Intel: add .ack support for HDaudio platforms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we disable rewinds, then the .ack can be used to program SPIB with the application pointer, which allows the HDaudio DMA to save power by opportunistically bursting data transfers when the path to memory is enabled (and conversely to shut it down when there are no transfer requests). The SPIB register can only be programmed with incremental values with wrap-around after the DMA RUN bits are set. For simplicity, we set the INFO_NO_REWINDS flag in the .open callback when we already need to program the SNDRV_PCM_INFO_SYNC_APPLPTR flag. Rewinds are not used by many applications. One notable application using rewinds is PulseAudio. Practical experiments with Ubuntu/PulseAudio default settings did not show any audible issues, but the user may hear volume changes and notification with a delay, depending on the size of the ring buffer and latency constraints. The choice of disabling rewinds is exposed as a kernel parameter and not a Kconfig option to avoid any undesirable side-effects. Reviewed-by: Péter Ujfalusi Reviewed-by: Kai Vehmanen Co-developed-by: Pierre-Louis Bossart Signed-off-by: Pierre-Louis Bossart Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20211119230852.206310-5-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/apl.c | 1 + sound/soc/sof/intel/cnl.c | 1 + sound/soc/sof/intel/hda-pcm.c | 41 ++++++++++++++++++++++++++++++++++++++-- sound/soc/sof/intel/hda-stream.c | 2 ++ sound/soc/sof/intel/hda.h | 1 + sound/soc/sof/intel/icl.c | 1 + sound/soc/sof/intel/tgl.c | 1 + 7 files changed, 46 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index 1baf0fddeb3d..8778f46f1d37 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -78,6 +78,7 @@ const struct snd_sof_dsp_ops sof_apl_ops = { .pcm_hw_free = hda_dsp_stream_hw_free, .pcm_trigger = hda_dsp_pcm_trigger, .pcm_pointer = hda_dsp_pcm_pointer, + .pcm_ack = hda_dsp_pcm_ack, #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) /* probe callbacks */ diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index d455272bfc8e..04daaa6100f1 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -283,6 +283,7 @@ const struct snd_sof_dsp_ops sof_cnl_ops = { .pcm_hw_free = hda_dsp_stream_hw_free, .pcm_trigger = hda_dsp_pcm_trigger, .pcm_pointer = hda_dsp_pcm_pointer, + .pcm_ack = hda_dsp_pcm_ack, #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) /* probe callbacks */ diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index cc8ddef37f37..974383cd0440 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -32,6 +32,10 @@ static bool hda_always_enable_dmi_l1; module_param_named(always_enable_dmi_l1, hda_always_enable_dmi_l1, bool, 0444); MODULE_PARM_DESC(always_enable_dmi_l1, "SOF HDA always enable DMI l1"); +static bool hda_disable_rewinds = IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_DISABLE_REWINDS); +module_param_named(disable_rewinds, hda_disable_rewinds, bool, 0444); +MODULE_PARM_DESC(disable_rewinds, "SOF HDA disable rewinds"); + u32 hda_dsp_get_mult_div(struct snd_sof_dev *sdev, int rate) { switch (rate) { @@ -120,8 +124,11 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, return ret; } - /* disable SPIB, to enable buffer wrap for stream */ - hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0); + /* enable SPIB when rewinds are disabled */ + if (hda_disable_rewinds) + hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_ENABLE, 0); + else + hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0); /* update no_stream_position flag for ipc params */ if (hda && hda->no_ipc_position) { @@ -140,6 +147,29 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, return 0; } +/* update SPIB register with appl position */ +int hda_dsp_pcm_ack(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream) +{ + struct hdac_stream *hstream = substream->runtime->private_data; + struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream); + struct snd_pcm_runtime *runtime = substream->runtime; + ssize_t appl_pos, buf_size; + u32 spib; + + appl_pos = frames_to_bytes(runtime, runtime->control->appl_ptr); + buf_size = frames_to_bytes(runtime, runtime->buffer_size); + + spib = appl_pos % buf_size; + + /* Allowable value for SPIB is 1 byte to max buffer size */ + if (!spib) + spib = buf_size; + + sof_io_write(sdev, hext_stream->spib_addr, spib); + + return 0; +} + int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, int cmd) { @@ -234,6 +264,13 @@ int hda_dsp_pcm_open(struct snd_sof_dev *sdev, return -EINVAL; } + /* + * if we want the .ack to work, we need to prevent the control from being mapped. + * The status can still be mapped. + */ + if (hda_disable_rewinds) + runtime->hw.info |= SNDRV_PCM_INFO_NO_REWINDS | SNDRV_PCM_INFO_SYNC_APPLPTR; + /* * All playback streams are DMI L1 capable, capture streams need * pause push/release to be disabled diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 1d845c2cbc33..b6f037815344 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -655,6 +655,8 @@ int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev, SOF_HDA_REG_PP_PPCTL, mask, 0); spin_unlock_irq(&bus->reg_lock); + hda_dsp_stream_spib_config(sdev, link_dev, HDA_DSP_SPIB_DISABLE, 0); + stream->substream = NULL; return 0; diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 646f5d4dc882..8ed4031ca007 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -534,6 +534,7 @@ int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, int cmd); snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); +int hda_dsp_pcm_ack(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); /* * DSP Stream Operations. diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c index 6c5422157ec8..343c1af7c453 100644 --- a/sound/soc/sof/intel/icl.c +++ b/sound/soc/sof/intel/icl.c @@ -77,6 +77,7 @@ const struct snd_sof_dsp_ops sof_icl_ops = { .pcm_hw_free = hda_dsp_stream_hw_free, .pcm_trigger = hda_dsp_pcm_trigger, .pcm_pointer = hda_dsp_pcm_pointer, + .pcm_ack = hda_dsp_pcm_ack, #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) /* probe callbacks */ diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c index 237e92e790b7..7f7929c5cb88 100644 --- a/sound/soc/sof/intel/tgl.c +++ b/sound/soc/sof/intel/tgl.c @@ -113,6 +113,7 @@ const struct snd_sof_dsp_ops sof_tgl_ops = { .pcm_hw_free = hda_dsp_stream_hw_free, .pcm_trigger = hda_dsp_pcm_trigger, .pcm_pointer = hda_dsp_pcm_pointer, + .pcm_ack = hda_dsp_pcm_ack, #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) /* probe callbacks */ -- cgit v1.2.3 From 01429183f479c54c1b5d15453a8ce574ea43e525 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 23 Nov 2021 19:16:04 +0200 Subject: ASoC: SOF: sof-audio: setup sched widgets during pipeline complete step Older firmware prior to ABI 3.19 has a dependency where the scheduler widgets need to be setup last. Moving the call to sof_widget_setup() before the pipeline_complete() call also helps remove the need for the 'reverse' direction when walking through the widget list - this was only working because of the topology macros but the topology does not require any order. Fixes: 5fcdbb2d45df ("ASoC: SOF: Add support for dynamic pipelines") Signed-off-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211123171606.129350-1-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-audio.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 0f2566f7c094..f4e142ec0fbd 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -637,16 +637,25 @@ const struct sof_ipc_pipe_new *snd_sof_pipeline_find(struct snd_sof_dev *sdev, int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify) { + struct sof_ipc_fw_version *v = &sdev->fw_ready.version; struct snd_sof_widget *swidget; struct snd_sof_route *sroute; int ret; /* restore pipeline components */ - list_for_each_entry_reverse(swidget, &sdev->widget_list, list) { + list_for_each_entry(swidget, &sdev->widget_list, list) { /* only set up the widgets belonging to static pipelines */ if (!verify && swidget->dynamic_pipeline_widget) continue; + /* + * For older firmware, skip scheduler widgets in this loop, + * sof_widget_setup() will be called in the 'complete pipeline' loop + */ + if (v->abi_version < SOF_ABI_VER(3, 19, 0) && + swidget->id == snd_soc_dapm_scheduler) + continue; + /* update DAI config. The IPC will be sent in sof_widget_setup() */ if (WIDGET_IS_DAI(swidget->id)) { struct snd_sof_dai *dai = swidget->private; @@ -694,6 +703,12 @@ int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify) if (!verify && swidget->dynamic_pipeline_widget) continue; + if (v->abi_version < SOF_ABI_VER(3, 19, 0)) { + ret = sof_widget_setup(sdev, swidget); + if (ret < 0) + return ret; + } + swidget->complete = snd_sof_complete_pipeline(sdev, swidget); break; @@ -722,7 +737,7 @@ int sof_tear_down_pipelines(struct snd_sof_dev *sdev, bool verify) * sroute->setup because during suspend all streams are suspended and during topology * loading the sound card unavailable to open PCMs. */ - list_for_each_entry_reverse(swidget, &sdev->widget_list, list) { + list_for_each_entry(swidget, &sdev->widget_list, list) { if (swidget->dynamic_pipeline_widget) continue; -- cgit v1.2.3 From fb71d03b29bcbd8c03798d36e7b2a2297b6dea45 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 23 Nov 2021 19:16:05 +0200 Subject: ASoC: SOF: topology: don't use list_for_each_entry_reverse() It's not clear why we would walk the list backwards. That makes no difference. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211123171606.129350-2-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 63948bb30710..b3ad3a604918 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -3529,7 +3529,7 @@ static int sof_complete(struct snd_soc_component *scomp) * Apply the dynamic_pipeline_widget flag and set the pipe_widget field * for all widgets that have the same pipeline ID as the scheduler widget */ - list_for_each_entry_reverse(comp_swidget, &sdev->widget_list, list) + list_for_each_entry(comp_swidget, &sdev->widget_list, list) if (comp_swidget->pipeline_id == swidget->pipeline_id) { ret = sof_set_pipe_widget(sdev, swidget, comp_swidget); if (ret < 0) -- cgit v1.2.3 From 96da174024b9c63bd5d3358668d0bc12677be877 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 23 Nov 2021 19:16:06 +0200 Subject: ASoC: SOF: handle paused streams during system suspend During system suspend, paused streams do not get suspended. Therefore, we need to explicitly free these PCMs in the DSP and free the associated DAPM widgets so that they can be set up again during resume. Fixes: 5fcdbb2d45df ("ASoC: SOF: Add support for dynamic pipelines") Signed-off-by: Ranjani Sridharan Reviewed-by: Paul Olaru Reviewed-by: Bard Liao Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211123171606.129350-3-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/pcm.c | 5 ++-- sound/soc/sof/sof-audio.c | 74 +++++++++++++++++++++++++++++++++++++++++++++-- sound/soc/sof/sof-audio.h | 2 ++ 3 files changed, 76 insertions(+), 5 deletions(-) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 31dd79b794f1..0ceb1a9cbf73 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -100,9 +100,8 @@ void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream) } EXPORT_SYMBOL(snd_sof_pcm_period_elapsed); -static int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, - struct snd_sof_dev *sdev, - struct snd_sof_pcm *spcm) +int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev *sdev, + struct snd_sof_pcm *spcm) { struct sof_ipc_stream stream; struct sof_ipc_reply reply; diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index f4e142ec0fbd..e00ce275052f 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -129,6 +129,14 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) case snd_soc_dapm_buffer: ipc_free.hdr.cmd |= SOF_IPC_TPLG_BUFFER_FREE; break; + case snd_soc_dapm_dai_in: + case snd_soc_dapm_dai_out: + { + struct snd_sof_dai *dai = swidget->private; + + dai->configured = false; + fallthrough; + } default: ipc_free.hdr.cmd |= SOF_IPC_TPLG_COMP_FREE; break; @@ -720,6 +728,55 @@ int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify) return 0; } +/* + * Free the PCM, its associated widgets and set the prepared flag to false for all PCMs that + * did not get suspended(ex: paused streams) so the widgets can be set up again during resume. + */ +static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev) +{ + struct snd_sof_widget *swidget; + struct snd_sof_pcm *spcm; + int dir, ret; + + /* + * free all PCMs and their associated DAPM widgets if their connected DAPM widget + * list is not NULL. This should only be true for paused streams at this point. + * This is equivalent to the handling of FE DAI suspend trigger for running streams. + */ + list_for_each_entry(spcm, &sdev->pcm_list, list) + for_each_pcm_streams(dir) { + struct snd_pcm_substream *substream = spcm->stream[dir].substream; + + if (!substream || !substream->runtime) + continue; + + if (spcm->stream[dir].list) { + ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); + if (ret < 0) + return ret; + + ret = sof_widget_list_free(sdev, spcm, dir); + if (ret < 0) { + dev_err(sdev->dev, "failed to free widgets during suspend\n"); + return ret; + } + } + } + + /* + * free any left over DAI widgets. This is equivalent to the handling of suspend trigger + * for the BE DAI for running streams. + */ + list_for_each_entry(swidget, &sdev->widget_list, list) + if (WIDGET_IS_DAI(swidget->id) && swidget->use_count == 1) { + ret = sof_widget_free(sdev, swidget); + if (ret < 0) + return ret; + } + + return 0; +} + /* * For older firmware, this function doesn't free widgets for static pipelines during suspend. * It only resets use_count for all widgets. @@ -734,8 +791,8 @@ int sof_tear_down_pipelines(struct snd_sof_dev *sdev, bool verify) /* * This function is called during suspend and for one-time topology verification during * first boot. In both cases, there is no need to protect swidget->use_count and - * sroute->setup because during suspend all streams are suspended and during topology - * loading the sound card unavailable to open PCMs. + * sroute->setup because during suspend all running streams are suspended and during + * topology loading the sound card unavailable to open PCMs. */ list_for_each_entry(swidget, &sdev->widget_list, list) { if (swidget->dynamic_pipeline_widget) @@ -754,6 +811,19 @@ int sof_tear_down_pipelines(struct snd_sof_dev *sdev, bool verify) return ret; } + /* + * Tear down all pipelines associated with PCMs that did not get suspended + * and unset the prepare flag so that they can be set up again during resume. + * Skip this step for older firmware. + */ + if (!verify && v->abi_version >= SOF_ABI_VER(3, 19, 0)) { + ret = sof_tear_down_left_over_pipelines(sdev); + if (ret < 0) { + dev_err(sdev->dev, "failed to tear down paused pipelines\n"); + return ret; + } + } + list_for_each_entry(sroute, &sdev->route_list, list) sroute->setup = false; diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 389d56ac3aba..1c4f59d34717 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -265,4 +265,6 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); /* PCM */ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir); int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir); +int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev *sdev, + struct snd_sof_pcm *spcm); #endif -- cgit v1.2.3 From faf695517c1c77bb4a4b46d54007a283962eb00e Mon Sep 17 00:00:00 2001 From: Jiaxin Yu Date: Thu, 25 Nov 2021 12:24:22 +0800 Subject: ASoC: mediatek: remove unnecessary CONFIG_PM The unnecessary conditional inclusion caused the following warning. Such as: >> sound/soc/mediatek/mt8192/mt8192-afe-pcm.c:2368:32: warning: unused >> variable 'mt8192_afe_pm_ops' [-Wunused-const-variable] static const struct dev_pm_ops mt8192_afe_pm_ops = { Because runtime_pm already handles the case without CONFIG_PM, we can remove CONFIG_PM condition. Signed-off-by: Jiaxin Yu Reported-by: kernel test robot Acked-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20211125042422.2349-1-jiaxin.yu@mediatek.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt2701/mt2701-afe-pcm.c | 2 -- sound/soc/mediatek/mt6797/mt6797-afe-pcm.c | 2 -- sound/soc/mediatek/mt8173/mt8173-max98090.c | 2 -- sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c | 2 -- sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c | 2 -- sound/soc/mediatek/mt8173/mt8173-rt5650.c | 2 -- sound/soc/mediatek/mt8183/mt8183-afe-pcm.c | 2 -- sound/soc/mediatek/mt8192/mt8192-afe-pcm.c | 2 -- 8 files changed, 16 deletions(-) diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c index bc3d0466472b..0f178de92a0f 100644 --- a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c +++ b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c @@ -1474,9 +1474,7 @@ static struct platform_driver mt2701_afe_pcm_driver = { .driver = { .name = "mt2701-audio", .of_match_table = mt2701_afe_pcm_dt_match, -#ifdef CONFIG_PM .pm = &mt2701_afe_pm_ops, -#endif }, .probe = mt2701_afe_pcm_dev_probe, .remove = mt2701_afe_pcm_dev_remove, diff --git a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c index 3d68e4726ea2..fb4abec9aa5f 100644 --- a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c +++ b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c @@ -901,9 +901,7 @@ static struct platform_driver mt6797_afe_pcm_driver = { .driver = { .name = "mt6797-audio", .of_match_table = mt6797_afe_pcm_dt_match, -#ifdef CONFIG_PM .pm = &mt6797_afe_pm_ops, -#endif }, .probe = mt6797_afe_pcm_dev_probe, .remove = mt6797_afe_pcm_dev_remove, diff --git a/sound/soc/mediatek/mt8173/mt8173-max98090.c b/sound/soc/mediatek/mt8173/mt8173-max98090.c index fc94314bfc02..2408c9d3d9b3 100644 --- a/sound/soc/mediatek/mt8173/mt8173-max98090.c +++ b/sound/soc/mediatek/mt8173/mt8173-max98090.c @@ -193,9 +193,7 @@ static struct platform_driver mt8173_max98090_driver = { .driver = { .name = "mt8173-max98090", .of_match_table = mt8173_max98090_dt_match, -#ifdef CONFIG_PM .pm = &snd_soc_pm_ops, -#endif }, .probe = mt8173_max98090_dev_probe, }; diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c index 0f28dc2217c0..e6e824f3d24a 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c @@ -231,9 +231,7 @@ static struct platform_driver mt8173_rt5650_rt5514_driver = { .driver = { .name = "mtk-rt5650-rt5514", .of_match_table = mt8173_rt5650_rt5514_dt_match, -#ifdef CONFIG_PM .pm = &snd_soc_pm_ops, -#endif }, .probe = mt8173_rt5650_rt5514_dev_probe, }; diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c index 077c6ee06780..ba6fe3d90bfc 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c @@ -298,9 +298,7 @@ static struct platform_driver mt8173_rt5650_rt5676_driver = { .driver = { .name = "mtk-rt5650-rt5676", .of_match_table = mt8173_rt5650_rt5676_dt_match, -#ifdef CONFIG_PM .pm = &snd_soc_pm_ops, -#endif }, .probe = mt8173_rt5650_rt5676_dev_probe, }; diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c index 2cbf679f5c74..9b933cce0b20 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c @@ -336,9 +336,7 @@ static struct platform_driver mt8173_rt5650_driver = { .driver = { .name = "mtk-rt5650", .of_match_table = mt8173_rt5650_dt_match, -#ifdef CONFIG_PM .pm = &snd_soc_pm_ops, -#endif }, .probe = mt8173_rt5650_dev_probe, }; diff --git a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c index 14e77df06b01..86c8a523fe9e 100644 --- a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c +++ b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c @@ -1279,9 +1279,7 @@ static struct platform_driver mt8183_afe_pcm_driver = { .driver = { .name = "mt8183-audio", .of_match_table = mt8183_afe_pcm_dt_match, -#ifdef CONFIG_PM .pm = &mt8183_afe_pm_ops, -#endif }, .probe = mt8183_afe_pcm_dev_probe, .remove = mt8183_afe_pcm_dev_remove, diff --git a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c index 31c280339c50..e1e4ca931551 100644 --- a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c +++ b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c @@ -2381,9 +2381,7 @@ static struct platform_driver mt8192_afe_pcm_driver = { .driver = { .name = "mt8192-audio", .of_match_table = mt8192_afe_pcm_dt_match, -#ifdef CONFIG_PM .pm = &mt8192_afe_pm_ops, -#endif }, .probe = mt8192_afe_pcm_dev_probe, .remove = mt8192_afe_pcm_dev_remove, -- cgit v1.2.3 From fc6c62cf1cbf24c81ccb1d248120311336d5f3cc Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Thu, 25 Nov 2021 15:16:08 +0800 Subject: ASoC: SOF: mediatek: Add missing of_node_put() in platform_parse_resource() The node pointer is returned by of_parse_phandle() with refcount incremented in platform_parse_resource(). Calling of_node_put() to aovid the refcount leak. Reported-by: Hulk Robot Signed-off-by: Yang Yingliang Link: https://lore.kernel.org/r/20211125071608.3056715-1-yangyingliang@huawei.com Signed-off-by: Mark Brown --- sound/soc/sof/mediatek/mt8195/mt8195.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.c b/sound/soc/sof/mediatek/mt8195/mt8195.c index 40e5a25875a6..55d9812870a4 100644 --- a/sound/soc/sof/mediatek/mt8195/mt8195.c +++ b/sound/soc/sof/mediatek/mt8195/mt8195.c @@ -43,6 +43,7 @@ static int platform_parse_resource(struct platform_device *pdev, void *data) } ret = of_address_to_resource(mem_region, 0, &res); + of_node_put(mem_region); if (ret) { dev_err(dev, "of_address_to_resource dma failed\n"); return ret; @@ -64,6 +65,7 @@ static int platform_parse_resource(struct platform_device *pdev, void *data) } ret = of_address_to_resource(mem_region, 0, &res); + of_node_put(mem_region); if (ret) { dev_err(dev, "of_address_to_resource sysmem failed\n"); return ret; -- cgit v1.2.3 From 49f893253ab43566e34332a969324531fea463f6 Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Thu, 25 Nov 2021 10:51:57 +0100 Subject: ASoC: uniphier: drop selecting non-existing SND_SOC_UNIPHIER_AIO_DMA Commit f37fe2f9987b ("ASoC: uniphier: add support for UniPhier AIO common driver") adds configs SND_SOC_UNIPHIER_{LD11,PXS2}, which select the non-existing config SND_SOC_UNIPHIER_AIO_DMA. Hence, ./scripts/checkkconfigsymbols.py warns: SND_SOC_UNIPHIER_AIO_DMA Referencing files: sound/soc/uniphier/Kconfig Probably, there is actually no further config intended to be selected here. So, just drop selecting the non-existing config. Fixes: f37fe2f9987b ("ASoC: uniphier: add support for UniPhier AIO common driver") Signed-off-by: Lukas Bulwahn Link: https://lore.kernel.org/r/20211125095158.8394-2-lukas.bulwahn@gmail.com Signed-off-by: Mark Brown --- sound/soc/uniphier/Kconfig | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/uniphier/Kconfig b/sound/soc/uniphier/Kconfig index aa3592ee1358..ddfa6424c656 100644 --- a/sound/soc/uniphier/Kconfig +++ b/sound/soc/uniphier/Kconfig @@ -23,7 +23,6 @@ config SND_SOC_UNIPHIER_LD11 tristate "UniPhier LD11/LD20 Device Driver" depends on SND_SOC_UNIPHIER select SND_SOC_UNIPHIER_AIO - select SND_SOC_UNIPHIER_AIO_DMA help This adds ASoC driver for Socionext UniPhier LD11/LD20 input and output that can be used with other codecs. @@ -34,7 +33,6 @@ config SND_SOC_UNIPHIER_PXS2 tristate "UniPhier PXs2 Device Driver" depends on SND_SOC_UNIPHIER select SND_SOC_UNIPHIER_AIO - select SND_SOC_UNIPHIER_AIO_DMA help This adds ASoC driver for Socionext UniPhier PXs2 input and output that can be used with other codecs. -- cgit v1.2.3 From 2039cc1da4bee1fd0df644e26b28ed769cd32a81 Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Thu, 25 Nov 2021 10:51:58 +0100 Subject: ASoC: codecs: wcd938x: add SND_SOC_WCD938_SDW to codec list instead Commit 045442228868 ("ASoC: codecs: wcd938x: add audio routing and Kconfig") adds SND_SOC_WCD937X, which does not exist, and SND_SOC_WCD938X, which seems not really to be the intended config to be selected, but only a supporting config symbol to the actual config SND_SOC_WCD938X_SDW for the codec. Add SND_SOC_WCD938_SDW to the list instead of SND_SOC_WCD93{7,8}X. The issue was identified with ./scripts/checkkconfigsymbols.py. Fixes: 045442228868 ("ASoC: codecs: wcd938x: add audio routing and Kconfig") Signed-off-by: Lukas Bulwahn Link: https://lore.kernel.org/r/20211125095158.8394-3-lukas.bulwahn@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 3fe62df32238..b6d1827e7986 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -241,8 +241,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_UDA1380 imply SND_SOC_WCD9335 imply SND_SOC_WCD934X - imply SND_SOC_WCD937X - imply SND_SOC_WCD938X + imply SND_SOC_WCD938X_SDW imply SND_SOC_LPASS_RX_MACRO imply SND_SOC_LPASS_TX_MACRO imply SND_SOC_WL1273 -- cgit v1.2.3 From 083a7fba38885a8ffa03a2857e383421cefd36e6 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Thu, 25 Nov 2021 13:58:11 +0800 Subject: ASoC: rt5640: Add the binding include file for the HDA header support The patch adds the binding include file for the HDA header support. Signed-off-by: Oder Chiou Link: https://lore.kernel.org/r/20211125055812.8911-1-oder_chiou@realtek.com Signed-off-by: Mark Brown --- include/dt-bindings/sound/rt5640.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/dt-bindings/sound/rt5640.h b/include/dt-bindings/sound/rt5640.h index 154c9b4414f2..655f6946388a 100644 --- a/include/dt-bindings/sound/rt5640.h +++ b/include/dt-bindings/sound/rt5640.h @@ -16,6 +16,7 @@ #define RT5640_JD_SRC_GPIO2 4 #define RT5640_JD_SRC_GPIO3 5 #define RT5640_JD_SRC_GPIO4 6 +#define RT5640_JD_SRC_HDA_HEADER 7 #define RT5640_OVCD_SF_0P5 0 #define RT5640_OVCD_SF_0P75 1 -- cgit v1.2.3 From 2b9c8d2b3c89708d53b6124dc49c212dc5341840 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Thu, 25 Nov 2021 13:58:12 +0800 Subject: ASoC: rt5640: Add the HDA header support The patch adds the HDA header support. Signed-off-by: Oder Chiou Link: https://lore.kernel.org/r/20211125055812.8911-2-oder_chiou@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5640.c | 97 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index d01fe73ab9c8..08b37878cb00 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -195,6 +195,7 @@ static bool rt5640_volatile_register(struct device *dev, unsigned int reg) case RT5640_PRIV_DATA: case RT5640_PGM_REG_ARR1: case RT5640_PGM_REG_ARR3: + case RT5640_DUMMY2: case RT5640_VENDOR_ID: case RT5640_VENDOR_ID1: case RT5640_VENDOR_ID2: @@ -2301,6 +2302,38 @@ static void rt5640_jack_work(struct work_struct *work) struct snd_soc_component *component = rt5640->component; int status; + if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) { + int val, jack_type = 0, hda_mic_plugged, hda_hp_plugged; + + /* mic jack */ + val = snd_soc_component_read(component, RT5640_INT_IRQ_ST); + hda_mic_plugged = !(val & RT5640_JD_STATUS); + dev_dbg(component->dev, "mic jack status %d\n", + hda_mic_plugged); + + snd_soc_component_update_bits(component, RT5640_IRQ_CTRL1, + RT5640_JD_P_MASK, !hda_mic_plugged << RT5640_JD_P_SFT); + + if (hda_mic_plugged) + jack_type |= SND_JACK_MICROPHONE; + + /* headphone jack */ + val = snd_soc_component_read(component, RT5640_DUMMY2); + hda_hp_plugged = !(val & (0x1 << 11)); + dev_dbg(component->dev, "headphone jack status %d\n", + hda_hp_plugged); + + snd_soc_component_update_bits(component, RT5640_DUMMY2, + (0x1 << 10), !hda_hp_plugged << 10); + + if (hda_hp_plugged) + jack_type |= SND_JACK_HEADPHONE; + + snd_soc_jack_report(rt5640->jack, jack_type, SND_JACK_HEADSET); + + return; + } + if (!rt5640_jack_inserted(component)) { /* Jack removed, or spurious IRQ? */ if (rt5640->jack->status & SND_JACK_HEADPHONE) { @@ -2478,13 +2511,57 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component, queue_work(system_long_wq, &rt5640->jack_work); } +static void rt5640_enable_hda_jack_detect( + struct snd_soc_component *component, struct snd_soc_jack *jack) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + int ret; + + /* Select JD1 for Mic */ + snd_soc_component_update_bits(component, RT5640_JD_CTRL, + RT5640_JD_MASK, RT5640_JD_JD1_IN4P); + snd_soc_component_write(component, RT5640_IRQ_CTRL1, RT5640_IRQ_JD_NOR); + + /* Select JD2 for Headphone */ + snd_soc_component_update_bits(component, RT5640_DUMMY2, 0x1100, 0x1100); + + /* Selecting GPIO01 as an interrupt */ + snd_soc_component_update_bits(component, RT5640_GPIO_CTRL1, + RT5640_GP1_PIN_MASK, RT5640_GP1_PIN_IRQ); + + /* Set GPIO1 output */ + snd_soc_component_update_bits(component, RT5640_GPIO_CTRL3, + RT5640_GP1_PF_MASK, RT5640_GP1_PF_OUT); + + snd_soc_component_update_bits(component, RT5640_DUMMY1, 0x700, 0x300); + + rt5640->jack = jack; + + ret = request_irq(rt5640->irq, rt5640_irq, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, "rt5640", rt5640); + if (ret) { + dev_warn(component->dev, "Failed to reguest IRQ %d: %d\n", rt5640->irq, ret); + rt5640->irq = -ENXIO; + return; + } + + /* sync initial jack state */ + queue_work(system_long_wq, &rt5640->jack_work); +} + static int rt5640_set_jack(struct snd_soc_component *component, struct snd_soc_jack *jack, void *data) { - if (jack) - rt5640_enable_jack_detect(component, jack); - else + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + + if (jack) { + if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) + rt5640_enable_hda_jack_detect(component, jack); + else + rt5640_enable_jack_detect(component, jack); + } else { rt5640_disable_jack_detect(component); + } return 0; } @@ -2576,6 +2653,8 @@ static int rt5640_probe(struct snd_soc_component *component) "realtek,jack-detect-source", &val) == 0) { if (val <= RT5640_JD_SRC_GPIO4) rt5640->jd_src = val << RT5640_JD_SFT; + else if (val == RT5640_JD_SRC_HDA_HEADER) + rt5640->jd_src = RT5640_JD_SRC_HDA_HEADER; else dev_warn(component->dev, "Warning: Invalid jack-detect-source value: %d, leaving jack-detect disabled\n", val); @@ -2632,6 +2711,7 @@ static int rt5640_suspend(struct snd_soc_component *component) { struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + rt5640_cancel_work(rt5640); snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF); rt5640_reset(component); regcache_cache_only(rt5640->regmap, true); @@ -2654,6 +2734,17 @@ static int rt5640_resume(struct snd_soc_component *component) regcache_cache_only(rt5640->regmap, false); regcache_sync(rt5640->regmap); + if (rt5640->jd_src) { + if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) + snd_soc_component_update_bits(component, + RT5640_DUMMY2, 0x1100, 0x1100); + else + snd_soc_component_write(component, RT5640_DUMMY2, + 0x4001); + + queue_work(system_long_wq, &rt5640->jack_work); + } + return 0; } #else -- cgit v1.2.3 From e14cddc5888418cc9f2ba66c01a04cdbab3b5b25 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 25 Nov 2021 12:15:11 +0200 Subject: ASoC: SOF: Intel: hda: clear stream before freeing the DAI widget The DAI_CONFIG IPC that is sent during the STOP trigger is used for stopping the DMA in the FW. This must be done after the DMA RUN bit is cleared by the host. So move the call to snd_hdac_ext_link_stream_clear() before hda_link_dai_widget_update() to follow the correct programming sequence for DMA stop for HDA DAIs. Signed-off-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211125101520.291581-2-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-dai.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 5c9ee6c49473..748e8ed61475 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -329,6 +329,8 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: + snd_hdac_ext_link_stream_clear(link_dev); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) w = dai->playback_widget; else @@ -347,8 +349,7 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, } link_dev->link_prepared = 0; - - fallthrough; + break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: snd_hdac_ext_link_stream_clear(link_dev); break; -- cgit v1.2.3 From 2b1acedccf36434924ae530410e008e7eb427cd3 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 25 Nov 2021 12:15:12 +0200 Subject: ASoC: SOF: Intel: hda: Add a helper function for stream reset Add a helper function to perform stream reset. Signed-off-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211125101520.291581-3-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-stream.c | 76 +++++++++++++++++++++++----------------- sound/soc/sof/intel/hda.h | 2 ++ 2 files changed, 46 insertions(+), 32 deletions(-) diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index b6f037815344..c2895bdd3f07 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -279,6 +279,45 @@ int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag) return 0; } +static int hda_dsp_stream_reset(struct snd_sof_dev *sdev, struct hdac_stream *hstream) +{ + int sd_offset = SOF_STREAM_SD_OFFSET(hstream); + int timeout = HDA_DSP_STREAM_RESET_TIMEOUT; + u32 val; + + /* enter stream reset */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, SOF_STREAM_SD_OFFSET_CRST, + SOF_STREAM_SD_OFFSET_CRST); + do { + val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, sd_offset); + if (val & SOF_STREAM_SD_OFFSET_CRST) + break; + } while (--timeout); + if (timeout == 0) { + dev_err(sdev->dev, "timeout waiting for stream reset\n"); + return -ETIMEDOUT; + } + + timeout = HDA_DSP_STREAM_RESET_TIMEOUT; + + /* exit stream reset and wait to read a zero before reading any other register */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, SOF_STREAM_SD_OFFSET_CRST, 0x0); + + /* wait for hardware to report that stream is out of reset */ + udelay(3); + do { + val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, sd_offset); + if ((val & SOF_STREAM_SD_OFFSET_CRST) == 0) + break; + } while (--timeout); + if (timeout == 0) { + dev_err(sdev->dev, "timeout waiting for stream to exit reset\n"); + return -ETIMEDOUT; + } + + return 0; +} + int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream, int cmd) { @@ -436,9 +475,9 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, struct hdac_bus *bus = sof_to_bus(sdev); struct hdac_stream *hstream = &stream->hstream; int sd_offset = SOF_STREAM_SD_OFFSET(hstream); - int ret, timeout = HDA_DSP_STREAM_RESET_TIMEOUT; + int ret; u32 dma_start = SOF_HDA_SD_CTL_DMA_START; - u32 val, mask; + u32 mask; u32 run; if (!stream) { @@ -483,36 +522,9 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, SOF_HDA_CL_DMA_SD_INT_MASK); /* stream reset */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 0x1, - 0x1); - udelay(3); - do { - val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, - sd_offset); - if (val & 0x1) - break; - } while (--timeout); - if (timeout == 0) { - dev_err(sdev->dev, "error: stream reset failed\n"); - return -ETIMEDOUT; - } - - timeout = HDA_DSP_STREAM_RESET_TIMEOUT; - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 0x1, - 0x0); - - /* wait for hardware to report that stream is out of reset */ - udelay(3); - do { - val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, - sd_offset); - if ((val & 0x1) == 0) - break; - } while (--timeout); - if (timeout == 0) { - dev_err(sdev->dev, "error: timeout waiting for stream reset\n"); - return -ETIMEDOUT; - } + ret = hda_dsp_stream_reset(sdev, hstream); + if (ret < 0) + return ret; if (hstream->posbuf) *hstream->posbuf = 0; diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 8ed4031ca007..60139ea9b8de 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -487,6 +487,8 @@ struct sof_intel_hda_stream { (SOF_HDA_ADSP_SD_ENTRY_SIZE * ((s)->index) \ + SOF_HDA_ADSP_LOADER_BASE) +#define SOF_STREAM_SD_OFFSET_CRST 0x1 + /* * DSP Core services. */ -- cgit v1.2.3 From 4794601a52d40a425542be1b88f8f5614fcf45b4 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 25 Nov 2021 12:15:13 +0200 Subject: ASoC: SOF: Intel: hda: reset stream before coupling host and link DMA's The recommended programming sequence for HD-Audio DMA is to reset the stream before coupling the link and host DMA's. Signed-off-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211125101520.291581-4-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-stream.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index c2895bdd3f07..440827ce390d 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -659,6 +659,11 @@ int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev, hstream); struct hdac_bus *bus = sof_to_bus(sdev); u32 mask = 0x1 << stream->index; + int ret; + + ret = hda_dsp_stream_reset(sdev, stream); + if (ret < 0) + return ret; spin_lock_irq(&bus->reg_lock); /* couple host and link DMA if link DMA channel is idle */ -- cgit v1.2.3 From 0dd71a3340b92b503278af4565156f086ccbca3f Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 25 Nov 2021 12:15:14 +0200 Subject: ASoC: SOF: pcm: invoke platform hw_free for STOP/SUSPEND triggers snd_sof_pcm_platform_hw_params() will be called when the stream is restarted with a prepare ioctl. This happens in two cases i.e. when a suspended stream is resumed or when a stream is restarted without intermediate call to sof_pcm_hw_free(). Make sure to call snd_sof_pcm_platform_hw_free() in both these cases to keep it balanced. Signed-off-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211125101520.291581-5-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/pcm.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 1bf7e60be772..1d0d90551e8f 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -470,6 +470,10 @@ static int sof_pcm_trigger(struct snd_soc_component *component, if (ret < 0) return ret; + ret = snd_sof_pcm_platform_hw_free(sdev, substream); + if (ret < 0) + return ret; + /* free widget list only for SUSPEND trigger */ if (free_widget_list) ret = sof_widget_list_free(sdev, spcm, substream->stream); -- cgit v1.2.3 From 47934e0fcbbe2bf488bcae2d68431b9ea5972488 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 25 Nov 2021 12:15:15 +0200 Subject: ASoC: SOF: call platform hw_free for paused streams during suspend Paused streams must be stopped and platform hw_free should be invoked during system suspend so they can be restarted properly after system resume. Signed-off-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211125101520.291581-6-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-audio.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index e00ce275052f..d81071b39825 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -751,10 +751,17 @@ static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev) continue; if (spcm->stream[dir].list) { + /* Free PCM in the DSP */ ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); if (ret < 0) return ret; + /* stop DMA */ + ret = snd_sof_pcm_platform_hw_free(sdev, substream); + if (ret < 0) + return ret; + + /* free the DAPM widget list */ ret = sof_widget_list_free(sdev, spcm, dir); if (ret < 0) { dev_err(sdev->dev, "failed to free widgets during suspend\n"); -- cgit v1.2.3 From d9a7246534753efa383ad8d05ab3691df846c4b4 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 25 Nov 2021 12:15:16 +0200 Subject: ASoC: SOF: Add a helper for freeing PCM stream Add a helper function to free PCM in the FW, stop the DMA and free the widget list. These actions are performed both during PCM trigger STOP and when a paused stream is freed during system suspend. Signed-off-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211125101520.291581-7-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/pcm.c | 11 ++--------- sound/soc/sof/sof-audio.c | 40 ++++++++++++++++++++++++++-------------- sound/soc/sof/sof-audio.h | 2 ++ 3 files changed, 30 insertions(+), 23 deletions(-) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 1d0d90551e8f..3aa708b1ac26 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -466,17 +466,10 @@ static int sof_pcm_trigger(struct snd_soc_component *component, /* free PCM if reset_hw_params is set and the STOP IPC is successful */ if (!ret && reset_hw_params) { - ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); + ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, + free_widget_list); if (ret < 0) return ret; - - ret = snd_sof_pcm_platform_hw_free(sdev, substream); - if (ret < 0) - return ret; - - /* free widget list only for SUSPEND trigger */ - if (free_widget_list) - ret = sof_widget_list_free(sdev, spcm, substream->stream); } return ret; diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index d81071b39825..a275f7b7c812 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -728,6 +728,31 @@ int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify) return 0; } +int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, + struct snd_sof_pcm *spcm, int dir, bool free_widget_list) +{ + int ret; + + /* Send PCM_FREE IPC to reset pipeline */ + ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); + if (ret < 0) + return ret; + + /* stop the DMA */ + ret = snd_sof_pcm_platform_hw_free(sdev, substream); + if (ret < 0) + return 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"); + } + + return ret; +} + /* * Free the PCM, its associated widgets and set the prepared flag to false for all PCMs that * did not get suspended(ex: paused streams) so the widgets can be set up again during resume. @@ -751,22 +776,9 @@ static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev) continue; if (spcm->stream[dir].list) { - /* Free PCM in the DSP */ - ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); - if (ret < 0) - return ret; - - /* stop DMA */ - ret = snd_sof_pcm_platform_hw_free(sdev, substream); + ret = sof_pcm_stream_free(sdev, substream, spcm, dir, true); if (ret < 0) return ret; - - /* free the DAPM widget list */ - ret = sof_widget_list_free(sdev, spcm, dir); - if (ret < 0) { - dev_err(sdev->dev, "failed to free widgets during suspend\n"); - return ret; - } } } diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 1c4f59d34717..e419e7082c28 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -267,4 +267,6 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, in int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir); int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm); +int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, + struct snd_sof_pcm *spcm, int dir, bool free_widget_list); #endif -- cgit v1.2.3 From 85d7acd0ef18725b1d3a7980eee8b84d46296b91 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 25 Nov 2021 12:15:17 +0200 Subject: ASoC: SOF: pcm: move the check for prepared flag Move the check for the prepared flag inside snd_pcm_dsp_pcm_free() to avoid having to check it before every invocation of the function. Signed-off-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211125101520.291581-8-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/pcm.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 3aa708b1ac26..c61cd3cc4f02 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -107,6 +107,9 @@ int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev struct sof_ipc_reply reply; int ret; + if (!spcm->prepared[substream->stream]) + return 0; + stream.hdr.size = sizeof(stream); stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_FREE; stream.comp_id = spcm->stream[substream->stream].comp_id; @@ -178,11 +181,9 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, * Handle repeated calls to hw_params() without free_pcm() in * between. At least ALSA OSS emulation depends on this. */ - if (spcm->prepared[substream->stream]) { - ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); - if (ret < 0) - return ret; - } + ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); + if (ret < 0) + return ret; dev_dbg(component->dev, "pcm: hw params stream %d dir %d\n", spcm->pcm.pcm_id, substream->stream); @@ -298,11 +299,9 @@ 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]) { - ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); - if (ret < 0) - err = ret; - } + ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); + if (ret < 0) + err = ret; ret = sof_widget_list_free(sdev, spcm, substream->stream); if (ret < 0) -- cgit v1.2.3 From 0b639dcd457b1d3fc660e5a77b02cf65acde3b5a Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 25 Nov 2021 12:15:18 +0200 Subject: ASoC: SOF: align the hw_free sequence with stop Even though the order of stopping the DMA and freeing the widget list is not important, align the sequence to match with the stop trigger to avoid confusion. Signed-off-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211125101520.291581-9-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/pcm.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index c61cd3cc4f02..e4446defe51e 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -299,22 +299,26 @@ 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); + /* free PCM in the DSP */ ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); if (ret < 0) err = ret; - ret = sof_widget_list_free(sdev, spcm, substream->stream); - if (ret < 0) - err = ret; - - cancel_work_sync(&spcm->stream[substream->stream].period_elapsed_work); + /* stop 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; + + cancel_work_sync(&spcm->stream[substream->stream].period_elapsed_work); + return err; } -- cgit v1.2.3 From a0f84dfb3f6d9f78f862cbe885036d3e4449fc6f Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 25 Nov 2021 12:15:19 +0200 Subject: ASoC: SOF: IPC: dai: Expand DAI_CONFIG IPC flags Some DAI components, such as HDaudio, need to be stopped in two steps a) stop the DAI component b) stop the DAI DMA This patch enables this two-step stop by expanding the DAI_CONFIG IPC flags and split them into 2 parts. The 4 LSB bits indicate when the DAI_CONFIG IPC is sent, ex: hw_params, hw_free or pause. The 4 MSB bits are used as the quirk flags to be used along with the command flags. The quirk flag called SOF_DAI_CONFIG_FLAGS_2_STEP_STOP shall be set along with the HW_PARAMS command flag, i.e. before the pipeline is started so that the stop/pause trigger op in the FW can take the appropriate action to either perform/skip the DMA stop. If set, the DMA stop will be executed when the DAI_CONFIG IPC is sent during hw_free. In the case of pause, DMA pause will be handled when the DAI_CONFIG IPC is sent with the PAUSE command flag. Along with this, modify the signature for the hda_ctrl_dai_widget_setup/ hda_ctrl_dai_widget_free() functions to take additional flags as an argument and modify all users to pass the appropriate quirk flags. Only the HDA DAI's need to pass the SOF_DAI_CONFIG_FLAGS_2_STEP_STOP quirk flag during hw_params to indicate that it supports two-step stop and pause. Signed-off-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211125101520.291581-10-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- include/sound/sof/dai.h | 25 +++++++++++++++++++------ sound/soc/sof/intel/hda-dai.c | 8 ++++---- sound/soc/sof/intel/hda.c | 26 ++++++++++++++++++-------- sound/soc/sof/intel/hda.h | 4 ++-- sound/soc/sof/sof-audio.c | 2 +- 5 files changed, 44 insertions(+), 21 deletions(-) diff --git a/include/sound/sof/dai.h b/include/sound/sof/dai.h index 5132bc60f54b..59ee50ac7705 100644 --- a/include/sound/sof/dai.h +++ b/include/sound/sof/dai.h @@ -52,12 +52,25 @@ #define SOF_DAI_FMT_INV_MASK 0x0f00 #define SOF_DAI_FMT_CLOCK_PROVIDER_MASK 0xf000 -/* DAI_CONFIG flags */ -#define SOF_DAI_CONFIG_FLAGS_MASK 0x3 -#define SOF_DAI_CONFIG_FLAGS_NONE (0 << 0) /**< DAI_CONFIG sent without stage information */ -#define SOF_DAI_CONFIG_FLAGS_HW_PARAMS (1 << 0) /**< DAI_CONFIG sent during hw_params stage */ -#define SOF_DAI_CONFIG_FLAGS_HW_FREE (2 << 0) /**< DAI_CONFIG sent during hw_free stage */ -#define SOF_DAI_CONFIG_FLAGS_RFU (3 << 0) /**< not used, reserved for future use */ +/* + * DAI_CONFIG flags. The 4 LSB bits are used for the commands, HW_PARAMS, HW_FREE and PAUSE + * representing when the IPC is sent. The 4 MSB bits are used to add quirks along with the above + * commands. + */ +#define SOF_DAI_CONFIG_FLAGS_CMD_MASK 0xF +#define SOF_DAI_CONFIG_FLAGS_NONE 0 /**< DAI_CONFIG sent without stage information */ +#define SOF_DAI_CONFIG_FLAGS_HW_PARAMS BIT(0) /**< DAI_CONFIG sent during hw_params stage */ +#define SOF_DAI_CONFIG_FLAGS_HW_FREE BIT(1) /**< DAI_CONFIG sent during hw_free stage */ +/**< DAI_CONFIG sent during pause trigger. Only available ABI 3.20 onwards */ +#define SOF_DAI_CONFIG_FLAGS_PAUSE BIT(2) +#define SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT 4 +#define SOF_DAI_CONFIG_FLAGS_QUIRK_MASK (0xF << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT) +/* + * This should be used along with the SOF_DAI_CONFIG_FLAGS_HW_PARAMS to indicate that pipeline + * stop/pause and DAI DMA stop/pause should happen in two steps. This change is only available + * ABI 3.20 onwards. + */ +#define SOF_DAI_CONFIG_FLAGS_2_STEP_STOP BIT(0) /** \brief Types of DAI */ enum sof_ipc_dai_type { diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 748e8ed61475..bce5366cf913 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -197,9 +197,9 @@ static int hda_link_dai_widget_update(struct sof_intel_hda_stream *hda_stream, /* set up/free DAI widget and send DAI_CONFIG IPC */ if (widget_setup) - return hda_ctrl_dai_widget_setup(w); + return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_2_STEP_STOP); - return hda_ctrl_dai_widget_free(w); + return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE); } static int hda_link_hw_params(struct snd_pcm_substream *substream, @@ -452,9 +452,9 @@ static int ssp_dai_setup_or_free(struct snd_pcm_substream *substream, struct snd return 0; if (setup) - return hda_ctrl_dai_widget_setup(w); + return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE); - return hda_ctrl_dai_widget_free(w); + return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE); } static int ssp_dai_startup(struct snd_pcm_substream *substream, diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 1e1e9659ea86..cfe026dbf124 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -41,7 +41,7 @@ #define EXCEPT_MAX_HDR_SIZE 0x400 #define HDA_EXT_ROM_STATUS_SIZE 8 -int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w) +int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags) { struct snd_sof_widget *swidget = w->dobj.private; struct snd_soc_component *component = swidget->scomp; @@ -58,6 +58,13 @@ int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w) return -EINVAL; } + /* DAI already configured, reset it before reconfiguring it */ + if (sof_dai->configured) { + ret = hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE); + if (ret < 0) + return ret; + } + config = &sof_dai->dai_config[sof_dai->current_config]; /* @@ -71,8 +78,10 @@ int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w) return ret; } - /* set HW_PARAMS flag */ - config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_MASK, SOF_DAI_CONFIG_FLAGS_HW_PARAMS); + /* set HW_PARAMS flag along with quirks */ + config->flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS | + quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT; + /* send DAI_CONFIG IPC */ ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size, @@ -87,7 +96,7 @@ int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w) return 0; } -int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w) +int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags) { struct snd_sof_widget *swidget = w->dobj.private; struct snd_soc_component *component = swidget->scomp; @@ -110,8 +119,9 @@ int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w) config = &sof_dai->dai_config[sof_dai->current_config]; - /* set HW_FREE flag */ - config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_MASK, SOF_DAI_CONFIG_FLAGS_HW_FREE); + /* set HW_FREE flag along with any quirks */ + config->flags = SOF_DAI_CONFIG_FLAGS_HW_FREE | + quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT; ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size, &reply, sizeof(reply)); @@ -166,9 +176,9 @@ static int sdw_dai_config_ipc(struct snd_sof_dev *sdev, config->alh.stream_id = alh_stream_id; if (setup) - return hda_ctrl_dai_widget_setup(w); + return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE); - return hda_ctrl_dai_widget_free(w); + return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE); } static int sdw_params_stream(struct device *dev, diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 60139ea9b8de..72e78c449aa8 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -739,7 +739,7 @@ int hda_pci_intel_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) struct snd_sof_dai; struct sof_ipc_dai_config; -int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w); -int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w); +int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags); +int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags); #endif diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index a275f7b7c812..58a62bfb16ab 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -57,7 +57,7 @@ static int sof_dai_config_setup(struct snd_sof_dev *sdev, struct snd_sof_dai *da } /* set NONE flag to clear all previous settings */ - config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_MASK, SOF_DAI_CONFIG_FLAGS_NONE); + config->flags = SOF_DAI_CONFIG_FLAGS_NONE; ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size, &reply, sizeof(reply)); -- cgit v1.2.3 From 69acac569031426e2ab9b5244593b60d0c9abd04 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 25 Nov 2021 12:15:20 +0200 Subject: ASoC: SOF: Intel: hda: send DAI_CONFIG IPC during pause For HDA DAI's the DMA must be paused after the RUN bit is cleared by the host. So, send the DAI_CONFIG IPC with just the SOF_DAI_CONFIG_FLAGS_PAUSE flag set to indicate this to the firmware. Signed-off-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211125101520.291581-11-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-dai.c | 42 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index bce5366cf913..8c1d7ddb00e2 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -287,6 +287,36 @@ static int hda_link_pcm_prepare(struct snd_pcm_substream *substream, dai); } +static int hda_link_dai_config_pause_push_ipc(struct snd_soc_dapm_widget *w) +{ + struct snd_sof_widget *swidget = w->dobj.private; + struct snd_soc_component *component = swidget->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct sof_ipc_dai_config *config; + struct snd_sof_dai *sof_dai; + struct sof_ipc_reply reply; + int ret; + + sof_dai = swidget->private; + + if (!sof_dai || !sof_dai->dai_config) { + dev_err(sdev->dev, "No config for DAI %s\n", w->name); + return -EINVAL; + } + + config = &sof_dai->dai_config[sof_dai->current_config]; + + /* set PAUSE command flag */ + config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_CMD_MASK, SOF_DAI_CONFIG_FLAGS_PAUSE); + + ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size, + &reply, sizeof(reply)); + if (ret < 0) + dev_err(sdev->dev, "DAI config for %s failed during pause push\n", w->name); + + return ret; +} + static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { @@ -312,6 +342,9 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, hda_stream = hstream_to_sof_hda_stream(link_dev); dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd); + + w = snd_soc_dai_get_widget(dai, substream->stream); + switch (cmd) { case SNDRV_PCM_TRIGGER_RESUME: /* set up hw_params */ @@ -331,11 +364,6 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_STOP: snd_hdac_ext_link_stream_clear(link_dev); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - w = dai->playback_widget; - else - w = dai->capture_widget; - /* * free DAI widget during stop/suspend to keep widget use_count's balanced. */ @@ -352,6 +380,10 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: snd_hdac_ext_link_stream_clear(link_dev); + + ret = hda_link_dai_config_pause_push_ipc(w); + if (ret < 0) + return ret; break; default: return -EINVAL; -- cgit v1.2.3 From f670b274f7f6f4b2722d7f08d0fddf606a727e92 Mon Sep 17 00:00:00 2001 From: Ye Guojin Date: Wed, 10 Nov 2021 00:29:10 +0000 Subject: ASoC: imx-hdmi: add put_device() after of_find_device_by_node() This was found by coccicheck: ./sound/soc/fsl/imx-hdmi.c,209,1-7,ERROR missing put_device; call of_find_device_by_node on line 119, but without a corresponding object release within this function. Reported-by: Zeal Robot Signed-off-by: Ye Guojin Link: https://lore.kernel.org/r/20211110002910.134915-1-ye.guojin@zte.com.cn Signed-off-by: Mark Brown --- sound/soc/fsl/imx-hdmi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/fsl/imx-hdmi.c b/sound/soc/fsl/imx-hdmi.c index f10359a28800..929f69b758af 100644 --- a/sound/soc/fsl/imx-hdmi.c +++ b/sound/soc/fsl/imx-hdmi.c @@ -145,6 +145,8 @@ static int imx_hdmi_probe(struct platform_device *pdev) data->dai.capture_only = false; data->dai.init = imx_hdmi_init; + put_device(&cpu_pdev->dev); + if (of_node_name_eq(cpu_np, "sai")) { data->cpu_priv.sysclk_id[1] = FSL_SAI_CLK_MAST1; data->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1; -- cgit v1.2.3 From 0b189395945dc59d327c1e0588d144ce439dfa55 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Thu, 25 Nov 2021 23:25:43 +0000 Subject: ASoC: codecs/jz4770: Add missing gain control after DAC/ADC mixer The capture and playback paths both have a configurable gain after their respective mixer, which can be set from -31 dB to 0 dB in 32 steps. Signed-off-by: Paul Cercueil Link: https://lore.kernel.org/r/20211125232543.117074-1-paul@crapouillou.net Signed-off-by: Mark Brown --- sound/soc/codecs/jz4770.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sound/soc/codecs/jz4770.c b/sound/soc/codecs/jz4770.c index 6b60120f59a6..1d0c467ab57b 100644 --- a/sound/soc/codecs/jz4770.c +++ b/sound/soc/codecs/jz4770.c @@ -307,6 +307,7 @@ static const DECLARE_TLV_DB_MINMAX_MUTE(dac_tlv, -3100, 0); static const DECLARE_TLV_DB_SCALE(adc_tlv, 0, 100, 0); static const DECLARE_TLV_DB_MINMAX(out_tlv, -2500, 600); static const DECLARE_TLV_DB_SCALE(linein_tlv, -2500, 100, 0); +static const DECLARE_TLV_DB_MINMAX(mixer_tlv, -3100, 0); /* Unconditional controls. */ static const struct snd_kcontrol_new jz4770_codec_snd_controls[] = { @@ -319,6 +320,14 @@ static const struct snd_kcontrol_new jz4770_codec_snd_controls[] = { SOC_DOUBLE_R_TLV("Line In Bypass Playback Volume", JZ4770_CODEC_REG_GCR_LIBYL, JZ4770_CODEC_REG_GCR_LIBYR, REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, linein_tlv), + + SOC_SINGLE_TLV("Mixer Capture Volume", + JZ4770_CODEC_REG_GCR_MIXADC, + REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, mixer_tlv), + + SOC_SINGLE_TLV("Mixer Playback Volume", + JZ4770_CODEC_REG_GCR_MIXDAC, + REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, mixer_tlv), }; static const struct snd_kcontrol_new jz4770_codec_pcm_playback_controls[] = { -- cgit v1.2.3 From a5e0091d62abb9599d9dea505ec0e8c820001831 Mon Sep 17 00:00:00 2001 From: Lucas Tanure Date: Thu, 25 Nov 2021 14:35:01 +0000 Subject: ASoC: cs35l41: Fix link problem Can't link I2C and SPI to the same binary, better to move CS35L41 to 3 modules approach. And instead of exposing cs35l41_reg, volatile_reg, readable_reg and precious_reg arrays, move cs35l41_regmap_i2c/spi to new module and expose it. Signed-off-by: Lucas Tanure Link: https://lore.kernel.org/r/20211125143501.7720-1-tanureal@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 5 +++++ sound/soc/codecs/Makefile | 6 ++++-- sound/soc/codecs/cs35l41-i2c.c | 15 -------------- sound/soc/codecs/cs35l41-spi.c | 16 --------------- sound/soc/codecs/cs35l41-tables.c | 41 +++++++++++++++++++++++++++++++++++---- sound/soc/codecs/cs35l41.c | 2 ++ sound/soc/codecs/cs35l41.h | 7 ++----- 7 files changed, 50 insertions(+), 42 deletions(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index b6d1827e7986..b4f70e27342c 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -612,14 +612,19 @@ config SND_SOC_CS35L36 tristate "Cirrus Logic CS35L36 CODEC" depends on I2C +config SND_SOC_CS35L41 + tristate + config SND_SOC_CS35L41_SPI tristate "Cirrus Logic CS35L41 CODEC (SPI)" depends on SPI_MASTER + select SND_SOC_CS35L41 select REGMAP_SPI config SND_SOC_CS35L41_I2C tristate "Cirrus Logic CS35L41 CODEC (I2C)" depends on I2C + select SND_SOC_CS35L41 select REGMAP_I2C config SND_SOC_CS42L42 diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 9acfbcbfc46d..485eee75502b 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -54,8 +54,9 @@ snd-soc-cs35l33-objs := cs35l33.o snd-soc-cs35l34-objs := cs35l34.o snd-soc-cs35l35-objs := cs35l35.o snd-soc-cs35l36-objs := cs35l36.o -snd-soc-cs35l41-spi-objs := cs35l41-spi.o cs35l41.o cs35l41-tables.o -snd-soc-cs35l41-i2c-objs := cs35l41-i2c.o cs35l41.o cs35l41-tables.o +snd-soc-cs35l41-objs := cs35l41.o cs35l41-tables.o +snd-soc-cs35l41-spi-objs := cs35l41-spi.o +snd-soc-cs35l41-i2c-objs := cs35l41-i2c.o snd-soc-cs42l42-objs := cs42l42.o snd-soc-cs42l51-objs := cs42l51.o snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o @@ -391,6 +392,7 @@ obj-$(CONFIG_SND_SOC_CS35L33) += snd-soc-cs35l33.o obj-$(CONFIG_SND_SOC_CS35L34) += snd-soc-cs35l34.o obj-$(CONFIG_SND_SOC_CS35L35) += snd-soc-cs35l35.o obj-$(CONFIG_SND_SOC_CS35L36) += snd-soc-cs35l36.o +obj-$(CONFIG_SND_SOC_CS35L41) += snd-soc-cs35l41.o obj-$(CONFIG_SND_SOC_CS35L41_SPI) += snd-soc-cs35l41-spi.o obj-$(CONFIG_SND_SOC_CS35L41_I2C) += snd-soc-cs35l41-i2c.o obj-$(CONFIG_SND_SOC_CS42L42) += snd-soc-cs42l42.o diff --git a/sound/soc/codecs/cs35l41-i2c.c b/sound/soc/codecs/cs35l41-i2c.c index d5fa8d2c4a70..c9b604af6b71 100644 --- a/sound/soc/codecs/cs35l41-i2c.c +++ b/sound/soc/codecs/cs35l41-i2c.c @@ -20,21 +20,6 @@ #include #include "cs35l41.h" -static struct regmap_config cs35l41_regmap_i2c = { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = CS35L41_REGSTRIDE, - .reg_format_endian = REGMAP_ENDIAN_BIG, - .val_format_endian = REGMAP_ENDIAN_BIG, - .max_register = CS35L41_LASTREG, - .reg_defaults = cs35l41_reg, - .num_reg_defaults = ARRAY_SIZE(cs35l41_reg), - .volatile_reg = cs35l41_volatile_reg, - .readable_reg = cs35l41_readable_reg, - .precious_reg = cs35l41_precious_reg, - .cache_type = REGCACHE_RBTREE, -}; - static const struct i2c_device_id cs35l41_id_i2c[] = { { "cs35l40", 0 }, { "cs35l41", 0 }, diff --git a/sound/soc/codecs/cs35l41-spi.c b/sound/soc/codecs/cs35l41-spi.c index 90a921f726c3..5d6cf39abec4 100644 --- a/sound/soc/codecs/cs35l41-spi.c +++ b/sound/soc/codecs/cs35l41-spi.c @@ -18,22 +18,6 @@ #include #include "cs35l41.h" -static struct regmap_config cs35l41_regmap_spi = { - .reg_bits = 32, - .val_bits = 32, - .pad_bits = 16, - .reg_stride = CS35L41_REGSTRIDE, - .reg_format_endian = REGMAP_ENDIAN_BIG, - .val_format_endian = REGMAP_ENDIAN_BIG, - .max_register = CS35L41_LASTREG, - .reg_defaults = cs35l41_reg, - .num_reg_defaults = ARRAY_SIZE(cs35l41_reg), - .volatile_reg = cs35l41_volatile_reg, - .readable_reg = cs35l41_readable_reg, - .precious_reg = cs35l41_precious_reg, - .cache_type = REGCACHE_RBTREE, -}; - static const struct spi_device_id cs35l41_id_spi[] = { { "cs35l40", 0 }, { "cs35l41", 0 }, diff --git a/sound/soc/codecs/cs35l41-tables.c b/sound/soc/codecs/cs35l41-tables.c index 9d1a7d7dd24d..3eb18b17a7b0 100644 --- a/sound/soc/codecs/cs35l41-tables.c +++ b/sound/soc/codecs/cs35l41-tables.c @@ -8,7 +8,7 @@ #include "cs35l41.h" -const struct reg_default cs35l41_reg[CS35L41_MAX_CACHE_REG] = { +static const struct reg_default cs35l41_reg[] = { { CS35L41_PWR_CTRL1, 0x00000000 }, { CS35L41_PWR_CTRL3, 0x01000010 }, { CS35L41_GPIO_PAD_CONTROL, 0x00000000 }, @@ -47,7 +47,7 @@ const struct reg_default cs35l41_reg[CS35L41_MAX_CACHE_REG] = { { CS35L41_MIXER_NGATE_CH2_CFG, 0x00000303 }, }; -bool cs35l41_readable_reg(struct device *dev, unsigned int reg) +static bool cs35l41_readable_reg(struct device *dev, unsigned int reg) { switch (reg) { case CS35L41_DEVID: @@ -331,7 +331,7 @@ bool cs35l41_readable_reg(struct device *dev, unsigned int reg) } } -bool cs35l41_precious_reg(struct device *dev, unsigned int reg) +static bool cs35l41_precious_reg(struct device *dev, unsigned int reg) { switch (reg) { case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31: @@ -344,7 +344,7 @@ bool cs35l41_precious_reg(struct device *dev, unsigned int reg) } } -bool cs35l41_volatile_reg(struct device *dev, unsigned int reg) +static bool cs35l41_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { case CS35L41_DEVID: @@ -688,3 +688,36 @@ const struct cs35l41_otp_map_element_t cs35l41_otp_map_map[CS35L41_NUM_OTP_MAPS] .word_offset = 2, }, }; + +struct regmap_config cs35l41_regmap_i2c = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = CS35L41_REGSTRIDE, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_BIG, + .max_register = CS35L41_LASTREG, + .reg_defaults = cs35l41_reg, + .num_reg_defaults = ARRAY_SIZE(cs35l41_reg), + .volatile_reg = cs35l41_volatile_reg, + .readable_reg = cs35l41_readable_reg, + .precious_reg = cs35l41_precious_reg, + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL_GPL(cs35l41_regmap_i2c); + +struct regmap_config cs35l41_regmap_spi = { + .reg_bits = 32, + .val_bits = 32, + .pad_bits = 16, + .reg_stride = CS35L41_REGSTRIDE, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_BIG, + .max_register = CS35L41_LASTREG, + .reg_defaults = cs35l41_reg, + .num_reg_defaults = ARRAY_SIZE(cs35l41_reg), + .volatile_reg = cs35l41_volatile_reg, + .readable_reg = cs35l41_readable_reg, + .precious_reg = cs35l41_precious_reg, + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL_GPL(cs35l41_regmap_spi); diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index afb07d2991ba..e04924526883 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -1731,6 +1731,7 @@ err: return ret; } +EXPORT_SYMBOL_GPL(cs35l41_probe); void cs35l41_remove(struct cs35l41_private *cs35l41) { @@ -1739,6 +1740,7 @@ void cs35l41_remove(struct cs35l41_private *cs35l41) regulator_bulk_disable(CS35L41_NUM_SUPPLIES, cs35l41->supplies); gpiod_set_value_cansleep(cs35l41->reset_gpio, 0); } +EXPORT_SYMBOL_GPL(cs35l41_remove); MODULE_DESCRIPTION("ASoC CS35L41 driver"); MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, "); diff --git a/sound/soc/codecs/cs35l41.h b/sound/soc/codecs/cs35l41.h index eea3b14acb0b..f82075ea855f 100644 --- a/sound/soc/codecs/cs35l41.h +++ b/sound/soc/codecs/cs35l41.h @@ -538,7 +538,6 @@ #define CS35L41_OTP_TRIM_35 0x0000400C #define CS35L41_OTP_TRIM_36 0x00002030 -#define CS35L41_MAX_CACHE_REG 36 #define CS35L41_OTP_SIZE_WORDS 32 #define CS35L41_NUM_OTP_ELEM 100 #define CS35L41_NUM_OTP_MAPS 5 @@ -734,9 +733,8 @@ #define CS35L41_RX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) #define CS35L41_TX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) -bool cs35l41_readable_reg(struct device *dev, unsigned int reg); -bool cs35l41_precious_reg(struct device *dev, unsigned int reg); -bool cs35l41_volatile_reg(struct device *dev, unsigned int reg); +extern struct regmap_config cs35l41_regmap_i2c; +extern struct regmap_config cs35l41_regmap_spi; struct cs35l41_otp_packed_element_t { u32 reg; @@ -752,7 +750,6 @@ struct cs35l41_otp_map_element_t { u32 word_offset; }; -extern const struct reg_default cs35l41_reg[CS35L41_MAX_CACHE_REG]; extern const struct cs35l41_otp_map_element_t cs35l41_otp_map_map[CS35L41_NUM_OTP_MAPS]; -- cgit v1.2.3 From 4dcddadf5530a0da00e6b2eb8194297b49d33506 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 23 Nov 2021 11:30:13 +0100 Subject: ASoC: SOF: mediatek: Use %pR/%pa to print resources/physical addresses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On 32-bit with CONFIG_ARCH_DMA_ADDR_T_64BIT=n: sound/soc/sof/mediatek/mt8195/mt8195.c: In function ‘platform_parse_resource’: sound/soc/sof/mediatek/mt8195/mt8195.c:51:15: error: format ‘%llx’ expects argument of type ‘long long unsigned int’, but argument 4 has type ‘unsigned int’ [-Werror=format=] 51 | dev_dbg(dev, "DMA pbase=0x%llx, size=0x%llx\n", | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ sound/soc/sof/mediatek/mt8195/mt8195.c: In function ‘adsp_memory_remap_init’: sound/soc/sof/mediatek/mt8195/mt8195.c:167:15: error: format ‘%llx’ expects argument of type ‘long long unsigned int’, but argument 4 has type ‘phys_addr_t’ {aka ‘unsigned int’} [-Werror=format=] 167 | dev_dbg(dev, "adsp->pa_dram %llx, offset %#x\n", adsp->pa_dram, offset); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ sound/soc/sof/mediatek/mt8195/mt8195.c: In function ‘adsp_shared_base_ioremap’: sound/soc/sof/mediatek/mt8195/mt8195.c:196:15: error: format ‘%llx’ expects argument of type ‘long long unsigned int’, but argument 5 has type ‘phys_addr_t’ {aka ‘unsigned int’} [-Werror=format=] 196 | dev_dbg(dev, "shared-dram vbase=%p, phy addr :%llx, size=%#x\n", | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Fix the first cases by printing the full resource using %pR. Fix the other cases by printing the physical addresses using %pa. Reported-by: noreply@ellerman.id.au Fixes: 32d7e03d26fd9318 ("ASoC: SOF: mediatek: Add mt8195 hardware support") Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20211123103013.73645-1-geert@linux-m68k.org Signed-off-by: Mark Brown --- sound/soc/sof/mediatek/mt8195/mt8195.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.c b/sound/soc/sof/mediatek/mt8195/mt8195.c index 55d9812870a4..c719ba470620 100644 --- a/sound/soc/sof/mediatek/mt8195/mt8195.c +++ b/sound/soc/sof/mediatek/mt8195/mt8195.c @@ -49,8 +49,7 @@ static int platform_parse_resource(struct platform_device *pdev, void *data) return ret; } - dev_dbg(dev, "DMA pbase=0x%llx, size=0x%llx\n", - (phys_addr_t)res.start, resource_size(&res)); + dev_dbg(dev, "DMA %pR\n", &res); ret = of_reserved_mem_device_init(dev); if (ret) { @@ -166,7 +165,7 @@ static int adsp_memory_remap_init(struct device *dev, struct mtk_adsp_chip_info offset = adsp->pa_dram - DRAM_PHYS_BASE_FROM_DSP_VIEW; adsp->dram_offset = offset; offset >>= DRAM_REMAP_SHIFT; - dev_dbg(dev, "adsp->pa_dram %llx, offset %#x\n", adsp->pa_dram, offset); + dev_dbg(dev, "adsp->pa_dram %pa, offset %#x\n", &adsp->pa_dram, offset); writel(offset, vaddr_emi_map); if (offset != readl(vaddr_emi_map)) { dev_err(dev, "write emi map fail : %#x\n", readl(vaddr_emi_map)); @@ -195,8 +194,8 @@ static int adsp_shared_base_ioremap(struct platform_device *pdev, void *data) return -ENOMEM; } } - dev_dbg(dev, "shared-dram vbase=%p, phy addr :%llx, size=%#x\n", - adsp->shared_dram, adsp->pa_shared_dram, shared_size); + dev_dbg(dev, "shared-dram vbase=%p, phy addr :%pa, size=%#x\n", + adsp->shared_dram, &adsp->pa_shared_dram, shared_size); return 0; } -- cgit v1.2.3 From 8544f08c816292c2219f28c6eaa69236b978bfb9 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 16 Nov 2021 16:45:12 +0900 Subject: ASoC: soc-dai: update snd_soc_dai_delay() to snd_soc_pcm_dai_delay() Current soc_pcm_pointer() is manually calculating both CPU-DAI's max delay (= A) and Codec-DAI's max delay (= B). static snd_pcm_uframes_t soc_pcm_pointer(...) { ... ^ for_each_rtd_cpu_dais(rtd, i, cpu_dai) (A) cpu_delay = max(cpu_delay, ...); v delay += cpu_delay; ^ for_each_rtd_codec_dais(rtd, i, codec_dai) (B) codec_delay = max(codec_delay, ...); v delay += codec_delay; runtime->delay = delay; ... } Current soc_pcm_pointer() and the total delay calculating is not readable / difficult to understand. This patch update snd_soc_dai_delay() to snd_soc_pcm_dai_delay(), and calcule both CPU/Codec delay in one function. Link: https://lore.kernel.org/r/87fszl4yrq.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/875yssy25z.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-dai.h | 4 ++-- sound/soc/soc-dai.c | 40 ++++++++++++++++++++++++++++------------ sound/soc/soc-pcm.c | 18 ++---------------- 3 files changed, 32 insertions(+), 30 deletions(-) diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 0dcb361a98bb..5d4dd7c5450b 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -208,8 +208,6 @@ int snd_soc_dai_startup(struct snd_soc_dai *dai, struct snd_pcm_substream *substream); void snd_soc_dai_shutdown(struct snd_soc_dai *dai, struct snd_pcm_substream *substream, int rollback); -snd_pcm_sframes_t snd_soc_dai_delay(struct snd_soc_dai *dai, - struct snd_pcm_substream *substream); void snd_soc_dai_suspend(struct snd_soc_dai *dai); void snd_soc_dai_resume(struct snd_soc_dai *dai); int snd_soc_dai_compress_new(struct snd_soc_dai *dai, @@ -238,6 +236,8 @@ int snd_soc_pcm_dai_trigger(struct snd_pcm_substream *substream, int cmd, int rollback); int snd_soc_pcm_dai_bespoke_trigger(struct snd_pcm_substream *substream, int cmd); +void snd_soc_pcm_dai_delay(struct snd_pcm_substream *substream, + snd_pcm_sframes_t *cpu_delay, snd_pcm_sframes_t *codec_delay); int snd_soc_dai_compr_startup(struct snd_soc_dai *dai, struct snd_compr_stream *cstream); diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c index 3db0fcf24385..6078afe335f8 100644 --- a/sound/soc/soc-dai.c +++ b/sound/soc/soc-dai.c @@ -453,18 +453,6 @@ void snd_soc_dai_shutdown(struct snd_soc_dai *dai, soc_dai_mark_pop(dai, substream, startup); } -snd_pcm_sframes_t snd_soc_dai_delay(struct snd_soc_dai *dai, - struct snd_pcm_substream *substream) -{ - int delay = 0; - - if (dai->driver->ops && - dai->driver->ops->delay) - delay = dai->driver->ops->delay(substream, dai); - - return delay; -} - int snd_soc_dai_compress_new(struct snd_soc_dai *dai, struct snd_soc_pcm_runtime *rtd, int num) { @@ -693,6 +681,34 @@ int snd_soc_pcm_dai_bespoke_trigger(struct snd_pcm_substream *substream, return 0; } +void snd_soc_pcm_dai_delay(struct snd_pcm_substream *substream, + snd_pcm_sframes_t *cpu_delay, + snd_pcm_sframes_t *codec_delay) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *dai; + int i; + + /* + * We're looking for the delay through the full audio path so it needs to + * be the maximum of the DAIs doing transmit and the maximum of the DAIs + * doing receive (ie, all CPUs and all CODECs) rather than just the maximum + * of all DAIs. + */ + + /* for CPU */ + for_each_rtd_cpu_dais(rtd, i, dai) + if (dai->driver->ops && + dai->driver->ops->delay) + *cpu_delay = max(*cpu_delay, dai->driver->ops->delay(substream, dai)); + + /* for Codec */ + for_each_rtd_codec_dais(rtd, i, dai) + if (dai->driver->ops && + dai->driver->ops->delay) + *codec_delay = max(*codec_delay, dai->driver->ops->delay(substream, dai)); +} + int snd_soc_dai_compr_startup(struct snd_soc_dai *dai, struct snd_compr_stream *cstream) { diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 4d41ad302802..82fd170e16af 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1084,15 +1084,11 @@ start_err: */ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_soc_dai *cpu_dai; - struct snd_soc_dai *codec_dai; struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t offset = 0; snd_pcm_sframes_t delay = 0; snd_pcm_sframes_t codec_delay = 0; snd_pcm_sframes_t cpu_delay = 0; - int i; /* clearing the previous total delay */ runtime->delay = 0; @@ -1102,19 +1098,9 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) /* base delay if assigned in pointer callback */ delay = runtime->delay; - for_each_rtd_cpu_dais(rtd, i, cpu_dai) { - cpu_delay = max(cpu_delay, - snd_soc_dai_delay(cpu_dai, substream)); - } - delay += cpu_delay; - - for_each_rtd_codec_dais(rtd, i, codec_dai) { - codec_delay = max(codec_delay, - snd_soc_dai_delay(codec_dai, substream)); - } - delay += codec_delay; + snd_soc_pcm_dai_delay(substream, &cpu_delay, &codec_delay); - runtime->delay = delay; + runtime->delay = delay + cpu_delay + codec_delay; return offset; } -- cgit v1.2.3 From 403f830e7a0be5a9e33c7a9d208574f79887ec57 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 16 Nov 2021 16:45:18 +0900 Subject: ASoC: soc-component: add snd_soc_pcm_component_delay() Current soc-pcm.c :: soc_pcm_pointer() is assuming that component driver might update runtime->delay silently in snd_soc_pcm_component_pointer() (= A). static snd_pcm_uframes_t soc_pcm_pointer(...) { ... /* clearing the previous total delay */ => runtime->delay = 0; (A) offset = snd_soc_pcm_component_pointer(substream); /* base delay if assigned in pointer callback */ => delay = runtime->delay; ... } 1) The behavior that ".pointer callback secretly updates runtime->delay" is strange and confusable. 2) Current snd_soc_pcm_component_pointer() uses 1st found component's .pointer callback only, thus it is no problem for now. But runtime->delay might be overwrote if it adjusted to multiple components in the future. 3) Component delay is updated at .pointer callback timing (secretly). But some components which doesn't have .pointer callback might want to increase runtime->delay for some reasons. We already have .delay function for DAI, but not have for Component. This patch adds new snd_soc_pcm_component_delay() for it. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/874k8cy25t.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-component.h | 4 ++++ sound/soc/soc-component.c | 28 ++++++++++++++++++++++++++++ sound/soc/soc-pcm.c | 2 ++ 3 files changed, 34 insertions(+) diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h index a4317144ab62..a52080407b98 100644 --- a/include/sound/soc-component.h +++ b/include/sound/soc-component.h @@ -148,6 +148,8 @@ struct snd_soc_component_driver { struct vm_area_struct *vma); int (*ack)(struct snd_soc_component *component, struct snd_pcm_substream *substream); + snd_pcm_sframes_t (*delay)(struct snd_soc_component *component, + struct snd_pcm_substream *substream); const struct snd_compress_ops *compress_ops; @@ -505,5 +507,7 @@ int snd_soc_pcm_component_pm_runtime_get(struct snd_soc_pcm_runtime *rtd, void snd_soc_pcm_component_pm_runtime_put(struct snd_soc_pcm_runtime *rtd, void *stream, int rollback); int snd_soc_pcm_component_ack(struct snd_pcm_substream *substream); +void snd_soc_pcm_component_delay(struct snd_pcm_substream *substream, + snd_pcm_sframes_t *cpu_delay, snd_pcm_sframes_t *codec_delay); #endif /* __SOC_COMPONENT_H */ diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index c76ff9c59dfb..c0664f94990c 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -932,6 +932,34 @@ int snd_soc_pcm_component_pointer(struct snd_pcm_substream *substream) return 0; } +void snd_soc_pcm_component_delay(struct snd_pcm_substream *substream, + snd_pcm_sframes_t *cpu_delay, + snd_pcm_sframes_t *codec_delay) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_component *component; + snd_pcm_sframes_t delay; + int i; + + /* + * We're looking for the delay through the full audio path so it needs to + * be the maximum of the Components doing transmit and the maximum of the + * Components doing receive (ie, all CPUs and all CODECs) rather than + * just the maximum of all Components. + */ + for_each_rtd_components(rtd, i, component) { + if (!component->driver->delay) + continue; + + delay = component->driver->delay(component, substream); + + if (snd_soc_component_is_codec(component)) + *codec_delay = max(*codec_delay, delay); + else + *cpu_delay = max(*cpu_delay, delay); + } +} + int snd_soc_pcm_component_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg) { diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 82fd170e16af..493d231a2ffd 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1098,7 +1098,9 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) /* base delay if assigned in pointer callback */ delay = runtime->delay; + /* should be called *after* snd_soc_pcm_component_pointer() */ snd_soc_pcm_dai_delay(substream, &cpu_delay, &codec_delay); + snd_soc_pcm_component_delay(substream, &cpu_delay, &codec_delay); runtime->delay = delay + cpu_delay + codec_delay; -- cgit v1.2.3 From feea640aaf1a5ae9dff6e33931e680542432e8dd Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 16 Nov 2021 16:45:23 +0900 Subject: ASoC: amd: acp-pcm-dma: add .delay support Now ALSA SoC supports .delay for component. This patch uses it, and not update runtime->delay on .pointer directly / secretly. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/8735nwy25o.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/amd/acp-pcm-dma.c | 15 ++++++++++++++- sound/soc/amd/acp.h | 1 + 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c index 1f322accd9ea..8fa2e2fde4f1 100644 --- a/sound/soc/amd/acp-pcm-dma.c +++ b/sound/soc/amd/acp-pcm-dma.c @@ -1003,6 +1003,7 @@ static snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component, struct snd_pcm_runtime *runtime = substream->runtime; struct audio_substream_data *rtd = runtime->private_data; + struct audio_drv_data *adata = dev_get_drvdata(component->dev); if (!rtd) return -EINVAL; @@ -1023,7 +1024,7 @@ static snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component, } if (bytescount > 0) { delay = do_div(bytescount, period_bytes); - runtime->delay = bytes_to_frames(runtime, delay); + adata->delay += bytes_to_frames(runtime, delay); } } else { buffersize = frames_to_bytes(runtime, runtime->buffer_size); @@ -1035,6 +1036,17 @@ static snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component, return bytes_to_frames(runtime, pos); } +static snd_pcm_sframes_t acp_dma_delay(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct audio_drv_data *adata = dev_get_drvdata(component->dev); + snd_pcm_sframes_t delay = adata->delay; + + adata->delay = 0; + + return delay; +} + static int acp_dma_prepare(struct snd_soc_component *component, struct snd_pcm_substream *substream) { @@ -1198,6 +1210,7 @@ static const struct snd_soc_component_driver acp_asoc_platform = { .hw_params = acp_dma_hw_params, .trigger = acp_dma_trigger, .pointer = acp_dma_pointer, + .delay = acp_dma_delay, .prepare = acp_dma_prepare, .pcm_construct = acp_dma_new, }; diff --git a/sound/soc/amd/acp.h b/sound/soc/amd/acp.h index 85529ed7e5f5..db80a73aa593 100644 --- a/sound/soc/amd/acp.h +++ b/sound/soc/amd/acp.h @@ -151,6 +151,7 @@ struct audio_drv_data { struct snd_pcm_substream *capture_i2sbt_stream; void __iomem *acp_mmio; u32 asic_type; + snd_pcm_sframes_t delay; }; /* -- cgit v1.2.3 From 796b64a72db0b416f0aa1815e87aa28388b4715d Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 16 Nov 2021 16:45:29 +0900 Subject: ASoC: intel: sst-mfld-platform-pcm: add .delay support Now ALSA SoC supports .delay for component. This patch uses it, and not update runtime->delay on .pointer directly / secretly. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/871r3gy25j.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst-mfld-platform-pcm.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index 5db2f4865bbb..a56dd48c045f 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -653,10 +653,21 @@ static snd_pcm_uframes_t sst_soc_pointer(struct snd_soc_component *component, dev_err(rtd->dev, "sst: error code = %d\n", ret_val); return ret_val; } - substream->runtime->delay = str_info->pcm_delay; return str_info->buffer_ptr; } +static snd_pcm_sframes_t sst_soc_delay(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct sst_runtime_stream *stream = substream->runtime->private_data; + struct pcm_stream_info *str_info = &stream->stream_info; + + if (sst_get_stream_status(stream) == SST_PLATFORM_INIT) + return 0; + + return str_info->pcm_delay; +} + static int sst_soc_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd) { @@ -695,6 +706,7 @@ static const struct snd_soc_component_driver sst_soc_platform_drv = { .open = sst_soc_open, .trigger = sst_soc_trigger, .pointer = sst_soc_pointer, + .delay = sst_soc_delay, .compress_ops = &sst_platform_compress_ops, .pcm_construct = sst_soc_pcm_new, }; -- cgit v1.2.3 From dd894f4caf7df77cf72dc6ae7547900b55d0de42 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 16 Nov 2021 16:45:34 +0900 Subject: ASoC: soc-pcm: tidyup soc_pcm_pointer()'s delay update method No driver directly updates runtime->delay in .pointer. This patch cleanups its method. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87zgq4wnkx.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/soc-pcm.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 493d231a2ffd..3b4412183344 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1080,29 +1080,22 @@ start_err: /* * soc level wrapper for pointer callback * If cpu_dai, codec_dai, component driver has the delay callback, then - * the runtime->delay will be updated accordingly. + * the runtime->delay will be updated via snd_soc_pcm_component/dai_delay(). */ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t offset = 0; - snd_pcm_sframes_t delay = 0; snd_pcm_sframes_t codec_delay = 0; snd_pcm_sframes_t cpu_delay = 0; - /* clearing the previous total delay */ - runtime->delay = 0; - offset = snd_soc_pcm_component_pointer(substream); - /* base delay if assigned in pointer callback */ - delay = runtime->delay; - /* should be called *after* snd_soc_pcm_component_pointer() */ snd_soc_pcm_dai_delay(substream, &cpu_delay, &codec_delay); snd_soc_pcm_component_delay(substream, &cpu_delay, &codec_delay); - runtime->delay = delay + cpu_delay + codec_delay; + runtime->delay = cpu_delay + codec_delay; return offset; } -- cgit v1.2.3 From fd03cf7f5b4726028cfc2ef76e42d0d5c66377aa Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 17 Nov 2021 21:36:45 -0600 Subject: ASoC: sun8i-codec: Add AIF, ADC, and DAC volume controls This allows changing the volume of each digital input/output independently, and provides the only "master volume" for the DAC. (The ADC also has a gain control on the analog side.) While the hardware supports digital gain up to +72dB, the controls here are limited to +24dB maximum, as any gain above that level makes volume sliders difficult to use, and is extremely likely to cause clipping. Signed-off-by: Samuel Holland Link: https://lore.kernel.org/r/20211118033645.43524-1-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun8i-codec.c | 56 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index 518bfb724a5b..0bea2162f68d 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c @@ -21,6 +21,7 @@ #include #include #include +#include #define SUN8I_SYSCLK_CTL 0x00c #define SUN8I_SYSCLK_CTL_AIF1CLK_ENA 11 @@ -72,6 +73,12 @@ #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACR 10 #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR 9 #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL 8 +#define SUN8I_AIF1_VOL_CTRL1 0x050 +#define SUN8I_AIF1_VOL_CTRL1_AD0L_VOL 8 +#define SUN8I_AIF1_VOL_CTRL1_AD0R_VOL 0 +#define SUN8I_AIF1_VOL_CTRL3 0x058 +#define SUN8I_AIF1_VOL_CTRL3_DA0L_VOL 8 +#define SUN8I_AIF1_VOL_CTRL3_DA0R_VOL 0 #define SUN8I_AIF2_ADCDAT_CTRL 0x084 #define SUN8I_AIF2_ADCDAT_CTRL_AIF2_ADCL_ENA 15 #define SUN8I_AIF2_ADCDAT_CTRL_AIF2_ADCR_ENA 14 @@ -91,6 +98,12 @@ #define SUN8I_AIF2_MXR_SRC_ADCR_MXR_SRC_AIF1DA1R 10 #define SUN8I_AIF2_MXR_SRC_ADCR_MXR_SRC_AIF2DACL 9 #define SUN8I_AIF2_MXR_SRC_ADCR_MXR_SRC_ADCR 8 +#define SUN8I_AIF2_VOL_CTRL1 0x090 +#define SUN8I_AIF2_VOL_CTRL1_ADCL_VOL 8 +#define SUN8I_AIF2_VOL_CTRL1_ADCR_VOL 0 +#define SUN8I_AIF2_VOL_CTRL2 0x098 +#define SUN8I_AIF2_VOL_CTRL2_DACL_VOL 8 +#define SUN8I_AIF2_VOL_CTRL2_DACR_VOL 0 #define SUN8I_AIF3_CLK_CTRL_AIF3_CLK_SRC_AIF1 (0x0 << 0) #define SUN8I_AIF3_CLK_CTRL_AIF3_CLK_SRC_AIF2 (0x1 << 0) #define SUN8I_AIF3_CLK_CTRL_AIF3_CLK_SRC_AIF1CLK (0x2 << 0) @@ -102,8 +115,14 @@ #define SUN8I_ADC_DIG_CTRL_ENAD 15 #define SUN8I_ADC_DIG_CTRL_ADOUT_DTS 2 #define SUN8I_ADC_DIG_CTRL_ADOUT_DLY 1 +#define SUN8I_ADC_VOL_CTRL 0x104 +#define SUN8I_ADC_VOL_CTRL_ADCL_VOL 8 +#define SUN8I_ADC_VOL_CTRL_ADCR_VOL 0 #define SUN8I_DAC_DIG_CTRL 0x120 #define SUN8I_DAC_DIG_CTRL_ENDA 15 +#define SUN8I_DAC_VOL_CTRL 0x124 +#define SUN8I_DAC_VOL_CTRL_DACL_VOL 8 +#define SUN8I_DAC_VOL_CTRL_DACR_VOL 0 #define SUN8I_DAC_MXR_SRC 0x130 #define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA0L 15 #define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA1L 14 @@ -696,6 +715,41 @@ static struct snd_soc_dai_driver sun8i_codec_dais[] = { }, }; +static const DECLARE_TLV_DB_SCALE(sun8i_codec_vol_scale, -12000, 75, 1); + +static const struct snd_kcontrol_new sun8i_codec_controls[] = { + SOC_DOUBLE_TLV("AIF1 AD0 Capture Volume", + SUN8I_AIF1_VOL_CTRL1, + SUN8I_AIF1_VOL_CTRL1_AD0L_VOL, + SUN8I_AIF1_VOL_CTRL1_AD0R_VOL, + 0xc0, 0, sun8i_codec_vol_scale), + SOC_DOUBLE_TLV("AIF1 DA0 Playback Volume", + SUN8I_AIF1_VOL_CTRL3, + SUN8I_AIF1_VOL_CTRL3_DA0L_VOL, + SUN8I_AIF1_VOL_CTRL3_DA0R_VOL, + 0xc0, 0, sun8i_codec_vol_scale), + SOC_DOUBLE_TLV("AIF2 ADC Capture Volume", + SUN8I_AIF2_VOL_CTRL1, + SUN8I_AIF2_VOL_CTRL1_ADCL_VOL, + SUN8I_AIF2_VOL_CTRL1_ADCR_VOL, + 0xc0, 0, sun8i_codec_vol_scale), + SOC_DOUBLE_TLV("AIF2 DAC Playback Volume", + SUN8I_AIF2_VOL_CTRL2, + SUN8I_AIF2_VOL_CTRL2_DACL_VOL, + SUN8I_AIF2_VOL_CTRL2_DACR_VOL, + 0xc0, 0, sun8i_codec_vol_scale), + SOC_DOUBLE_TLV("ADC Capture Volume", + SUN8I_ADC_VOL_CTRL, + SUN8I_ADC_VOL_CTRL_ADCL_VOL, + SUN8I_ADC_VOL_CTRL_ADCR_VOL, + 0xc0, 0, sun8i_codec_vol_scale), + SOC_DOUBLE_TLV("DAC Playback Volume", + SUN8I_DAC_VOL_CTRL, + SUN8I_DAC_VOL_CTRL_DACL_VOL, + SUN8I_DAC_VOL_CTRL_DACR_VOL, + 0xc0, 0, sun8i_codec_vol_scale), +}; + static int sun8i_codec_aif_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -1215,6 +1269,8 @@ static int sun8i_codec_component_probe(struct snd_soc_component *component) } static const struct snd_soc_component_driver sun8i_soc_component = { + .controls = sun8i_codec_controls, + .num_controls = ARRAY_SIZE(sun8i_codec_controls), .dapm_widgets = sun8i_codec_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sun8i_codec_dapm_widgets), .dapm_routes = sun8i_codec_dapm_routes, -- cgit v1.2.3 From 425c5fce8a03c9da70a4c763cd7db22fbb422dcf Mon Sep 17 00:00:00 2001 From: lvzhaoxiong Date: Tue, 23 Nov 2021 10:43:29 +0800 Subject: ASoC: qcom: Add support for ALC5682I-VS codec Qcom machine driver adds rt5682s support in this patch. Card name can be specified from dts by model property, and driver makes use of the name to distinguish which headset codec is on the board. Signed-off-by: lvzhaoxiong Link: https://lore.kernel.org/r/20211123024329.21998-1-lvzhaoxiong@huaqin.corp-partner.google.com Signed-off-by: Mark Brown --- sound/soc/qcom/Kconfig | 1 + sound/soc/qcom/sc7180.c | 24 +++++++++++++++++++----- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index b2173847dc47..cf3e151bb635 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -169,6 +169,7 @@ config SND_SOC_SC7180 select SND_SOC_LPASS_SC7180 select SND_SOC_MAX98357A select SND_SOC_RT5682_I2C + select SND_SOC_RT5682S select SND_SOC_ADAU7002 help To add support for audio on Qualcomm Technologies Inc. diff --git a/sound/soc/qcom/sc7180.c b/sound/soc/qcom/sc7180.c index 768566bb57a5..2fff764a00a7 100644 --- a/sound/soc/qcom/sc7180.c +++ b/sound/soc/qcom/sc7180.c @@ -17,6 +17,7 @@ #include #include "../codecs/rt5682.h" +#include "../codecs/rt5682s.h" #include "common.h" #include "lpass.h" @@ -128,7 +129,21 @@ static int sc7180_snd_startup(struct snd_pcm_substream *substream) struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); - int ret; + int pll_id, pll_source, pll_in, pll_out, clk_id, ret; + + if (!(strcmp(card->name, "sc7180-rt5682-max98357a-1mic"))) { + pll_source = RT5682_PLL1_S_MCLK; + pll_id = 0; + clk_id = RT5682_SCLK_S_PLL1; + pll_out = RT5682_PLL1_FREQ; + pll_in = DEFAULT_MCLK_RATE; + } else if (!(strcmp(card->name, "sc7180-rt5682s-max98357a-1mic"))) { + pll_source = RT5682S_PLL_S_MCLK; + pll_id = RT5682S_PLL2; + clk_id = RT5682S_SCLK_S_PLL2; + pll_out = RT5682_PLL1_FREQ; + pll_in = DEFAULT_MCLK_RATE; + } switch (cpu_dai->id) { case MI2S_PRIMARY: @@ -145,16 +160,15 @@ static int sc7180_snd_startup(struct snd_pcm_substream *substream) SND_SOC_DAIFMT_I2S); /* Configure PLL1 for codec */ - ret = snd_soc_dai_set_pll(codec_dai, 0, RT5682_PLL1_S_MCLK, - DEFAULT_MCLK_RATE, RT5682_PLL1_FREQ); + ret = snd_soc_dai_set_pll(codec_dai, pll_id, pll_source, + pll_in, pll_out); if (ret) { dev_err(rtd->dev, "can't set codec pll: %d\n", ret); return ret; } /* Configure sysclk for codec */ - ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1, - RT5682_PLL1_FREQ, + ret = snd_soc_dai_set_sysclk(codec_dai, clk_id, pll_out, SND_SOC_CLOCK_IN); if (ret) dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", -- cgit v1.2.3 From 679de7b64f9622eff8f74357fc3ee071629d25b3 Mon Sep 17 00:00:00 2001 From: Jernej Skrabec Date: Wed, 17 Nov 2021 20:44:58 +0100 Subject: ASoC: sunxi: sun4i-spdif: Implement IEC958 control SPDIF core is capable of sending custom status. Implement IEC958 control handling. Signed-off-by: Jernej Skrabec Link: https://lore.kernel.org/r/20211117194458.2249643-1-jernej.skrabec@gmail.com Signed-off-by: Mark Brown --- sound/soc/sunxi/sun4i-spdif.c | 115 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c index a10949bf0ca1..17090f43150e 100644 --- a/sound/soc/sunxi/sun4i-spdif.c +++ b/sound/soc/sunxi/sun4i-spdif.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include #include @@ -186,6 +188,7 @@ struct sun4i_spdif_dev { struct regmap *regmap; struct snd_dmaengine_dai_dma_data dma_params_tx; const struct sun4i_spdif_quirks *quirks; + spinlock_t lock; }; static void sun4i_spdif_configure(struct sun4i_spdif_dev *host) @@ -385,11 +388,122 @@ static int sun4i_spdif_trigger(struct snd_pcm_substream *substream, int cmd, return ret; } +static int sun4i_spdif_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + + return 0; +} + +static int sun4i_spdif_get_status_mask(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 *status = ucontrol->value.iec958.status; + + status[0] = 0xff; + status[1] = 0xff; + status[2] = 0xff; + status[3] = 0xff; + status[4] = 0xff; + status[5] = 0x03; + + return 0; +} + +static int sun4i_spdif_get_status(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(cpu_dai); + u8 *status = ucontrol->value.iec958.status; + unsigned long flags; + unsigned int reg; + + spin_lock_irqsave(&host->lock, flags); + + regmap_read(host->regmap, SUN4I_SPDIF_TXCHSTA0, ®); + + status[0] = reg & 0xff; + status[1] = (reg >> 8) & 0xff; + status[2] = (reg >> 16) & 0xff; + status[3] = (reg >> 24) & 0xff; + + regmap_read(host->regmap, SUN4I_SPDIF_TXCHSTA1, ®); + + status[4] = reg & 0xff; + status[5] = (reg >> 8) & 0x3; + + spin_unlock_irqrestore(&host->lock, flags); + + return 0; +} + +static int sun4i_spdif_set_status(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(cpu_dai); + u8 *status = ucontrol->value.iec958.status; + unsigned long flags; + unsigned int reg; + bool chg0, chg1; + + spin_lock_irqsave(&host->lock, flags); + + reg = (u32)status[3] << 24; + reg |= (u32)status[2] << 16; + reg |= (u32)status[1] << 8; + reg |= (u32)status[0]; + + regmap_update_bits_check(host->regmap, SUN4I_SPDIF_TXCHSTA0, + GENMASK(31,0), reg, &chg0); + + reg = (u32)status[5] << 8; + reg |= (u32)status[4]; + + regmap_update_bits_check(host->regmap, SUN4I_SPDIF_TXCHSTA1, + GENMASK(9,0), reg, &chg1); + + reg = SUN4I_SPDIF_TXCFG_CHSTMODE; + if (status[0] & IEC958_AES0_NONAUDIO) + reg |= SUN4I_SPDIF_TXCFG_NONAUDIO; + + regmap_update_bits(host->regmap, SUN4I_SPDIF_TXCFG, + SUN4I_SPDIF_TXCFG_CHSTMODE | + SUN4I_SPDIF_TXCFG_NONAUDIO, reg); + + spin_unlock_irqrestore(&host->lock, flags); + + return chg0 || chg1; +} + +static struct snd_kcontrol_new sun4i_spdif_controls[] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK), + .info = sun4i_spdif_info, + .get = sun4i_spdif_get_status_mask + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), + .info = sun4i_spdif_info, + .get = sun4i_spdif_get_status, + .put = sun4i_spdif_set_status + } +}; + static int sun4i_spdif_soc_dai_probe(struct snd_soc_dai *dai) { struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(dai); snd_soc_dai_init_dma_data(dai, &host->dma_params_tx, NULL); + snd_soc_add_dai_controls(dai, sun4i_spdif_controls, + ARRAY_SIZE(sun4i_spdif_controls)); + return 0; } @@ -512,6 +626,7 @@ static int sun4i_spdif_probe(struct platform_device *pdev) return -ENOMEM; host->pdev = pdev; + spin_lock_init(&host->lock); /* Initialize this copy of the CPU DAI driver structure */ memcpy(&host->cpu_dai_drv, &sun4i_spdif_dai, sizeof(sun4i_spdif_dai)); -- cgit v1.2.3 From 6dd21ad81bf96478db3403b1bbe251c0612d0431 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 24 Nov 2021 23:40:01 +0100 Subject: ALSA: hda: Make proper use of timecounter HDA uses a timecounter to read a hardware clock running at 24 MHz. The conversion factor is set with a mult value of 125 and a shift value of 0, which is not converting the hardware clock to nanoseconds, it is converting to 1/3 nanoseconds because the conversion factor from 24Mhz to nanoseconds is 125/3. The usage sites divide the "nanoseconds" value returned by timecounter_read() by 3 to get a real nanoseconds value. There is a lengthy comment in azx_timecounter_init() explaining this choice. That comment makes blatantly wrong assumptions about how timecounters work and what can overflow. The comment says: * Applying the 1/3 factor as part of the multiplication * requires at least 20 bits for a decent precision, however * overflows occur after about 4 hours or less, not a option. timecounters operate on time deltas between two readouts of a clock and use the mult/shift pair to calculate a precise nanoseconds value: delta_nsec = (delta_clock * mult) >> shift; The fractional part is also taken into account and preserved to prevent accumulated rounding errors. For details see cyclecounter_cyc2ns(). The mult/shift pair has to be chosen so that the multiplication of the maximum expected delta value does not result in a 64bit overflow. As the counter wraps around on 32bit, the maximum observable delta between two reads is (1 << 32) - 1 which is about 178.9 seconds. That in turn means the maximum multiplication factor which fits into an u32 will not cause a 64bit overflow ever because it's guaranteed that: ((1 << 32) - 1) ^ 2 < (1 << 64) The resulting correct multiplication factor is 2796202667 and the shift value is 26, i.e. 26 bit precision. The overflow of the multiplication would happen exactly at a clock readout delta of 6597069765 which is way after the wrap around of the hardware clock at around 274.8 seconds which is off from the claimed 4 hours by more than an order of magnitude. If the counter ever wraps around the last read value then the calculation is off by the number of wrap arounds times 178.9 seconds because the overflow cannot be observed. Use clocks_calc_mult_shift(), which calculates the most accurate mult/shift pair based on the given clock frequency, and remove the bogus comment along with the divisions at the readout sites. Fixes: 5d890f591d15 ("ALSA: hda: support for wallclock timestamps") Signed-off-by: Thomas Gleixner Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/871r35kwji.ffs@tglx Signed-off-by: Takashi Iwai --- sound/hda/hdac_stream.c | 14 ++++---------- sound/pci/hda/hda_controller.c | 1 - sound/soc/intel/skylake/skl-pcm.c | 1 - 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index 9867555883c3..aa7955fdf68a 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -534,17 +534,11 @@ static void azx_timecounter_init(struct hdac_stream *azx_dev, cc->mask = CLOCKSOURCE_MASK(32); /* - * Converting from 24 MHz to ns means applying a 125/3 factor. - * To avoid any saturation issues in intermediate operations, - * the 125 factor is applied first. The division is applied - * last after reading the timecounter value. - * Applying the 1/3 factor as part of the multiplication - * requires at least 20 bits for a decent precision, however - * overflows occur after about 4 hours or less, not a option. + * Calculate the optimal mult/shift values. The counter wraps + * around after ~178.9 seconds. */ - - cc->mult = 125; /* saturation after 195 years */ - cc->shift = 0; + clocks_calc_mult_shift(&cc->mult, &cc->shift, 24000000, + NSEC_PER_SEC, 178); nsec = 0; /* audio time is elapsed time since trigger */ timecounter_init(tc, cc, nsec); diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 930ae4002a81..75dcb14ff20a 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -504,7 +504,6 @@ static int azx_get_time_info(struct snd_pcm_substream *substream, snd_pcm_gettime(substream->runtime, system_ts); nsec = timecounter_read(&azx_dev->core.tc); - nsec = div_u64(nsec, 3); /* can be optimized */ if (audio_tstamp_config->report_delay) nsec = azx_adjust_codec_delay(substream, nsec); diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 9ecaf6a1e847..e4aa366d356e 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1251,7 +1251,6 @@ static int skl_platform_soc_get_time_info( snd_pcm_gettime(substream->runtime, system_ts); nsec = timecounter_read(&hstr->tc); - nsec = div_u64(nsec, 3); /* can be optimized */ if (audio_tstamp_config->report_delay) nsec = skl_adjust_codec_delay(substream, nsec); -- cgit v1.2.3 From 8752d9a82fd065ef60c9a0e0e8ec820327509382 Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Sat, 27 Nov 2021 10:31:47 +0100 Subject: ASoC: mediatek: mt8195: Constify static snd_soc_ops These are only assigned to the ops field in the snd_soc_dai_link which is a pointer to const struct snd_soc_ops. Make them const to allow the compiler to put them in read-only memory. Signed-off-by: Rikard Falkeborn Link: https://lore.kernel.org/r/20211127093147.17368-1-rikard.falkeborn@gmail.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c | 2 +- sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c index e103102d7ef6..9e6b54e19c23 100644 --- a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c +++ b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c @@ -421,7 +421,7 @@ static int mt8195_dptx_hw_params(struct snd_pcm_substream *substream, SND_SOC_CLOCK_OUT); } -static struct snd_soc_ops mt8195_dptx_ops = { +static const struct snd_soc_ops mt8195_dptx_ops = { .hw_params = mt8195_dptx_hw_params, }; diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c index 95abaadcd842..e22e5fd40984 100644 --- a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c +++ b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c @@ -391,7 +391,7 @@ static int mt8195_dptx_hw_params(struct snd_pcm_substream *substream, SND_SOC_CLOCK_OUT); } -static struct snd_soc_ops mt8195_dptx_ops = { +static const struct snd_soc_ops mt8195_dptx_ops = { .hw_params = mt8195_dptx_hw_params, }; -- cgit v1.2.3 From 11918cdcffb127b6b35fe5c438e2ca8aa78249d0 Mon Sep 17 00:00:00 2001 From: Chris Down Date: Sun, 28 Nov 2021 14:31:46 +0000 Subject: ASoC: Intel: hda_dsp_common: don't multiline PCM topology warning On my T14s Gen2 I saw the following: [ 16.057258] skl_hda_dsp_generic skl_hda_dsp_generic: hda_dsp_hdmi_build_controls: no PCM in topology for HDMI converter 3 [ 16.057261] skl_hda_dsp_generic skl_hda_dsp_generic: hda_dsp_hdmi_build_controls: no PCM in topology for HDMI converter 4 [ 16.057263] skl_hda_dsp_generic skl_hda_dsp_generic: hda_dsp_hdmi_build_controls: no PCM in topology for HDMI converter 5 [...and so on.] It looks like the double newline is a mistake, so remove one. Signed-off-by: Chris Down Acked-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/YaOS0sBueAfApwOx@chrisdown.name Signed-off-by: Mark Brown --- sound/soc/intel/boards/hda_dsp_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/boards/hda_dsp_common.c b/sound/soc/intel/boards/hda_dsp_common.c index efdc4bc4bb1f..5c31ddc0884a 100644 --- a/sound/soc/intel/boards/hda_dsp_common.c +++ b/sound/soc/intel/boards/hda_dsp_common.c @@ -68,7 +68,7 @@ int hda_dsp_hdmi_build_controls(struct snd_soc_card *card, hpcm->pcm = NULL; hpcm->device = SNDRV_PCM_INVALID_DEVICE; dev_warn(card->dev, - "%s: no PCM in topology for HDMI converter %d\n\n", + "%s: no PCM in topology for HDMI converter %d\n", __func__, i); } i++; -- cgit v1.2.3 From 10b155fd413d31c89057986d0fc3d4ceef8e0e9f Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Sat, 27 Nov 2021 10:19:54 +0100 Subject: ASoC: intel: boards: bytcht*: Constify static snd_soc_ops These are only assigned to the ops fields in the snd_soc_dai_link struct which is a pointer to const struct snd_soc_ops. Make them const to allow the compiler to put them in read-only memory. Signed-off-by: Rikard Falkeborn Acked-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20211127091954.12075-1-rikard.falkeborn@gmail.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/bytcht_cx2072x.c | 2 +- sound/soc/intel/boards/bytcht_nocodec.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/boards/bytcht_cx2072x.c b/sound/soc/intel/boards/bytcht_cx2072x.c index 0a736308052a..ffd497a5b5a5 100644 --- a/sound/soc/intel/boards/bytcht_cx2072x.c +++ b/sound/soc/intel/boards/bytcht_cx2072x.c @@ -147,7 +147,7 @@ static int byt_cht_cx2072x_aif1_startup(struct snd_pcm_substream *substream) SNDRV_PCM_HW_PARAM_RATE, 48000); } -static struct snd_soc_ops byt_cht_cx2072x_aif1_ops = { +static const struct snd_soc_ops byt_cht_cx2072x_aif1_ops = { .startup = byt_cht_cx2072x_aif1_startup, }; diff --git a/sound/soc/intel/boards/bytcht_nocodec.c b/sound/soc/intel/boards/bytcht_nocodec.c index 67b3c4e97864..115c2bcaabd4 100644 --- a/sound/soc/intel/boards/bytcht_nocodec.c +++ b/sound/soc/intel/boards/bytcht_nocodec.c @@ -93,7 +93,7 @@ static int aif1_startup(struct snd_pcm_substream *substream) &constraints_48000); } -static struct snd_soc_ops aif1_ops = { +static const struct snd_soc_ops aif1_ops = { .startup = aif1_startup, }; -- cgit v1.2.3 From 043c0a6278ca443b1835726239dc2814c1313a9e Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 30 Nov 2021 10:28:42 +0000 Subject: firmware: cs_dsp: Move lockdep asserts to avoid potential null pointer Move the lockdep asserts until after the ctl pointer has been checked for NULL, to avoid potentially NULL pointer dereferences. Fixes: fb2f364fb5b9 ("firmware: cs_dsp: Add lockdep asserts to interface functions") Reported-by: kernel test robot Reported-by: Dan Carpenter Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20211130102842.26410-1-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/firmware/cirrus/cs_dsp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index 3814cbba0a54..5af8171d6ced 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -759,11 +759,11 @@ int cs_dsp_coeff_write_ctrl(struct cs_dsp_coeff_ctl *ctl, { int ret = 0; - lockdep_assert_held(&ctl->dsp->pwr_lock); - if (!ctl) return -ENOENT; + lockdep_assert_held(&ctl->dsp->pwr_lock); + if (len + off * sizeof(u32) > ctl->len) return -EINVAL; @@ -827,11 +827,11 @@ int cs_dsp_coeff_read_ctrl(struct cs_dsp_coeff_ctl *ctl, { int ret = 0; - lockdep_assert_held(&ctl->dsp->pwr_lock); - if (!ctl) return -ENOENT; + lockdep_assert_held(&ctl->dsp->pwr_lock); + if (len + off * sizeof(u32) > ctl->len) return -EINVAL; -- cgit v1.2.3 From 91745b034dca6044407b559fe28dd1cf7efccc29 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 29 Nov 2021 22:42:36 +0000 Subject: ASoC: mediatek: mt8195: make several arrays static const Don't populate various arrays on the stack but instead make them static const. Also makes the object code smaller by a few hundred bytes. Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20211129224236.506883-1-colin.i.king@gmail.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8195/mt8195-afe-clk.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/soc/mediatek/mt8195/mt8195-afe-clk.c b/sound/soc/mediatek/mt8195/mt8195-afe-clk.c index 8420b2c71332..c2543f4cffb7 100644 --- a/sound/soc/mediatek/mt8195/mt8195-afe-clk.c +++ b/sound/soc/mediatek/mt8195/mt8195-afe-clk.c @@ -326,7 +326,7 @@ int mt8195_afe_enable_reg_rw_clk(struct mtk_base_afe *afe) { struct mt8195_afe_private *afe_priv = afe->platform_priv; int i; - unsigned int clk_array[] = { + static const unsigned int clk_array[] = { MT8195_CLK_SCP_ADSP_AUDIODSP, /* bus clock for infra */ MT8195_CLK_TOP_AUDIO_H_SEL, /* clock for ADSP bus */ MT8195_CLK_TOP_AUDIO_LOCAL_BUS_SEL, /* bus clock for DRAM access */ @@ -347,7 +347,7 @@ int mt8195_afe_disable_reg_rw_clk(struct mtk_base_afe *afe) { struct mt8195_afe_private *afe_priv = afe->platform_priv; int i; - unsigned int clk_array[] = { + static const unsigned int clk_array[] = { MT8195_CLK_AUD_A1SYS, MT8195_CLK_AUD_A1SYS_HP, MT8195_CLK_AUD_AFE, @@ -380,11 +380,11 @@ static int mt8195_afe_enable_timing_sys(struct mtk_base_afe *afe) { struct mt8195_afe_private *afe_priv = afe->platform_priv; int i; - unsigned int clk_array[] = { + static const unsigned int clk_array[] = { MT8195_CLK_AUD_A1SYS, MT8195_CLK_AUD_A2SYS, }; - unsigned int cg_array[] = { + static const unsigned int cg_array[] = { MT8195_TOP_CG_A1SYS_TIMING, MT8195_TOP_CG_A2SYS_TIMING, MT8195_TOP_CG_26M_TIMING, @@ -403,11 +403,11 @@ static int mt8195_afe_disable_timing_sys(struct mtk_base_afe *afe) { struct mt8195_afe_private *afe_priv = afe->platform_priv; int i; - unsigned int clk_array[] = { + static const unsigned int clk_array[] = { MT8195_CLK_AUD_A2SYS, MT8195_CLK_AUD_A1SYS, }; - unsigned int cg_array[] = { + static const unsigned int cg_array[] = { MT8195_TOP_CG_26M_TIMING, MT8195_TOP_CG_A2SYS_TIMING, MT8195_TOP_CG_A1SYS_TIMING, -- cgit v1.2.3 From f316c9d9ba8ea08d6994bc5ba8fa276eab186208 Mon Sep 17 00:00:00 2001 From: Mac Chiang Date: Wed, 24 Nov 2021 22:04:53 -0500 Subject: ASoC: Intel: boards: add max98390 2/4 speakers support support 2 hw boards. 1. SSP2 connects max98390, 2 speakers. 2. SSP1 connects max98390, 2/4 speakers. 2 or 4 speakers playback add echo reference capture add bt offload support add DMI_OEM_STRING for board variants add ALC5682I-VS support Signed-off-by: Mark Hsieh Signed-off-by: Mac Chiang Signed-off-by: Kieth Tzeng Signed-off-by: Brent Lu Acked-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20211125030453.4382-1-mac.chiang@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/Kconfig | 1 + sound/soc/intel/boards/sof_maxim_common.c | 180 ++++++++++++++++++++++ sound/soc/intel/boards/sof_maxim_common.h | 16 ++ sound/soc/intel/boards/sof_rt5682.c | 72 +++++++++ sound/soc/intel/common/soc-acpi-intel-adl-match.c | 13 ++ sound/soc/sof/sof-pci-dev.c | 9 ++ 6 files changed, 291 insertions(+) diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 849445fcc05d..34ccefcc30c7 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -467,6 +467,7 @@ config SND_SOC_INTEL_SOF_RT5682_MACH (MFD_INTEL_LPSS || COMPILE_TEST)) ||\ (SND_SOC_SOF_BAYTRAIL && (X86_INTEL_LPSS || COMPILE_TEST)) select SND_SOC_MAX98373_I2C + select SND_SOC_MAX98390 select SND_SOC_RT1011 select SND_SOC_RT1015 select SND_SOC_RT1015P diff --git a/sound/soc/intel/boards/sof_maxim_common.c b/sound/soc/intel/boards/sof_maxim_common.c index e66dfe666915..9171d9cd179e 100644 --- a/sound/soc/intel/boards/sof_maxim_common.c +++ b/sound/soc/intel/boards/sof_maxim_common.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -133,6 +134,185 @@ void max_98373_set_codec_conf(struct snd_soc_card *card) } EXPORT_SYMBOL_NS(max_98373_set_codec_conf, SND_SOC_INTEL_SOF_MAXIM_COMMON); +/* + * Maxim MAX98390 + */ +const struct snd_soc_dapm_route max_98390_dapm_routes[] = { + /* speaker */ + { "Left Spk", NULL, "Left BE_OUT" }, + { "Right Spk", NULL, "Right BE_OUT" }, +}; + +static const struct snd_kcontrol_new max_98390_tt_kcontrols[] = { + SOC_DAPM_PIN_SWITCH("TL Spk"), + SOC_DAPM_PIN_SWITCH("TR Spk"), +}; + +static const struct snd_soc_dapm_widget max_98390_tt_dapm_widgets[] = { + SND_SOC_DAPM_SPK("TL Spk", NULL), + SND_SOC_DAPM_SPK("TR Spk", NULL), +}; + +const struct snd_soc_dapm_route max_98390_tt_dapm_routes[] = { + /* Tweeter speaker */ + { "TL Spk", NULL, "Tweeter Left BE_OUT" }, + { "TR Spk", NULL, "Tweeter Right BE_OUT" }, +}; + +static struct snd_soc_codec_conf max_98390_codec_conf[] = { + { + .dlc = COMP_CODEC_CONF(MAX_98390_DEV0_NAME), + .name_prefix = "Right", + }, + { + .dlc = COMP_CODEC_CONF(MAX_98390_DEV1_NAME), + .name_prefix = "Left", + }, +}; + +static struct snd_soc_codec_conf max_98390_4spk_codec_conf[] = { + { + .dlc = COMP_CODEC_CONF(MAX_98390_DEV0_NAME), + .name_prefix = "Right", + }, + { + .dlc = COMP_CODEC_CONF(MAX_98390_DEV1_NAME), + .name_prefix = "Left", + }, + { + .dlc = COMP_CODEC_CONF(MAX_98390_DEV2_NAME), + .name_prefix = "Tweeter Right", + }, + { + .dlc = COMP_CODEC_CONF(MAX_98390_DEV3_NAME), + .name_prefix = "Tweeter Left", + }, +}; + +struct snd_soc_dai_link_component max_98390_components[] = { + { + .name = MAX_98390_DEV0_NAME, + .dai_name = MAX_98390_CODEC_DAI, + }, + { + .name = MAX_98390_DEV1_NAME, + .dai_name = MAX_98390_CODEC_DAI, + }, +}; +EXPORT_SYMBOL_NS(max_98390_components, SND_SOC_INTEL_SOF_MAXIM_COMMON); + +struct snd_soc_dai_link_component max_98390_4spk_components[] = { + { + .name = MAX_98390_DEV0_NAME, + .dai_name = MAX_98390_CODEC_DAI, + }, + { + .name = MAX_98390_DEV1_NAME, + .dai_name = MAX_98390_CODEC_DAI, + }, + { + .name = MAX_98390_DEV2_NAME, + .dai_name = MAX_98390_CODEC_DAI, + }, + { + .name = MAX_98390_DEV3_NAME, + .dai_name = MAX_98390_CODEC_DAI, + }, +}; +EXPORT_SYMBOL_NS(max_98390_4spk_components, SND_SOC_INTEL_SOF_MAXIM_COMMON); + +static int max_98390_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai; + int i; + + for_each_rtd_codec_dais(rtd, i, codec_dai) { + if (i >= ARRAY_SIZE(max_98390_4spk_components)) { + dev_err(codec_dai->dev, "invalid codec index %d\n", i); + return -ENODEV; + } + + if (!strcmp(codec_dai->component->name, MAX_98390_DEV0_NAME)) { + /* DEV0 tdm slot configuration Right */ + snd_soc_dai_set_tdm_slot(codec_dai, 0x01, 3, 4, 32); + } + if (!strcmp(codec_dai->component->name, MAX_98390_DEV1_NAME)) { + /* DEV1 tdm slot configuration Left */ + snd_soc_dai_set_tdm_slot(codec_dai, 0x02, 3, 4, 32); + } + + if (!strcmp(codec_dai->component->name, MAX_98390_DEV2_NAME)) { + /* DEVi2 tdm slot configuration Tweeter Right */ + snd_soc_dai_set_tdm_slot(codec_dai, 0x04, 3, 4, 32); + } + if (!strcmp(codec_dai->component->name, MAX_98390_DEV3_NAME)) { + /* DEV3 tdm slot configuration Tweeter Left */ + snd_soc_dai_set_tdm_slot(codec_dai, 0x08, 3, 4, 32); + } + } + return 0; +} + +int max_98390_spk_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + int ret; + + /* add regular speakers dapm route */ + ret = snd_soc_dapm_add_routes(&card->dapm, max_98390_dapm_routes, + ARRAY_SIZE(max_98390_dapm_routes)); + if (ret) { + dev_err(rtd->dev, "unable to add Left/Right Speaker dapm, ret %d\n", ret); + return ret; + } + + /* add widgets/controls/dapm for tweeter speakers */ + if (acpi_dev_present("MX98390", "3", -1)) { + ret = snd_soc_dapm_new_controls(&card->dapm, max_98390_tt_dapm_widgets, + ARRAY_SIZE(max_98390_tt_dapm_widgets)); + + if (ret) { + dev_err(rtd->dev, "unable to add tweeter dapm controls, ret %d\n", ret); + /* Don't need to add routes if widget addition failed */ + return ret; + } + + ret = snd_soc_add_card_controls(card, max_98390_tt_kcontrols, + ARRAY_SIZE(max_98390_tt_kcontrols)); + if (ret) { + dev_err(rtd->dev, "unable to add tweeter card controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_add_routes(&card->dapm, max_98390_tt_dapm_routes, + ARRAY_SIZE(max_98390_tt_dapm_routes)); + if (ret) + dev_err(rtd->dev, + "unable to add Tweeter Left/Right Speaker dapm, ret %d\n", ret); + } + return ret; +} +EXPORT_SYMBOL_NS(max_98390_spk_codec_init, SND_SOC_INTEL_SOF_MAXIM_COMMON); + +const struct snd_soc_ops max_98390_ops = { + .hw_params = max_98390_hw_params, +}; +EXPORT_SYMBOL_NS(max_98390_ops, SND_SOC_INTEL_SOF_MAXIM_COMMON); + +void max_98390_set_codec_conf(struct snd_soc_card *card, int ch) +{ + if (ch == ARRAY_SIZE(max_98390_4spk_codec_conf)) { + card->codec_conf = max_98390_4spk_codec_conf; + card->num_configs = ARRAY_SIZE(max_98390_4spk_codec_conf); + } else { + card->codec_conf = max_98390_codec_conf; + card->num_configs = ARRAY_SIZE(max_98390_codec_conf); + } +} +EXPORT_SYMBOL_NS(max_98390_set_codec_conf, SND_SOC_INTEL_SOF_MAXIM_COMMON); + /* * Maxim MAX98357A/MAX98360A */ diff --git a/sound/soc/intel/boards/sof_maxim_common.h b/sound/soc/intel/boards/sof_maxim_common.h index 3ff5e8fec4de..7a8c53049e4d 100644 --- a/sound/soc/intel/boards/sof_maxim_common.h +++ b/sound/soc/intel/boards/sof_maxim_common.h @@ -24,6 +24,22 @@ int max_98373_spk_codec_init(struct snd_soc_pcm_runtime *rtd); void max_98373_set_codec_conf(struct snd_soc_card *card); int max_98373_trigger(struct snd_pcm_substream *substream, int cmd); +/* + * Maxim MAX98390 + */ +#define MAX_98390_CODEC_DAI "max98390-aif1" +#define MAX_98390_DEV0_NAME "i2c-MX98390:00" +#define MAX_98390_DEV1_NAME "i2c-MX98390:01" +#define MAX_98390_DEV2_NAME "i2c-MX98390:02" +#define MAX_98390_DEV3_NAME "i2c-MX98390:03" + +extern struct snd_soc_dai_link_component max_98390_components[2]; +extern struct snd_soc_dai_link_component max_98390_4spk_components[4]; +extern const struct snd_soc_ops max_98390_ops; + +void max_98390_set_codec_conf(struct snd_soc_card *card, int ch); +int max_98390_spk_codec_init(struct snd_soc_pcm_runtime *rtd); + /* * Maxim MAX98357A/MAX98360A */ diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index c41f386b4138..6cadb5fb72e0 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -59,6 +59,9 @@ (((quirk) << SOF_BT_OFFLOAD_SSP_SHIFT) & SOF_BT_OFFLOAD_SSP_MASK) #define SOF_SSP_BT_OFFLOAD_PRESENT BIT(22) #define SOF_RT5682S_HEADPHONE_CODEC_PRESENT BIT(23) +#define SOF_MAX98390_SPEAKER_AMP_PRESENT BIT(24) +#define SOF_MAX98390_TWEETER_SPEAKER_PRESENT BIT(25) + /* Default: MCLK on, MCLK 19.2M, SSP0 */ static unsigned long sof_rt5682_quirk = SOF_RT5682_MCLK_EN | @@ -179,6 +182,36 @@ static const struct dmi_system_id sof_rt5682_quirk_table[] = { SOF_RT5682_SSP_AMP(2) | SOF_RT5682_NUM_HDMIDEV(4)), }, + { + .callback = sof_rt5682_quirk_cb, + .matches = { + DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Brya"), + DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98390_ALC5682I_I2S"), + }, + .driver_data = (void *)(SOF_RT5682_MCLK_EN | + SOF_RT5682_SSP_CODEC(0) | + SOF_SPEAKER_AMP_PRESENT | + SOF_MAX98390_SPEAKER_AMP_PRESENT | + SOF_RT5682_SSP_AMP(2) | + SOF_RT5682_NUM_HDMIDEV(4)), + }, + { + .callback = sof_rt5682_quirk_cb, + .matches = { + DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Brya"), + DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98390_ALC5682I_I2S_4SPK"), + }, + .driver_data = (void *)(SOF_RT5682_MCLK_EN | + SOF_RT5682_SSP_CODEC(0) | + SOF_SPEAKER_AMP_PRESENT | + SOF_MAX98390_SPEAKER_AMP_PRESENT | + SOF_MAX98390_TWEETER_SPEAKER_PRESENT | + SOF_RT5682_SSP_AMP(1) | + SOF_RT5682_NUM_HDMIDEV(4) | + SOF_BT_OFFLOAD_SSP(2) | + SOF_SSP_BT_OFFLOAD_PRESENT), + + }, {} }; @@ -486,6 +519,7 @@ static int sof_card_late_probe(struct snd_soc_card *card) if (err < 0) return err; } + return hdac_hdmi_jack_port_init(component, &card->dapm); } @@ -784,6 +818,20 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, } else if (sof_rt5682_quirk & SOF_RT1011_SPEAKER_AMP_PRESENT) { sof_rt1011_dai_link(&links[id]); + } else if (sof_rt5682_quirk & + SOF_MAX98390_SPEAKER_AMP_PRESENT) { + if (sof_rt5682_quirk & + SOF_MAX98390_TWEETER_SPEAKER_PRESENT) { + links[id].codecs = max_98390_4spk_components; + links[id].num_codecs = ARRAY_SIZE(max_98390_4spk_components); + } else { + links[id].codecs = max_98390_components; + links[id].num_codecs = ARRAY_SIZE(max_98390_components); + } + links[id].init = max_98390_spk_codec_init; + links[id].ops = &max_98390_ops; + links[id].dpcm_capture = 1; + } else { max_98357a_dai_link(&links[id]); } @@ -868,6 +916,10 @@ static int sof_audio_probe(struct platform_device *pdev) if (acpi_dev_present("RTL5682", NULL, -1)) sof_rt5682_quirk |= SOF_RT5682S_HEADPHONE_CODEC_PRESENT; + /* Detect the headset codec variant to support machines in DMI quirk */ + if (acpi_dev_present("RTL5682", NULL, -1)) + sof_rt5682_quirk |= SOF_RT5682S_HEADPHONE_CODEC_PRESENT; + if (soc_intel_is_byt() || soc_intel_is_cht()) { is_legacy_cpu = 1; dmic_be_num = 0; @@ -924,6 +976,14 @@ static int sof_audio_probe(struct platform_device *pdev) sof_rt1011_codec_conf(&sof_audio_card_rt5682); else if (sof_rt5682_quirk & SOF_RT1015P_SPEAKER_AMP_PRESENT) sof_rt1015p_codec_conf(&sof_audio_card_rt5682); + else if (sof_rt5682_quirk & SOF_MAX98390_SPEAKER_AMP_PRESENT) { + if (sof_rt5682_quirk & SOF_MAX98390_TWEETER_SPEAKER_PRESENT) + max_98390_set_codec_conf(&sof_audio_card_rt5682, + ARRAY_SIZE(max_98390_4spk_components)); + else + max_98390_set_codec_conf(&sof_audio_card_rt5682, + ARRAY_SIZE(max_98390_components)); + } if (sof_rt5682_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) sof_audio_card_rt5682.num_links++; @@ -1050,6 +1110,17 @@ static const struct platform_device_id board_ids[] = { SOF_RT5682_SSP_AMP(2) | SOF_RT5682_NUM_HDMIDEV(4)), }, + { + .name = "adl_max98390_rt5682", + .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | + SOF_RT5682_SSP_CODEC(0) | + SOF_SPEAKER_AMP_PRESENT | + SOF_MAX98390_SPEAKER_AMP_PRESENT | + SOF_RT5682_SSP_AMP(1) | + SOF_RT5682_NUM_HDMIDEV(4) | + SOF_BT_OFFLOAD_SSP(2) | + SOF_SSP_BT_OFFLOAD_PRESENT), + }, { .name = "adl_mx98360_rt5682", .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | @@ -1080,6 +1151,7 @@ MODULE_DESCRIPTION("SOF Audio Machine driver"); MODULE_AUTHOR("Bard Liao "); MODULE_AUTHOR("Sathya Prakash M R "); MODULE_AUTHOR("Brent Lu "); +MODULE_AUTHOR("Mac Chiang "); MODULE_LICENSE("GPL v2"); MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON); MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_MAXIM_COMMON); diff --git a/sound/soc/intel/common/soc-acpi-intel-adl-match.c b/sound/soc/intel/common/soc-acpi-intel-adl-match.c index fde310e5724b..f32bcb2b2e09 100644 --- a/sound/soc/intel/common/soc-acpi-intel-adl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-adl-match.c @@ -379,6 +379,11 @@ static const struct snd_soc_acpi_codecs adl_rt1019p_amp = { .codecs = {"RTL1019"} }; +static const struct snd_soc_acpi_codecs adl_max98390_amp = { + .num_codecs = 1, + .codecs = {"MX98390"} +}; + struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { { .comp_ids = &adl_rt5682_rt5682s_hp, @@ -434,6 +439,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { .sof_fw_filename = "sof-adl.ri", .sof_tplg_filename = "sof-adl-nau8825.tplg", }, + { + .comp_ids = &adl_rt5682_rt5682s_hp, + .drv_name = "adl_max98390_rt5682", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &adl_max98390_amp, + .sof_fw_filename = "sof-adl.ri", + .sof_tplg_filename = "sof-adl-max98390-rt5682.tplg", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_adl_machines); diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index b4bc4f887b43..20c6ca37dbc4 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -59,6 +59,15 @@ static const struct dmi_system_id sof_tplg_table[] = { }, .driver_data = "sof-adl-rt5682-ssp0-max98373-ssp2.tplg", }, + { + .callback = sof_tplg_cb, + .matches = { + DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Brya"), + DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98390_ALC5682I_I2S"), + }, + .driver_data = "sof-adl-max98390-ssp2-rt5682-ssp0.tplg", + }, + {} }; -- cgit v1.2.3 From 15fa179f3f45415696d376abc84e0098a9586b33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amadeusz=20S=C5=82awi=C5=84ski?= Date: Fri, 26 Nov 2021 15:03:53 +0100 Subject: ALSA: hda: Fill gaps in NHLT endpoint-interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two key operations missings are: endpoint presence-check and retrieval of matching endpoint hardware configuration (blob). Add operations for both use cases. Signed-off-by: Amadeusz SÅ‚awiÅ„ski Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20211126140355.1042684-2-cezary.rojewski@intel.com Signed-off-by: Takashi Iwai --- include/sound/intel-nhlt.h | 37 ++++++++++++---- sound/hda/intel-nhlt.c | 102 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 8 deletions(-) diff --git a/include/sound/intel-nhlt.h b/include/sound/intel-nhlt.h index d0574805865f..089a760d36eb 100644 --- a/include/sound/intel-nhlt.h +++ b/include/sound/intel-nhlt.h @@ -10,6 +10,14 @@ #include +enum nhlt_link_type { + NHLT_LINK_HDA = 0, + NHLT_LINK_DSP = 1, + NHLT_LINK_DMIC = 2, + NHLT_LINK_SSP = 3, + NHLT_LINK_INVALID +}; + #if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_SND_INTEL_NHLT) struct wav_fmt { @@ -33,14 +41,6 @@ struct wav_fmt_ext { u8 sub_fmt[16]; } __packed; -enum nhlt_link_type { - NHLT_LINK_HDA = 0, - NHLT_LINK_DSP = 1, - NHLT_LINK_DMIC = 2, - NHLT_LINK_SSP = 3, - NHLT_LINK_INVALID -}; - enum nhlt_device_type { NHLT_DEVICE_BT = 0, NHLT_DEVICE_DMIC = 1, @@ -132,6 +132,12 @@ void intel_nhlt_free(struct nhlt_acpi_table *addr); int intel_nhlt_get_dmic_geo(struct device *dev, struct nhlt_acpi_table *nhlt); +bool intel_nhlt_has_endpoint_type(struct nhlt_acpi_table *nhlt, u8 link_type); +struct nhlt_specific_cfg * +intel_nhlt_get_endpoint_blob(struct device *dev, struct nhlt_acpi_table *nhlt, + u32 bus_id, u8 link_type, u8 vbps, u8 bps, + u8 num_ch, u32 rate, u8 dir, u8 dev_type); + #else struct nhlt_acpi_table; @@ -150,6 +156,21 @@ static inline int intel_nhlt_get_dmic_geo(struct device *dev, { return 0; } + +static inline bool intel_nhlt_has_endpoint_type(struct nhlt_acpi_table *nhlt, + u8 link_type) +{ + return false; +} + +static inline struct nhlt_specific_cfg * +intel_nhlt_get_endpoint_blob(struct device *dev, struct nhlt_acpi_table *nhlt, + u32 bus_id, u8 link_type, u8 vbps, u8 bps, + u8 num_ch, u32 rate, u8 dir, u8 dev_type) +{ + return NULL; +} + #endif #endif diff --git a/sound/hda/intel-nhlt.c b/sound/hda/intel-nhlt.c index e2237239d922..128476aa7c61 100644 --- a/sound/hda/intel-nhlt.c +++ b/sound/hda/intel-nhlt.c @@ -110,3 +110,105 @@ int intel_nhlt_get_dmic_geo(struct device *dev, struct nhlt_acpi_table *nhlt) return dmic_geo; } EXPORT_SYMBOL_GPL(intel_nhlt_get_dmic_geo); + +bool intel_nhlt_has_endpoint_type(struct nhlt_acpi_table *nhlt, u8 link_type) +{ + struct nhlt_endpoint *epnt; + int i; + + if (!nhlt) + return false; + + epnt = (struct nhlt_endpoint *)nhlt->desc; + for (i = 0; i < nhlt->endpoint_count; i++) { + if (epnt->linktype == link_type) + return true; + + epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length); + } + return false; +} +EXPORT_SYMBOL(intel_nhlt_has_endpoint_type); + +static struct nhlt_specific_cfg * +nhlt_get_specific_cfg(struct device *dev, struct nhlt_fmt *fmt, u8 num_ch, + u32 rate, u8 vbps, u8 bps) +{ + struct nhlt_fmt_cfg *cfg = fmt->fmt_config; + struct wav_fmt *wfmt; + u16 _bps, _vbps; + int i; + + dev_dbg(dev, "Endpoint format count=%d\n", fmt->fmt_count); + + for (i = 0; i < fmt->fmt_count; i++) { + wfmt = &cfg->fmt_ext.fmt; + _bps = wfmt->bits_per_sample; + _vbps = cfg->fmt_ext.sample.valid_bits_per_sample; + + dev_dbg(dev, "Endpoint format: ch=%d fmt=%d/%d rate=%d\n", + wfmt->channels, _vbps, _bps, wfmt->samples_per_sec); + + if (wfmt->channels == num_ch && wfmt->samples_per_sec == rate && + vbps == _vbps && bps == _bps) + return &cfg->config; + + cfg = (struct nhlt_fmt_cfg *)(cfg->config.caps + cfg->config.size); + } + + return NULL; +} + +static bool nhlt_check_ep_match(struct device *dev, struct nhlt_endpoint *epnt, + u32 bus_id, u8 link_type, u8 dir, u8 dev_type) +{ + dev_dbg(dev, "Endpoint: vbus_id=%d link_type=%d dir=%d dev_type = %d\n", + epnt->virtual_bus_id, epnt->linktype, + epnt->direction, epnt->device_type); + + if ((epnt->virtual_bus_id != bus_id) || + (epnt->linktype != link_type) || + (epnt->direction != dir)) + return false; + + /* link of type DMIC bypasses device_type check */ + return epnt->linktype == NHLT_LINK_DMIC || + epnt->device_type == dev_type; +} + +struct nhlt_specific_cfg * +intel_nhlt_get_endpoint_blob(struct device *dev, struct nhlt_acpi_table *nhlt, + u32 bus_id, u8 link_type, u8 vbps, u8 bps, + u8 num_ch, u32 rate, u8 dir, u8 dev_type) +{ + struct nhlt_specific_cfg *cfg; + struct nhlt_endpoint *epnt; + struct nhlt_fmt *fmt; + int i; + + if (!nhlt) + return NULL; + + dev_dbg(dev, "Looking for configuration:\n"); + dev_dbg(dev, " vbus_id=%d link_type=%d dir=%d, dev_type=%d\n", + bus_id, link_type, dir, dev_type); + dev_dbg(dev, " ch=%d fmt=%d/%d rate=%d\n", num_ch, vbps, bps, rate); + dev_dbg(dev, "Endpoint count=%d\n", nhlt->endpoint_count); + + epnt = (struct nhlt_endpoint *)nhlt->desc; + + for (i = 0; i < nhlt->endpoint_count; i++) { + if (nhlt_check_ep_match(dev, epnt, bus_id, link_type, dir, dev_type)) { + fmt = (struct nhlt_fmt *)(epnt->config.caps + epnt->config.size); + + cfg = nhlt_get_specific_cfg(dev, fmt, num_ch, rate, vbps, bps); + if (cfg) + return cfg; + } + + epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length); + } + + return NULL; +} +EXPORT_SYMBOL(intel_nhlt_get_endpoint_blob); -- cgit v1.2.3 From 8235a08bbc6be993c5de1de1f5d7a07110831248 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amadeusz=20S=C5=82awi=C5=84ski?= Date: Fri, 26 Nov 2021 15:03:54 +0100 Subject: ALSA: hda: Simplify DMIC-in-NHLT check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only DMIC endpoint presence is relevant, not its configuration. Signed-off-by: Amadeusz SÅ‚awiÅ„ski Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20211126140355.1042684-3-cezary.rojewski@intel.com Signed-off-by: Takashi Iwai --- sound/hda/intel-dsp-config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/intel-dsp-config.c index b9ac9e9e45a4..26f8665da689 100644 --- a/sound/hda/intel-dsp-config.c +++ b/sound/hda/intel-dsp-config.c @@ -384,7 +384,7 @@ static int snd_intel_dsp_check_dmic(struct pci_dev *pci) nhlt = intel_nhlt_init(&pci->dev); if (nhlt) { - if (intel_nhlt_get_dmic_geo(&pci->dev, nhlt)) + if (intel_nhlt_has_endpoint_type(nhlt, NHLT_LINK_DMIC)) ret = 1; intel_nhlt_free(nhlt); } -- cgit v1.2.3 From 322fa4315400807c697b034b4694f0a074cc1258 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amadeusz=20S=C5=82awi=C5=84ski?= Date: Fri, 26 Nov 2021 15:03:55 +0100 Subject: ASoC: Intel: Skylake: Use NHLT API to search for blob MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With NHLT enriched with new search functions, remove local code in favour of them. This also fixes broken behaviour: search should be based on significant bits count rather than container size. Signed-off-by: Amadeusz SÅ‚awiÅ„ski Signed-off-by: Cezary Rojewski Acked-by: Mark Brown Link: https://lore.kernel.org/r/20211126140355.1042684-4-cezary.rojewski@intel.com Signed-off-by: Takashi Iwai --- sound/soc/intel/skylake/skl-nhlt.c | 102 --------------------------------- sound/soc/intel/skylake/skl-pcm.c | 3 + sound/soc/intel/skylake/skl-topology.c | 29 ++++++---- sound/soc/intel/skylake/skl-topology.h | 1 + sound/soc/intel/skylake/skl.h | 4 -- 5 files changed, 21 insertions(+), 118 deletions(-) diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index 64226072f0ee..2439a574ac2f 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -13,108 +13,6 @@ #include "skl.h" #include "skl-i2s.h" -static struct nhlt_specific_cfg *skl_get_specific_cfg( - struct device *dev, struct nhlt_fmt *fmt, - u8 no_ch, u32 rate, u16 bps, u8 linktype) -{ - struct nhlt_specific_cfg *sp_config; - struct wav_fmt *wfmt; - struct nhlt_fmt_cfg *fmt_config = fmt->fmt_config; - int i; - - dev_dbg(dev, "Format count =%d\n", fmt->fmt_count); - - for (i = 0; i < fmt->fmt_count; i++) { - wfmt = &fmt_config->fmt_ext.fmt; - dev_dbg(dev, "ch=%d fmt=%d s_rate=%d\n", wfmt->channels, - wfmt->bits_per_sample, wfmt->samples_per_sec); - if (wfmt->channels == no_ch && wfmt->bits_per_sample == bps) { - /* - * if link type is dmic ignore rate check as the blob is - * generic for all rates - */ - sp_config = &fmt_config->config; - if (linktype == NHLT_LINK_DMIC) - return sp_config; - - if (wfmt->samples_per_sec == rate) - return sp_config; - } - - fmt_config = (struct nhlt_fmt_cfg *)(fmt_config->config.caps + - fmt_config->config.size); - } - - return NULL; -} - -static void dump_config(struct device *dev, u32 instance_id, u8 linktype, - u8 s_fmt, u8 num_channels, u32 s_rate, u8 dirn, u16 bps) -{ - dev_dbg(dev, "Input configuration\n"); - dev_dbg(dev, "ch=%d fmt=%d s_rate=%d\n", num_channels, s_fmt, s_rate); - dev_dbg(dev, "vbus_id=%d link_type=%d\n", instance_id, linktype); - dev_dbg(dev, "bits_per_sample=%d\n", bps); -} - -static bool skl_check_ep_match(struct device *dev, struct nhlt_endpoint *epnt, - u32 instance_id, u8 link_type, u8 dirn, u8 dev_type) -{ - dev_dbg(dev, "vbus_id=%d link_type=%d dir=%d dev_type = %d\n", - epnt->virtual_bus_id, epnt->linktype, - epnt->direction, epnt->device_type); - - if ((epnt->virtual_bus_id == instance_id) && - (epnt->linktype == link_type) && - (epnt->direction == dirn)) { - /* do not check dev_type for DMIC link type */ - if (epnt->linktype == NHLT_LINK_DMIC) - return true; - - if (epnt->device_type == dev_type) - return true; - } - - return false; -} - -struct nhlt_specific_cfg -*skl_get_ep_blob(struct skl_dev *skl, u32 instance, u8 link_type, - u8 s_fmt, u8 num_ch, u32 s_rate, - u8 dirn, u8 dev_type) -{ - struct nhlt_fmt *fmt; - struct nhlt_endpoint *epnt; - struct hdac_bus *bus = skl_to_bus(skl); - struct device *dev = bus->dev; - struct nhlt_specific_cfg *sp_config; - struct nhlt_acpi_table *nhlt = skl->nhlt; - u16 bps = (s_fmt == 16) ? 16 : 32; - u8 j; - - dump_config(dev, instance, link_type, s_fmt, num_ch, s_rate, dirn, bps); - - epnt = (struct nhlt_endpoint *)nhlt->desc; - - dev_dbg(dev, "endpoint count =%d\n", nhlt->endpoint_count); - - for (j = 0; j < nhlt->endpoint_count; j++) { - if (skl_check_ep_match(dev, epnt, instance, link_type, - dirn, dev_type)) { - fmt = (struct nhlt_fmt *)(epnt->config.caps + - epnt->config.size); - sp_config = skl_get_specific_cfg(dev, fmt, num_ch, - s_rate, bps, link_type); - if (sp_config) - return sp_config; - } - - epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length); - } - - return NULL; -} - static void skl_nhlt_trim_space(char *trim) { char *s = trim; diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index e4aa366d356e..4c5d209a67ba 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -317,6 +317,7 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream, dev_dbg(dai->dev, "dma_id=%d\n", dma_id); p_params.s_fmt = snd_pcm_format_width(params_format(params)); + p_params.s_cont = snd_pcm_format_physical_width(params_format(params)); p_params.ch = params_channels(params); p_params.s_freq = params_rate(params); p_params.host_dma_id = dma_id; @@ -405,6 +406,7 @@ static int skl_be_hw_params(struct snd_pcm_substream *substream, struct skl_pipe_params p_params = {0}; p_params.s_fmt = snd_pcm_format_width(params_format(params)); + p_params.s_cont = snd_pcm_format_physical_width(params_format(params)); p_params.ch = params_channels(params); p_params.s_freq = params_rate(params); p_params.stream = substream->stream; @@ -569,6 +571,7 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream, snd_soc_dai_set_tdm_slot(codec_dai, 0, stream_tag, 0, 0); p_params.s_fmt = snd_pcm_format_width(params_format(params)); + p_params.s_cont = snd_pcm_format_physical_width(params_format(params)); p_params.ch = params_channels(params); p_params.s_freq = params_rate(params); p_params.stream = substream->stream; diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 89e4231304dd..9bdf020a2b64 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -285,7 +285,7 @@ static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w, { struct skl_module_cfg *m_cfg = w->priv; int link_type, dir; - u32 ch, s_freq, s_fmt; + u32 ch, s_freq, s_fmt, s_cont; struct nhlt_specific_cfg *cfg; u8 dev_type = skl_tplg_be_dev_type(m_cfg->dev_type); int fmt_idx = m_cfg->fmt_idx; @@ -301,7 +301,8 @@ static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w, link_type = NHLT_LINK_DMIC; dir = SNDRV_PCM_STREAM_CAPTURE; s_freq = m_iface->inputs[0].fmt.s_freq; - s_fmt = m_iface->inputs[0].fmt.bit_depth; + s_fmt = m_iface->inputs[0].fmt.valid_bit_depth; + s_cont = m_iface->inputs[0].fmt.bit_depth; ch = m_iface->inputs[0].fmt.channels; break; @@ -310,12 +311,14 @@ static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w, if (m_cfg->hw_conn_type == SKL_CONN_SOURCE) { dir = SNDRV_PCM_STREAM_PLAYBACK; s_freq = m_iface->outputs[0].fmt.s_freq; - s_fmt = m_iface->outputs[0].fmt.bit_depth; + s_fmt = m_iface->outputs[0].fmt.valid_bit_depth; + s_cont = m_iface->outputs[0].fmt.bit_depth; ch = m_iface->outputs[0].fmt.channels; } else { dir = SNDRV_PCM_STREAM_CAPTURE; s_freq = m_iface->inputs[0].fmt.s_freq; - s_fmt = m_iface->inputs[0].fmt.bit_depth; + s_fmt = m_iface->inputs[0].fmt.valid_bit_depth; + s_cont = m_iface->inputs[0].fmt.bit_depth; ch = m_iface->inputs[0].fmt.channels; } break; @@ -325,16 +328,17 @@ static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w, } /* update the blob based on virtual bus_id and default params */ - cfg = skl_get_ep_blob(skl, m_cfg->vbus_id, link_type, - s_fmt, ch, s_freq, dir, dev_type); + cfg = intel_nhlt_get_endpoint_blob(skl->dev, skl->nhlt, m_cfg->vbus_id, + link_type, s_fmt, s_cont, ch, + s_freq, dir, dev_type); if (cfg) { m_cfg->formats_config[SKL_PARAM_INIT].caps_size = cfg->size; m_cfg->formats_config[SKL_PARAM_INIT].caps = (u32 *)&cfg->caps; } else { dev_err(skl->dev, "Blob NULL for id %x type %d dirn %d\n", m_cfg->vbus_id, link_type, dir); - dev_err(skl->dev, "PCM: ch %d, freq %d, fmt %d\n", - ch, s_freq, s_fmt); + dev_err(skl->dev, "PCM: ch %d, freq %d, fmt %d/%d\n", + ch, s_freq, s_fmt, s_cont); return -EIO; } @@ -1849,10 +1853,11 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, pipe_fmt = &pipe->configs[pipe->pipe_config_idx].in_fmt; /* update the blob based on virtual bus_id*/ - cfg = skl_get_ep_blob(skl, mconfig->vbus_id, link_type, - pipe_fmt->bps, pipe_fmt->channels, - pipe_fmt->freq, pipe->direction, - dev_type); + cfg = intel_nhlt_get_endpoint_blob(dai->dev, skl->nhlt, + mconfig->vbus_id, link_type, + pipe_fmt->bps, params->s_cont, + pipe_fmt->channels, pipe_fmt->freq, + pipe->direction, dev_type); if (cfg) { mconfig->formats_config[SKL_PARAM_INIT].caps_size = cfg->size; mconfig->formats_config[SKL_PARAM_INIT].caps = (u32 *)&cfg->caps; diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index f0695b2ac5dd..22963634fbea 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -284,6 +284,7 @@ struct skl_pipe_params { u32 ch; u32 s_freq; u32 s_fmt; + u32 s_cont; u8 linktype; snd_pcm_format_t format; int link_index; diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 33ed274fc0cb..f55f8b3dbdc3 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -165,10 +165,6 @@ struct skl_dsp_ops { int skl_platform_unregister(struct device *dev); int skl_platform_register(struct device *dev); -struct nhlt_specific_cfg *skl_get_ep_blob(struct skl_dev *skl, u32 instance, - u8 link_type, u8 s_fmt, u8 num_ch, - u32 s_rate, u8 dirn, u8 dev_type); - int skl_nhlt_update_topology_bin(struct skl_dev *skl); int skl_init_dsp(struct skl_dev *skl); int skl_free_dsp(struct skl_dev *skl); -- cgit v1.2.3 From 8e7daf318d97f25e18b2fc7eb5909e34cd903575 Mon Sep 17 00:00:00 2001 From: Bixuan Cui Date: Wed, 1 Dec 2021 16:58:54 +0800 Subject: ALSA: oss: fix compile error when OSS_DEBUG is enabled Fix compile error when OSS_DEBUG is enabled: sound/core/oss/pcm_oss.c: In function 'snd_pcm_oss_set_trigger': sound/core/oss/pcm_oss.c:2055:10: error: 'substream' undeclared (first use in this function); did you mean 'csubstream'? pcm_dbg(substream->pcm, "pcm_oss: trigger = 0x%x\n", trigger); ^ Fixes: 61efcee8608c ("ALSA: oss: Use standard printk helpers") Signed-off-by: Bixuan Cui Link: https://lore.kernel.org/r/1638349134-110369-1-git-send-email-cuibixuan@linux.alibaba.com Signed-off-by: Takashi Iwai --- sound/core/oss/pcm_oss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 82a818734a5f..bb37665ad3c2 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -2052,7 +2052,7 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr int err, cmd; #ifdef OSS_DEBUG - pcm_dbg(substream->pcm, "pcm_oss: trigger = 0x%x\n", trigger); + pr_debug("pcm_oss: trigger = 0x%x\n", trigger); #endif psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; -- cgit v1.2.3 From 0d242698fa693ab8cb98c11ba7cf7fc8f7242c0b Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Tue, 30 Nov 2021 18:53:25 +0530 Subject: ASoC: tegra: Add master volume/mute control support The MVC module has a per channel control bit, based on which it decides to apply channel specific volume/mute settings. When per channel control bit is enabled (which is the default HW configuration), all MVC channel volume/mute can be independently controlled. If the control is disabled, channel-0 volume/mute setting is applied by HW to all remaining channels. Thus add support to leverage this HW feature by exposing master controls for volume/mute. With this, now there are per channel and master volume/mute controls. Users need to just use controls which are suitable for their applications. The per channel control enable/disable is mananged in driver and hidden from users, so that they need to just worry about respective volume/mute controls. Signed-off-by: Sameer Pujar Link: https://lore.kernel.org/r/1638278605-28225-1-git-send-email-spujar@nvidia.com Signed-off-by: Mark Brown --- sound/soc/tegra/tegra210_mvc.c | 209 ++++++++++++++++++++++++++++++++--------- sound/soc/tegra/tegra210_mvc.h | 5 + 2 files changed, 169 insertions(+), 45 deletions(-) diff --git a/sound/soc/tegra/tegra210_mvc.c b/sound/soc/tegra/tegra210_mvc.c index acf59328dcb6..725385e17d84 100644 --- a/sound/soc/tegra/tegra210_mvc.c +++ b/sound/soc/tegra/tegra210_mvc.c @@ -108,67 +108,152 @@ static void tegra210_mvc_conv_vol(struct tegra210_mvc *mvc, u8 chan, s32 val) } } -static int tegra210_mvc_get_mute(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static u32 tegra210_mvc_get_ctrl_reg(struct snd_kcontrol *kcontrol) { struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt); - u8 mute_mask; u32 val; pm_runtime_get_sync(cmpnt->dev); regmap_read(mvc->regmap, TEGRA210_MVC_CTRL, &val); pm_runtime_put(cmpnt->dev); - mute_mask = (val >> TEGRA210_MVC_MUTE_SHIFT) & - TEGRA210_MUTE_MASK_EN; + return val; +} + +static int tegra210_mvc_get_mute(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u32 val = tegra210_mvc_get_ctrl_reg(kcontrol); + u8 mute_mask = TEGRA210_GET_MUTE_VAL(val); - ucontrol->value.integer.value[0] = mute_mask; + /* + * If per channel control is enabled, then return + * exact mute/unmute setting of all channels. + * + * Else report setting based on CH0 bit to reflect + * the correct HW state. + */ + if (val & TEGRA210_MVC_PER_CHAN_CTRL_EN) { + ucontrol->value.integer.value[0] = mute_mask; + } else { + if (mute_mask & TEGRA210_MVC_CH0_MUTE_EN) + ucontrol->value.integer.value[0] = + TEGRA210_MUTE_MASK_EN; + else + ucontrol->value.integer.value[0] = 0; + } return 0; } -static int tegra210_mvc_put_mute(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int tegra210_mvc_get_master_mute(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u32 val = tegra210_mvc_get_ctrl_reg(kcontrol); + u8 mute_mask = TEGRA210_GET_MUTE_VAL(val); + + /* + * If per channel control is disabled, then return + * master mute/unmute setting based on CH0 bit. + * + * Else report settings based on state of all + * channels. + */ + if (!(val & TEGRA210_MVC_PER_CHAN_CTRL_EN)) { + ucontrol->value.integer.value[0] = + mute_mask & TEGRA210_MVC_CH0_MUTE_EN; + } else { + if (mute_mask == TEGRA210_MUTE_MASK_EN) + ucontrol->value.integer.value[0] = + TEGRA210_MVC_CH0_MUTE_EN; + else + ucontrol->value.integer.value[0] = 0; + } + + return 0; +} + +static int tegra210_mvc_volume_switch_timeout(struct snd_soc_component *cmpnt) { - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt); - unsigned int value; - u8 new_mask, old_mask; + u32 value; int err; - pm_runtime_get_sync(cmpnt->dev); - - /* Check if VOLUME_SWITCH is triggered */ err = regmap_read_poll_timeout(mvc->regmap, TEGRA210_MVC_SWITCH, value, !(value & TEGRA210_MVC_VOLUME_SWITCH_MASK), 10, 10000); if (err < 0) - goto end; + dev_err(cmpnt->dev, + "Volume switch trigger is still active, err = %d\n", + err); - regmap_read(mvc->regmap, TEGRA210_MVC_CTRL, &value); + return err; +} - old_mask = (value >> TEGRA210_MVC_MUTE_SHIFT) & TEGRA210_MUTE_MASK_EN; - new_mask = ucontrol->value.integer.value[0]; +static int tegra210_mvc_update_mute(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol, + bool per_chan_ctrl) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt); + u32 mute_val = ucontrol->value.integer.value[0]; + u32 per_ch_ctrl_val; + bool change = false; + int err; - if (new_mask == old_mask) { - err = 0; + pm_runtime_get_sync(cmpnt->dev); + + err = tegra210_mvc_volume_switch_timeout(cmpnt); + if (err < 0) goto end; + + if (per_chan_ctrl) { + per_ch_ctrl_val = TEGRA210_MVC_PER_CHAN_CTRL_EN; + } else { + per_ch_ctrl_val = 0; + + if (mute_val) + mute_val = TEGRA210_MUTE_MASK_EN; } - err = regmap_update_bits(mvc->regmap, mc->reg, + regmap_update_bits_check(mvc->regmap, TEGRA210_MVC_CTRL, TEGRA210_MVC_MUTE_MASK, - new_mask << TEGRA210_MVC_MUTE_SHIFT); - if (err < 0) - goto end; + mute_val << TEGRA210_MVC_MUTE_SHIFT, + &change); - err = 1; + if (change) { + regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL, + TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK, + per_ch_ctrl_val); + + regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH, + TEGRA210_MVC_VOLUME_SWITCH_MASK, + TEGRA210_MVC_VOLUME_SWITCH_TRIGGER); + } end: pm_runtime_put(cmpnt->dev); - return err; + + if (err < 0) + return err; + + if (change) + return 1; + + return 0; +} + +static int tegra210_mvc_put_mute(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return tegra210_mvc_update_mute(kcontrol, ucontrol, true); +} + +static int tegra210_mvc_put_master_mute(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return tegra210_mvc_update_mute(kcontrol, ucontrol, false); } static int tegra210_mvc_get_vol(struct snd_kcontrol *kcontrol, @@ -178,7 +263,7 @@ static int tegra210_mvc_get_vol(struct snd_kcontrol *kcontrol, (struct soc_mixer_control *)kcontrol->private_value; struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt); - u8 chan = (mc->reg - TEGRA210_MVC_TARGET_VOL) / REG_SIZE; + u8 chan = TEGRA210_MVC_GET_CHAN(mc->reg, TEGRA210_MVC_TARGET_VOL); s32 val = mvc->volume[chan]; if (mvc->curve_type == CURVE_POLY) { @@ -193,44 +278,55 @@ static int tegra210_mvc_get_vol(struct snd_kcontrol *kcontrol, return 0; } -static int tegra210_mvc_put_vol(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int tegra210_mvc_get_master_vol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return tegra210_mvc_get_vol(kcontrol, ucontrol); +} + +static int tegra210_mvc_update_vol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol, + bool per_ch_enable) { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt); - unsigned int reg = mc->reg; - unsigned int value; - u8 chan; - int err, old_volume; + u8 chan = TEGRA210_MVC_GET_CHAN(mc->reg, TEGRA210_MVC_TARGET_VOL); + int old_volume = mvc->volume[chan]; + int err, i; pm_runtime_get_sync(cmpnt->dev); - /* Check if VOLUME_SWITCH is triggered */ - err = regmap_read_poll_timeout(mvc->regmap, TEGRA210_MVC_SWITCH, - value, !(value & TEGRA210_MVC_VOLUME_SWITCH_MASK), - 10, 10000); + err = tegra210_mvc_volume_switch_timeout(cmpnt); if (err < 0) goto end; - chan = (reg - TEGRA210_MVC_TARGET_VOL) / REG_SIZE; - old_volume = mvc->volume[chan]; - - tegra210_mvc_conv_vol(mvc, chan, - ucontrol->value.integer.value[0]); + tegra210_mvc_conv_vol(mvc, chan, ucontrol->value.integer.value[0]); if (mvc->volume[chan] == old_volume) { err = 0; goto end; } + if (per_ch_enable) { + regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL, + TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK, + TEGRA210_MVC_PER_CHAN_CTRL_EN); + } else { + regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL, + TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK, 0); + + for (i = 1; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++) + mvc->volume[i] = mvc->volume[chan]; + } + /* Configure init volume same as target volume */ regmap_write(mvc->regmap, TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_INIT_VOL, chan), mvc->volume[chan]); - regmap_write(mvc->regmap, reg, mvc->volume[chan]); + regmap_write(mvc->regmap, mc->reg, mvc->volume[chan]); regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH, TEGRA210_MVC_VOLUME_SWITCH_MASK, @@ -240,9 +336,22 @@ static int tegra210_mvc_put_vol(struct snd_kcontrol *kcontrol, end: pm_runtime_put(cmpnt->dev); + return err; } +static int tegra210_mvc_put_vol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return tegra210_mvc_update_vol(kcontrol, ucontrol, true); +} + +static int tegra210_mvc_put_master_vol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return tegra210_mvc_update_vol(kcontrol, ucontrol, false); +} + static void tegra210_mvc_reset_vol_settings(struct tegra210_mvc *mvc, struct device *dev) { @@ -436,6 +545,16 @@ static const struct snd_kcontrol_new tegra210_mvc_vol_ctrl[] = { TEGRA210_MVC_CTRL, 0, TEGRA210_MUTE_MASK_EN, 0, tegra210_mvc_get_mute, tegra210_mvc_put_mute), + /* Master volume */ + SOC_SINGLE_EXT("Volume", TEGRA210_MVC_TARGET_VOL, 0, 16000, 0, + tegra210_mvc_get_master_vol, + tegra210_mvc_put_master_vol), + + /* Master mute */ + SOC_SINGLE_EXT("Mute", TEGRA210_MVC_CTRL, 0, 1, 0, + tegra210_mvc_get_master_mute, + tegra210_mvc_put_master_mute), + SOC_ENUM_EXT("Curve Type", tegra210_mvc_curve_type_ctrl, tegra210_mvc_get_curve_type, tegra210_mvc_put_curve_type), }; diff --git a/sound/soc/tegra/tegra210_mvc.h b/sound/soc/tegra/tegra210_mvc.h index def29c4c7257..d775335dc60b 100644 --- a/sound/soc/tegra/tegra210_mvc.h +++ b/sound/soc/tegra/tegra210_mvc.h @@ -59,6 +59,7 @@ #define TEGRA210_MUTE_MASK_EN 0xff #define TEGRA210_MVC_MUTE_MASK (TEGRA210_MUTE_MASK_EN << TEGRA210_MVC_MUTE_SHIFT) #define TEGRA210_MVC_MUTE_EN (TEGRA210_MUTE_MASK_EN << TEGRA210_MVC_MUTE_SHIFT) +#define TEGRA210_MVC_CH0_MUTE_EN 1 #define TEGRA210_MVC_PER_CHAN_CTRL_EN_SHIFT 30 #define TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK (1 << TEGRA210_MVC_PER_CHAN_CTRL_EN_SHIFT) @@ -92,6 +93,10 @@ #define TEGRA210_MVC_MAX_CHAN_COUNT 8 #define TEGRA210_MVC_REG_OFFSET(reg, i) (reg + (REG_SIZE * i)) +#define TEGRA210_MVC_GET_CHAN(reg, base) (((reg) - (base)) / REG_SIZE) + +#define TEGRA210_GET_MUTE_VAL(val) (((val) >> TEGRA210_MVC_MUTE_SHIFT) & TEGRA210_MUTE_MASK_EN) + #define NUM_GAIN_POLY_COEFFS 9 enum { -- cgit v1.2.3 From b80155fe61a76784273c2e7b8b15ae8249eb7440 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 30 Nov 2021 16:05:05 +0000 Subject: ASoC: codecs: wcd934x: remove redundant ret variable return value form snd_soc_dapm_put_enum_double() directly instead of taking this in another redundant variable. Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20211130160507.22180-3-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd934x.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c index 4f568abd59e2..f94cbce96d77 100644 --- a/sound/soc/codecs/wcd934x.c +++ b/sound/soc/codecs/wcd934x.c @@ -3379,7 +3379,7 @@ static int wcd934x_int_dem_inp_mux_put(struct snd_kcontrol *kc, { struct soc_enum *e = (struct soc_enum *)kc->private_value; struct snd_soc_component *component; - int reg, val, ret; + int reg, val; component = snd_soc_dapm_kcontrol_component(kc); val = ucontrol->value.enumerated.item[0]; @@ -3402,9 +3402,7 @@ static int wcd934x_int_dem_inp_mux_put(struct snd_kcontrol *kc, WCD934X_RX_DLY_ZN_EN_MASK, WCD934X_RX_DLY_ZN_DISABLE); - ret = snd_soc_dapm_put_enum_double(kc, ucontrol); - - return ret; + return snd_soc_dapm_put_enum_double(kc, ucontrol); } static int wcd934x_dec_enum_put(struct snd_kcontrol *kcontrol, -- cgit v1.2.3 From ce9778b7a0272f7c7e5bc33f537380a5d2aed6c7 Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Thu, 2 Dec 2021 15:33:35 +0800 Subject: ALSA: hda/hdmi: Consider ELD is invalid when no SAD is present There's a system that reports a bogus HDMI audio interface: $ cat eld#2.0 monitor_present 1 eld_valid 1 monitor_name connection_type DisplayPort eld_version [0x2] CEA-861D or below edid_version [0x3] CEA-861-B, C or D manufacture_id 0xe430 product_id 0x690 port_id 0x0 support_hdcp 0 support_ai 0 audio_sync_delay 0 speakers [0xffff] FL/FR LFE FC RL/RR RC FLC/FRC RLC/RRC FLW/FRW FLH/FRH TC FCH sad_count 0 Since playing audio is not possible without SAD, also consider ELD is invalid for this case. Signed-off-by: Kai-Heng Feng Link: https://lore.kernel.org/r/20211202073338.1384768-1-kai.heng.feng@canonical.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 65d2c5539919..33e5f1aa24f9 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1535,7 +1535,7 @@ static void update_eld(struct hda_codec *codec, } } - if (!eld->eld_valid || eld->eld_size <= 0) { + if (!eld->eld_valid || eld->eld_size <= 0 || eld->info.sad_count <= 0) { eld->eld_valid = false; eld->eld_size = 0; } -- cgit v1.2.3 From 1e583aef12aa74afd37c1418255cc4b74e023236 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 2 Dec 2021 09:38:33 +0100 Subject: ALSA: usb-audio: Drop superfluous '0' in Presonus Studio 1810c's ID The vendor ID of Presonus Studio 1810c had a superfluous '0' in its USB ID. Drop it. Fixes: 8dc5efe3d17c ("ALSA: usb-audio: Add support for Presonus Studio 1810c") Link: https://lore.kernel.org/r/20211202083833.17784-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/format.c | 2 +- sound/usb/mixer_quirks.c | 2 +- sound/usb/quirks.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/usb/format.c b/sound/usb/format.c index f5e676a51b30..405dc0bf6678 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -375,7 +375,7 @@ static int parse_uac2_sample_rate_range(struct snd_usb_audio *chip, for (rate = min; rate <= max; rate += res) { /* Filter out invalid rates on Presonus Studio 1810c */ - if (chip->usb_id == USB_ID(0x0194f, 0x010c) && + if (chip->usb_id == USB_ID(0x194f, 0x010c) && !s1810c_valid_sample_rate(fp, rate)) goto skip_rate; diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index d489c1de3bae..db194ad168d0 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -3254,7 +3254,7 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) err = snd_rme_controls_create(mixer); break; - case USB_ID(0x0194f, 0x010c): /* Presonus Studio 1810c */ + case USB_ID(0x194f, 0x010c): /* Presonus Studio 1810c */ err = snd_sc1810_init_mixer(mixer); break; case USB_ID(0x2a39, 0x3fb0): /* RME Babyface Pro FS */ diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 64e1c20311ed..ab9f3da49941 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1290,7 +1290,7 @@ int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip, if (chip->usb_id == USB_ID(0x0763, 0x2012)) return fasttrackpro_skip_setting_quirk(chip, iface, altno); /* presonus studio 1810c: skip altsets incompatible with device_setup */ - if (chip->usb_id == USB_ID(0x0194f, 0x010c)) + if (chip->usb_id == USB_ID(0x194f, 0x010c)) return s1810c_skip_setting_quirk(chip, iface, altno); -- cgit v1.2.3 From e3dd4424c2f40aae9080667c4da42b0d7f9be711 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Wed, 1 Dec 2021 17:56:29 +0800 Subject: ASoC: rt5640: Fix the wrong state of the JD in the HDA header The patch fixes the wrong state of the JD with 1M pull up resistor in the HDA header. Signed-off-by: Oder Chiou Link: https://lore.kernel.org/r/20211201095629.21818-1-oder_chiou@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5640.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 08b37878cb00..f3659b14c74e 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -1973,7 +1973,7 @@ static int rt5640_set_bias_level(struct snd_soc_component *component, RT5640_PWR_FV1 | RT5640_PWR_FV2, RT5640_PWR_FV1 | RT5640_PWR_FV2); snd_soc_component_update_bits(component, RT5640_DUMMY1, - 0x0301, 0x0301); + 0x1, 0x1); snd_soc_component_update_bits(component, RT5640_MICBIAS, 0x0030, 0x0030); } @@ -2533,7 +2533,7 @@ static void rt5640_enable_hda_jack_detect( snd_soc_component_update_bits(component, RT5640_GPIO_CTRL3, RT5640_GP1_PF_MASK, RT5640_GP1_PF_OUT); - snd_soc_component_update_bits(component, RT5640_DUMMY1, 0x700, 0x300); + snd_soc_component_update_bits(component, RT5640_DUMMY1, 0x400, 0x0); rt5640->jack = jack; @@ -2651,13 +2651,16 @@ static int rt5640_probe(struct snd_soc_component *component) if (device_property_read_u32(component->dev, "realtek,jack-detect-source", &val) == 0) { - if (val <= RT5640_JD_SRC_GPIO4) + if (val <= RT5640_JD_SRC_GPIO4) { rt5640->jd_src = val << RT5640_JD_SFT; - else if (val == RT5640_JD_SRC_HDA_HEADER) + } else if (val == RT5640_JD_SRC_HDA_HEADER) { rt5640->jd_src = RT5640_JD_SRC_HDA_HEADER; - else + snd_soc_component_update_bits(component, RT5640_DUMMY1, + 0x0300, 0x0); + } else { dev_warn(component->dev, "Warning: Invalid jack-detect-source value: %d, leaving jack-detect disabled\n", val); + } } if (!device_property_read_bool(component->dev, "realtek,jack-detect-not-inverted")) -- cgit v1.2.3 From 19a628d8f1a6c16263d8037a918427207c8a95a0 Mon Sep 17 00:00:00 2001 From: Lucas Tanure Date: Wed, 1 Dec 2021 18:00:03 +0000 Subject: ASoC: amd: Fix dependency for SPI master Set SPI_MASTER as dependency as is using CS35L41 SPI driver Fixes: 96792fdd77cd1 ("ASoC: amd: enable vangogh platform machine driver build") Signed-off-by: Lucas Tanure Reported-by: kernel test robot Link: https://lore.kernel.org/r/20211201180004.1402156-1-tanureal@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/amd/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig index 092966ff5ea7..8961b8fd23eb 100644 --- a/sound/soc/amd/Kconfig +++ b/sound/soc/amd/Kconfig @@ -68,7 +68,7 @@ config SND_SOC_AMD_VANGOGH_MACH tristate "AMD Vangogh support for NAU8821 CS35L41" select SND_SOC_NAU8821 select SND_SOC_CS35L41_SPI - depends on SND_SOC_AMD_ACP5x && I2C + depends on SND_SOC_AMD_ACP5x && I2C && SPI_MASTER help This option enables machine driver for Vangogh platform using NAU8821 and CS35L41 codecs. -- cgit v1.2.3 From 0695ad92fe1a0bb7697eb92c6a145a73c5ab0e24 Mon Sep 17 00:00:00 2001 From: Lucas Tanure Date: Wed, 1 Dec 2021 18:00:04 +0000 Subject: ASoC: cs35l41: Fix undefined reference to core functions Auto select core driver if i2c or spi bus drivers are selected Fixes: a5e0091d62ab ("ASoC: cs35l41: Fix link problem") Signed-off-by: Lucas Tanure Reported-by: kernel test robot Link: https://lore.kernel.org/r/20211201180004.1402156-2-tanureal@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index b4f70e27342c..c033ee7d82e4 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -614,17 +614,19 @@ config SND_SOC_CS35L36 config SND_SOC_CS35L41 tristate + default y if SND_SOC_CS35L41_SPI=y + default y if SND_SOC_CS35L41_I2C=y + default m if SND_SOC_CS35L41_SPI=m + default m if SND_SOC_CS35L41_I2C=m config SND_SOC_CS35L41_SPI tristate "Cirrus Logic CS35L41 CODEC (SPI)" depends on SPI_MASTER - select SND_SOC_CS35L41 select REGMAP_SPI config SND_SOC_CS35L41_I2C tristate "Cirrus Logic CS35L41 CODEC (I2C)" depends on I2C - select SND_SOC_CS35L41 select REGMAP_I2C config SND_SOC_CS42L42 -- cgit v1.2.3 From d13a8f6d8e01a17a9fe36029e346a1f029362c9e Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 4 Dec 2021 08:28:40 +0100 Subject: ALSA: Fix some typo Some comments and include guards are not consistent with the name of the file where they can be found. This is likely some typo or cut'n'paste issues. Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/7b2bcbda298f02a34d46d8b6593daaaed9a09a45.1638602790.git.christophe.jaillet@wanadoo.fr Signed-off-by: Takashi Iwai --- sound/pci/ac97/ac97_pcm.c | 2 +- sound/pci/hda/hda_generic.h | 2 +- sound/soc/codecs/sta350.h | 2 +- sound/soc/codecs/tlv320aic26.h | 6 +++--- sound/usb/usx2y/usbusx2y.c | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/sound/pci/ac97/ac97_pcm.c b/sound/pci/ac97/ac97_pcm.c index 491de1a623cb..5fee8e89790f 100644 --- a/sound/pci/ac97/ac97_pcm.c +++ b/sound/pci/ac97/ac97_pcm.c @@ -231,7 +231,7 @@ static int set_spdif_rate(struct snd_ac97 *ac97, unsigned short rate) * If the codec doesn't support VAR, the rate must be 48000 (except * for SPDIF). * - * The valid registers are AC97_PMC_MIC_ADC_RATE, + * The valid registers are AC97_PCM_MIC_ADC_RATE, * AC97_PCM_FRONT_DAC_RATE, AC97_PCM_LR_ADC_RATE. * AC97_PCM_SURR_DAC_RATE and AC97_PCM_LFE_DAC_RATE are accepted * if the codec supports them. diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index c43bd0f0338e..8e1bc8ea74fc 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -183,7 +183,7 @@ struct hda_gen_spec { struct automic_entry am_entry[MAX_AUTO_MIC_PINS]; /* for pin sensing */ - /* current status; set in hda_geneic.c */ + /* current status; set in hda_generic.c */ unsigned int hp_jack_present:1; unsigned int line_jack_present:1; unsigned int speaker_muted:1; /* current status of speaker mute */ diff --git a/sound/soc/codecs/sta350.h b/sound/soc/codecs/sta350.h index f16900e00afa..80bf56093d94 100644 --- a/sound/soc/codecs/sta350.h +++ b/sound/soc/codecs/sta350.h @@ -14,7 +14,7 @@ #ifndef _ASOC_STA_350_H #define _ASOC_STA_350_H -/* STA50 register addresses */ +/* STA350 register addresses */ #define STA350_REGISTER_COUNT 0x4D #define STA350_COEF_COUNT 62 diff --git a/sound/soc/codecs/tlv320aic26.h b/sound/soc/codecs/tlv320aic26.h index 1f2879b7a080..c86569883e0c 100644 --- a/sound/soc/codecs/tlv320aic26.h +++ b/sound/soc/codecs/tlv320aic26.h @@ -6,8 +6,8 @@ * Copyright (C) 2008 Secret Lab Technologies Ltd. */ -#ifndef _TLV320AIC16_H_ -#define _TLV320AIC16_H_ +#ifndef _TLV320AIC26_H_ +#define _TLV320AIC26_H_ /* AIC26 Registers */ #define AIC26_PAGE_ADDR(page, offset) ((page << 11) | offset << 5) @@ -88,4 +88,4 @@ enum aic26_wlen { AIC26_WLEN_32 = 3 << 10, }; -#endif /* _TLV320AIC16_H_ */ +#endif /* _TLV320AIC26_H_ */ diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c index 099bee662af6..52f4e6652407 100644 --- a/sound/usb/usx2y/usbusx2y.c +++ b/sound/usb/usx2y/usbusx2y.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * usbusy2y.c - ALSA USB US-428 Driver + * usbusx2y.c - ALSA USB US-428 Driver * 2005-04-14 Karsten Wiese Version 0.8.7.2: -- cgit v1.2.3 From 82cd3ba691a920007503d189989d2495a41a3a10 Mon Sep 17 00:00:00 2001 From: Bernard Zhao Date: Sun, 5 Dec 2021 17:40:46 -0800 Subject: ALSA: oss: remove useless NULL check before kfree Tis patch try to remove useless NULL check before kfree Signed-off-by: Bernard Zhao Link: https://lore.kernel.org/r/20211206014135.320720-1-bernard@vivo.com Signed-off-by: Takashi Iwai --- sound/core/info_oss.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sound/core/info_oss.c b/sound/core/info_oss.c index 1ba887c7954e..ebc714b2f46b 100644 --- a/sound/core/info_oss.c +++ b/sound/core/info_oss.c @@ -32,10 +32,8 @@ int snd_oss_info_register(int dev, int num, char *string) mutex_lock(&strings); if (string == NULL) { x = snd_sndstat_strings[num][dev]; - if (x) { - kfree(x); - x = NULL; - } + kfree(x); + x = NULL; } else { x = kstrdup(string, GFP_KERNEL); if (x == NULL) { -- cgit v1.2.3 From 86a9bb5bf9f610ea6baa855b4f46ecea92876ea4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 2 Dec 2021 09:40:53 +0100 Subject: ALSA: usb-audio: Drop CONFIG_PM ifdefs Practically seen, CONFIG_PM is almost mandatory. Let's drop the ugly ifdef lines and simplify the code. Link: https://lore.kernel.org/r/20211202084053.18201-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/card.c | 7 ------- sound/usb/mixer.c | 4 ---- sound/usb/mixer.h | 2 -- sound/usb/mixer_quirks.c | 2 -- sound/usb/mixer_quirks.h | 2 -- sound/usb/power.h | 10 ---------- 6 files changed, 27 deletions(-) diff --git a/sound/usb/card.c b/sound/usb/card.c index 1764b9302d46..376962291c4d 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -987,8 +987,6 @@ void snd_usb_unlock_shutdown(struct snd_usb_audio *chip) wake_up(&chip->shutdown_wait); } -#ifdef CONFIG_PM - int snd_usb_autoresume(struct snd_usb_audio *chip) { int i, err; @@ -1100,11 +1098,6 @@ err_out: atomic_dec(&chip->active); /* allow autopm after this point */ return err; } -#else -#define usb_audio_suspend NULL -#define usb_audio_resume NULL -#define usb_audio_resume NULL -#endif /* CONFIG_PM */ static const struct usb_device_id usb_audio_ids [] = { #include "quirks-table.h" diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 5b9fd07ce2a2..e8f3f8d622ec 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -3629,7 +3629,6 @@ void snd_usb_mixer_disconnect(struct usb_mixer_interface *mixer) mixer->disconnected = true; } -#ifdef CONFIG_PM /* stop any bus activity of a mixer */ static void snd_usb_mixer_inactivate(struct usb_mixer_interface *mixer) { @@ -3711,7 +3710,6 @@ int snd_usb_mixer_resume(struct usb_mixer_interface *mixer) return snd_usb_mixer_activate(mixer); } -#endif void snd_usb_mixer_elem_init_std(struct usb_mixer_elem_list *list, struct usb_mixer_interface *mixer, @@ -3720,7 +3718,5 @@ void snd_usb_mixer_elem_init_std(struct usb_mixer_elem_list *list, list->mixer = mixer; list->id = unitid; list->dump = snd_usb_mixer_dump_cval; -#ifdef CONFIG_PM list->resume = restore_mixer_value; -#endif } diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index 98ea24d91d80..d43895c1ae5c 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -118,10 +118,8 @@ void snd_usb_mixer_elem_init_std(struct usb_mixer_elem_list *list, int snd_usb_mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *_tlv); -#ifdef CONFIG_PM int snd_usb_mixer_suspend(struct usb_mixer_interface *mixer); int snd_usb_mixer_resume(struct usb_mixer_interface *mixer); -#endif int snd_usb_set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, int index, int value); diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index db194ad168d0..1f9863725c7c 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -3280,7 +3280,6 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) return err; } -#ifdef CONFIG_PM void snd_usb_mixer_resume_quirk(struct usb_mixer_interface *mixer) { switch (mixer->chip->usb_id) { @@ -3289,7 +3288,6 @@ void snd_usb_mixer_resume_quirk(struct usb_mixer_interface *mixer) break; } } -#endif void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer, int unitid) diff --git a/sound/usb/mixer_quirks.h b/sound/usb/mixer_quirks.h index 52be26db558f..4ba01ba3fe8b 100644 --- a/sound/usb/mixer_quirks.h +++ b/sound/usb/mixer_quirks.h @@ -14,9 +14,7 @@ void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer, struct usb_mixer_elem_info *cval, int unitid, struct snd_kcontrol *kctl); -#ifdef CONFIG_PM void snd_usb_mixer_resume_quirk(struct usb_mixer_interface *mixer); -#endif #endif /* SND_USB_MIXER_QUIRKS_H */ diff --git a/sound/usb/power.h b/sound/usb/power.h index 6004231a7c75..396e3e51440a 100644 --- a/sound/usb/power.h +++ b/sound/usb/power.h @@ -21,17 +21,7 @@ struct snd_usb_power_domain * snd_usb_find_power_domain(struct usb_host_interface *ctrl_iface, unsigned char id); -#ifdef CONFIG_PM int snd_usb_autoresume(struct snd_usb_audio *chip); void snd_usb_autosuspend(struct snd_usb_audio *chip); -#else -static inline int snd_usb_autoresume(struct snd_usb_audio *chip) -{ - return 0; -} -static inline void snd_usb_autosuspend(struct snd_usb_audio *chip) -{ -} -#endif #endif /* __USBAUDIO_POWER_H */ -- cgit v1.2.3 From 4d408ea0282c374a304ce402866cb7b8a56c6b05 Mon Sep 17 00:00:00 2001 From: Trevor Wu Date: Tue, 30 Nov 2021 13:39:04 +0800 Subject: ASoC: mediatek: mt8195: support reserved memory assignment For security purpose, restrict the memory assess region of AFE memif. The specified memory region should be assigned from DTS. Signed-off-by: Trevor Wu Link: https://lore.kernel.org/r/20211130053905.28470-2-trevor.wu@mediatek.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8195/mt8195-afe-pcm.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c index 2bb05a828e8d..8a6db24116e3 100644 --- a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c +++ b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "mt8195-afe-common.h" #include "mt8195-afe-clk.h" @@ -3061,6 +3062,12 @@ static int mt8195_afe_pcm_dev_probe(struct platform_device *pdev) int i, irq_id, ret; struct snd_soc_component *component; + ret = of_reserved_mem_device_init(dev); + if (ret) { + dev_err(dev, "failed to assign memory region: %d\n", ret); + return ret; + } + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(33)); if (ret) return ret; -- cgit v1.2.3 From b6ce5d85b1425d3a1211f85835ab152c9bf3803a Mon Sep 17 00:00:00 2001 From: Ariel D'Alessandro Date: Fri, 3 Dec 2021 14:50:18 -0300 Subject: ASoC: fsl-asoc-card: Add missing Kconfig option for tlv320aic31xx The following commit added support for tlv320aic31xx codec to fsl-asoc-card, but missed the related Kconfig option. Fix this. commit 8c9b9cfb7724685ce705f511b882f30597596536 Author: Ariel D'Alessandro Date: Fri Nov 19 12:32:48 2021 -0300 ASoC: fsl-asoc-card: Support fsl,imx-audio-tlv320aic31xx codec Signed-off-by: Ariel D'Alessandro Signed-off-by: Michael Trimarchi Link: https://lore.kernel.org/r/20211203175018.252641-2-ariel.dalessandro@collabora.com Signed-off-by: Mark Brown --- sound/soc/fsl/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 8e05d092790e..10fa38753453 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -311,6 +311,7 @@ config SND_SOC_FSL_ASOC_CARD select SND_SOC_FSL_ESAI select SND_SOC_FSL_SAI select SND_SOC_FSL_SSI + select SND_SOC_TLV320AIC31XX select SND_SOC_WM8994 select MFD_WM8994 help -- cgit v1.2.3 From c9d57a25de53800e54969f4bf2b672b3a58cdaf5 Mon Sep 17 00:00:00 2001 From: Trevor Wu Date: Mon, 29 Nov 2021 22:10:54 +0800 Subject: ASoC: mediatek: mt8195: add headset codec rt5682s support mt8195 machine driver adds rt5682s support in this patch. Card name can be specified from dts by model property, and driver makes use of the name to distinguish which headset codec is on the board. Signed-off-by: Trevor Wu Link: https://lore.kernel.org/r/20211129141057.12422-2-trevor.wu@mediatek.com Signed-off-by: Mark Brown --- sound/soc/mediatek/Kconfig | 2 ++ .../mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c | 29 ++++++++++++++++----- .../mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c | 30 +++++++++++++++++----- 3 files changed, 47 insertions(+), 14 deletions(-) diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig index 3b1ddea26a9e..9306b7ca2644 100644 --- a/sound/soc/mediatek/Kconfig +++ b/sound/soc/mediatek/Kconfig @@ -205,6 +205,7 @@ config SND_SOC_MT8195_MT6359_RT1019_RT5682 select SND_SOC_MT6359 select SND_SOC_RT1015P select SND_SOC_RT5682_I2C + select SND_SOC_RT5682S select SND_SOC_DMIC select SND_SOC_HDMI_CODEC help @@ -220,6 +221,7 @@ config SND_SOC_MT8195_MT6359_RT1011_RT5682 select SND_SOC_MT6359 select SND_SOC_RT1011 select SND_SOC_RT5682_I2C + select SND_SOC_RT5682S select SND_SOC_DMIC select SND_SOC_HDMI_CODEC help diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c index 9e6b54e19c23..cca1c739e690 100644 --- a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c +++ b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c @@ -27,6 +27,9 @@ #define RT5682_CODEC_DAI "rt5682-aif1" #define RT5682_DEV0_NAME "rt5682.2-001a" +#define RT5682S_CODEC_DAI "rt5682s-aif1" +#define RT5682S_DEV0_NAME "rt5682s.2-001a" + struct mt8195_mt6359_rt1011_rt5682_priv { struct device_node *platform_node; struct device_node *hdmi_node; @@ -691,14 +694,12 @@ SND_SOC_DAILINK_DEFS(ETDM1_IN_BE, SND_SOC_DAILINK_DEFS(ETDM2_IN_BE, DAILINK_COMP_ARRAY(COMP_CPU("ETDM2_IN")), - DAILINK_COMP_ARRAY(COMP_CODEC(RT5682_DEV0_NAME, - RT5682_CODEC_DAI)), + DAILINK_COMP_ARRAY(COMP_DUMMY()), DAILINK_COMP_ARRAY(COMP_EMPTY())); SND_SOC_DAILINK_DEFS(ETDM1_OUT_BE, DAILINK_COMP_ARRAY(COMP_CPU("ETDM1_OUT")), - DAILINK_COMP_ARRAY(COMP_CODEC(RT5682_DEV0_NAME, - RT5682_CODEC_DAI)), + DAILINK_COMP_ARRAY(COMP_DUMMY()), DAILINK_COMP_ARRAY(COMP_EMPTY())); SND_SOC_DAILINK_DEFS(ETDM2_OUT_BE, @@ -1046,9 +1047,19 @@ static int mt8195_mt6359_rt1011_rt5682_dev_probe(struct platform_device *pdev) struct snd_soc_card *card = &mt8195_mt6359_rt1011_rt5682_soc_card; struct snd_soc_dai_link *dai_link; struct mt8195_mt6359_rt1011_rt5682_priv *priv; + int is5682s = 0; int ret, i; card->dev = &pdev->dev; + ret = snd_soc_of_parse_card_name(card, "model"); + if (ret) { + dev_err(&pdev->dev, "%s new card name parsing error %d\n", + __func__, ret); + return ret; + } + + if (strstr(card->name, "_5682s")) + is5682s = 1; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -1078,9 +1089,7 @@ static int mt8195_mt6359_rt1011_rt5682_dev_probe(struct platform_device *pdev) dai_link->codecs->dai_name = "i2s-hifi"; dai_link->init = mt8195_dptx_codec_init; } - } - - if (strcmp(dai_link->name, "ETDM3_OUT_BE") == 0) { + } else if (strcmp(dai_link->name, "ETDM3_OUT_BE") == 0) { priv->hdmi_node = of_parse_phandle(pdev->dev.of_node, "mediatek,hdmi-codec", 0); @@ -1092,6 +1101,12 @@ static int mt8195_mt6359_rt1011_rt5682_dev_probe(struct platform_device *pdev) dai_link->codecs->dai_name = "i2s-hifi"; dai_link->init = mt8195_hdmi_codec_init; } + } else if (strcmp(dai_link->name, "ETDM1_OUT_BE") == 0 || + strcmp(dai_link->name, "ETDM2_IN_BE") == 0) { + dai_link->codecs->name = + is5682s ? RT5682S_DEV0_NAME : RT5682_DEV0_NAME; + dai_link->codecs->dai_name = + is5682s ? RT5682S_CODEC_DAI : RT5682_CODEC_DAI; } } diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c index e22e5fd40984..c33b69b4f8e7 100644 --- a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c +++ b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c @@ -25,6 +25,9 @@ #define RT5682_CODEC_DAI "rt5682-aif1" #define RT5682_DEV0_NAME "rt5682.2-001a" +#define RT5682S_CODEC_DAI "rt5682s-aif1" +#define RT5682S_DEV0_NAME "rt5682s.2-001a" + struct mt8195_mt6359_rt1019_rt5682_priv { struct device_node *platform_node; struct device_node *hdmi_node; @@ -661,14 +664,12 @@ SND_SOC_DAILINK_DEFS(ETDM1_IN_BE, SND_SOC_DAILINK_DEFS(ETDM2_IN_BE, DAILINK_COMP_ARRAY(COMP_CPU("ETDM2_IN")), - DAILINK_COMP_ARRAY(COMP_CODEC(RT5682_DEV0_NAME, - RT5682_CODEC_DAI)), + DAILINK_COMP_ARRAY(COMP_DUMMY()), DAILINK_COMP_ARRAY(COMP_EMPTY())); SND_SOC_DAILINK_DEFS(ETDM1_OUT_BE, DAILINK_COMP_ARRAY(COMP_CPU("ETDM1_OUT")), - DAILINK_COMP_ARRAY(COMP_CODEC(RT5682_DEV0_NAME, - RT5682_CODEC_DAI)), + DAILINK_COMP_ARRAY(COMP_DUMMY()), DAILINK_COMP_ARRAY(COMP_EMPTY())); SND_SOC_DAILINK_DEFS(ETDM2_OUT_BE, @@ -999,10 +1000,21 @@ static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev) struct snd_soc_card *card = &mt8195_mt6359_rt1019_rt5682_soc_card; struct snd_soc_dai_link *dai_link; struct mt8195_mt6359_rt1019_rt5682_priv *priv; + int is5682s = 0; int ret, i; card->dev = &pdev->dev; + ret = snd_soc_of_parse_card_name(card, "model"); + if (ret) { + dev_err(&pdev->dev, "%s new card name parsing error %d\n", + __func__, ret); + return ret; + } + + if (strstr(card->name, "_5682s")) + is5682s = 1; + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -1031,9 +1043,7 @@ static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev) dai_link->codecs->dai_name = "i2s-hifi"; dai_link->init = mt8195_dptx_codec_init; } - } - - if (strcmp(dai_link->name, "ETDM3_OUT_BE") == 0) { + } else if (strcmp(dai_link->name, "ETDM3_OUT_BE") == 0) { priv->hdmi_node = of_parse_phandle(pdev->dev.of_node, "mediatek,hdmi-codec", 0); @@ -1045,6 +1055,12 @@ static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev) dai_link->codecs->dai_name = "i2s-hifi"; dai_link->init = mt8195_hdmi_codec_init; } + } else if (strcmp(dai_link->name, "ETDM1_OUT_BE") == 0 || + strcmp(dai_link->name, "ETDM2_IN_BE") == 0) { + dai_link->codecs->name = + is5682s ? RT5682S_DEV0_NAME : RT5682_DEV0_NAME; + dai_link->codecs->dai_name = + is5682s ? RT5682S_CODEC_DAI : RT5682_CODEC_DAI; } } -- cgit v1.2.3 From e733ab7e3e5dc1bf7d34e050e839fc902ce7ff98 Mon Sep 17 00:00:00 2001 From: Bernard Zhao Date: Sun, 5 Dec 2021 18:11:00 -0800 Subject: sound/soc: remove useless bool conversion to bool variable This patch remove useless bool conversion to bool variable Signed-off-by: Bernard Zhao Link: https://lore.kernel.org/r/20211206021100.321170-1-bernard@vivo.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l35.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c index 7a5588f1df01..961a3e07e70f 100644 --- a/sound/soc/codecs/cs35l35.c +++ b/sound/soc/codecs/cs35l35.c @@ -1311,7 +1311,7 @@ static int cs35l35_handle_of_data(struct i2c_client *i2c_client, pdata->gain_zc = of_property_read_bool(np, "cirrus,amp-gain-zc"); classh = of_get_child_by_name(np, "cirrus,classh-internal-algo"); - classh_config->classh_algo_enable = classh ? true : false; + classh_config->classh_algo_enable = (classh != NULL); if (classh_config->classh_algo_enable) { classh_config->classh_bst_override = -- cgit v1.2.3 From c686316ec1210d43653c91e104c1e4cd0156dc89 Mon Sep 17 00:00:00 2001 From: Ameer Hamza Date: Mon, 6 Dec 2021 01:42:00 +0500 Subject: ASoC: test-component: fix null pointer dereference. Dereferncing of_id pointer will result in exception in current implementation since of_match_device() will assign it to NULL. Adding NULL check for protection. Signed-off-by: Ameer Hamza Link: https://lore.kernel.org/r/20211205204200.7852-1-amhamza.mgc@gmail.com Signed-off-by: Mark Brown --- sound/soc/generic/test-component.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sound/soc/generic/test-component.c b/sound/soc/generic/test-component.c index 85385a771d80..8fc97d3ff011 100644 --- a/sound/soc/generic/test-component.c +++ b/sound/soc/generic/test-component.c @@ -532,13 +532,16 @@ static int test_driver_probe(struct platform_device *pdev) struct device_node *node = dev->of_node; struct device_node *ep; const struct of_device_id *of_id = of_match_device(test_of_match, &pdev->dev); - const struct test_adata *adata = of_id->data; + const struct test_adata *adata; struct snd_soc_component_driver *cdriv; struct snd_soc_dai_driver *ddriv; struct test_dai_name *dname; struct test_priv *priv; int num, ret, i; + if (!of_id) + return -EINVAL; + adata = of_id->data; num = of_graph_get_endpoint_count(node); if (!num) { dev_err(dev, "no port exits\n"); -- cgit v1.2.3 From 766cc7f12078fe80dd88469e3dfe045e49bdf2bb Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 2 Dec 2021 22:48:38 +0200 Subject: ASoC: zl38060: Setup parent device and get rid of unnecessary of_node assignment Some of the drivers do not set parent device. This may lead to obstacles during debugging or understanding the device relations from the Linux point of view. Assign parent device for GPIO chips created by these drivers. While at it, let GPIO library to assign of_node from the parent device. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20211202204838.75287-1-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/zl38060.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/soc/codecs/zl38060.c b/sound/soc/codecs/zl38060.c index d20ec1571010..6cae0fb08093 100644 --- a/sound/soc/codecs/zl38060.c +++ b/sound/soc/codecs/zl38060.c @@ -589,9 +589,7 @@ static int zl38_spi_probe(struct spi_device *spi) sizeof(template_chip), GFP_KERNEL); if (!priv->gpio_chip) return -ENOMEM; -#ifdef CONFIG_OF_GPIO - priv->gpio_chip->of_node = dev->of_node; -#endif + priv->gpio_chip->parent = dev; err = devm_gpiochip_add_data(dev, priv->gpio_chip, priv->regmap); if (err) return err; -- cgit v1.2.3 From 4db32072b8ab18a8b90191c57c74f42d00bf9991 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 2 Dec 2021 22:56:11 +0200 Subject: ASoC: ti: davinci-mcasp: Get rid of duplicate of_node assignment GPIO library does copy the of_node from the parent device of the GPIO chip, there is no need to repeat this in the individual drivers. Remove assignment here. For the details one may look into the of_gpio_dev_init() implementation. Signed-off-by: Andy Shevchenko Acked-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20211202205612.76216-1-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/ti/davinci-mcasp.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index 56a19eeec5c7..81c1ccec5904 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -2230,9 +2230,6 @@ static int davinci_mcasp_init_gpiochip(struct davinci_mcasp *mcasp) mcasp->gpio_chip = davinci_mcasp_template_chip; mcasp->gpio_chip.label = dev_name(mcasp->dev); mcasp->gpio_chip.parent = mcasp->dev; -#ifdef CONFIG_OF_GPIO - mcasp->gpio_chip.of_node = mcasp->dev->of_node; -#endif return devm_gpiochip_add_data(mcasp->dev, &mcasp->gpio_chip, mcasp); } -- cgit v1.2.3 From c1a77ba466c0dd0bdf1ec2bbebb8996d7cd7b8f7 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 2 Dec 2021 22:56:12 +0200 Subject: ASoC: ti: davinci-mcasp: Remove unnecessary conditional Instead of double validating of_node, return value of the boolean property directly. We can't remove ifdeffery, because in OF_GPIO=n cases it might bring unwanted surprises. Signed-off-by: Andy Shevchenko Acked-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20211202205612.76216-2-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/ti/davinci-mcasp.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index 81c1ccec5904..3e105caac95e 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -1870,12 +1870,10 @@ err1: static bool davinci_mcasp_have_gpiochip(struct davinci_mcasp *mcasp) { #ifdef CONFIG_OF_GPIO - if (mcasp->dev->of_node && - of_property_read_bool(mcasp->dev->of_node, "gpio-controller")) - return true; -#endif - + return of_property_read_bool(mcasp->dev->of_node, "gpio-controller"); +#else return false; +#endif } static int davinci_mcasp_get_config(struct davinci_mcasp *mcasp, -- cgit v1.2.3 From 9a83dfcc5ae8230fbf12b63e281d5bb8450ec0e7 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Fri, 3 Dec 2021 17:47:21 +0200 Subject: ASoC: SOF: Intel: fix build issue related to CODEC_PROBE_ENTRIES Fix following error: sound/soc/sof/intel/hda-codec.c:132:35: error: use of undeclared identifier 'CODEC_PROBE_RETRIES' Found with config: i386-randconfig-r033-20211202 (https://download.01.org/0day-ci/archive/20211203/202112031943.Twg19fWT-lkp@intel.com/config) Fixes: 046aede2f847 ("ASoC: SOF: Intel: Retry codec probing if it fails") Reported-by: kernel test robot Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211203154721.923496-1-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-codec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/intel/hda-codec.c b/sound/soc/sof/intel/hda-codec.c index 13cd96e6724a..2f3f4a733d9e 100644 --- a/sound/soc/sof/intel/hda-codec.c +++ b/sound/soc/sof/intel/hda-codec.c @@ -20,9 +20,10 @@ #include "../../codecs/hdac_hda.h" #endif /* CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC */ +#define CODEC_PROBE_RETRIES 3 + #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) #define IDISP_VID_INTEL 0x80860000 -#define CODEC_PROBE_RETRIES 3 /* load the legacy HDA codec driver */ static int request_codec_module(struct hda_codec *codec) -- cgit v1.2.3 From 7bef00106bc68beddcddcd06e3b02dde5525face Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Sat, 4 Dec 2021 12:08:48 +0100 Subject: ASoC: amd: acp6x-pdm-dma: Constify static snd_soc_dai_ops The only usage of acp6x_pdm_dai_ops is to assign its address to the ops field in the snd_soc_dai_driver struct, which is a pointer to const snd_soc_dai_ops. Make it const to allow the compiler to put it in read-only memory. Signed-off-by: Rikard Falkeborn Link: https://lore.kernel.org/r/20211204110848.21322-1-rikard.falkeborn@gmail.com Signed-off-by: Mark Brown --- sound/soc/amd/yc/acp6x-pdm-dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/amd/yc/acp6x-pdm-dma.c b/sound/soc/amd/yc/acp6x-pdm-dma.c index e604f4ea524f..7e66393e4153 100644 --- a/sound/soc/amd/yc/acp6x-pdm-dma.c +++ b/sound/soc/amd/yc/acp6x-pdm-dma.c @@ -318,7 +318,7 @@ static int acp6x_pdm_dai_trigger(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_dai_ops acp6x_pdm_dai_ops = { +static const struct snd_soc_dai_ops acp6x_pdm_dai_ops = { .trigger = acp6x_pdm_dai_trigger, }; -- cgit v1.2.3 From 629e442761bae0c62b2fb14061d66bbd08b4155e Mon Sep 17 00:00:00 2001 From: Trevor Wu Date: Mon, 29 Nov 2021 22:10:55 +0800 Subject: ASoC: mediatek: mt8195: add model property This patch adds the description of model property used to specify card name from dts. Signed-off-by: Trevor Wu Acked-by: Rob Herring Link: https://lore.kernel.org/r/20211129141057.12422-3-trevor.wu@mediatek.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/mt8195-mt6359-rt1011-rt5682.yaml | 4 ++++ .../devicetree/bindings/sound/mt8195-mt6359-rt1019-rt5682.yaml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/mt8195-mt6359-rt1011-rt5682.yaml b/Documentation/devicetree/bindings/sound/mt8195-mt6359-rt1011-rt5682.yaml index d354c30d3377..cf6ad7933e23 100644 --- a/Documentation/devicetree/bindings/sound/mt8195-mt6359-rt1011-rt5682.yaml +++ b/Documentation/devicetree/bindings/sound/mt8195-mt6359-rt1011-rt5682.yaml @@ -16,6 +16,10 @@ properties: compatible: const: mediatek,mt8195_mt6359_rt1011_rt5682 + model: + $ref: /schemas/types.yaml#/definitions/string + description: User specified audio sound card name + mediatek,platform: $ref: "/schemas/types.yaml#/definitions/phandle" description: The phandle of MT8195 ASoC platform. diff --git a/Documentation/devicetree/bindings/sound/mt8195-mt6359-rt1019-rt5682.yaml b/Documentation/devicetree/bindings/sound/mt8195-mt6359-rt1019-rt5682.yaml index 20bc0ffd0e34..e6786dece9a3 100644 --- a/Documentation/devicetree/bindings/sound/mt8195-mt6359-rt1019-rt5682.yaml +++ b/Documentation/devicetree/bindings/sound/mt8195-mt6359-rt1019-rt5682.yaml @@ -16,6 +16,10 @@ properties: compatible: const: mediatek,mt8195_mt6359_rt1019_rt5682 + model: + $ref: /schemas/types.yaml#/definitions/string + description: User specified audio sound card name + mediatek,platform: $ref: "/schemas/types.yaml#/definitions/phandle" description: The phandle of MT8195 ASoC platform. -- cgit v1.2.3 From 3d00d2c07f04f47aa4228700b440ac47abf13853 Mon Sep 17 00:00:00 2001 From: Trevor Wu Date: Mon, 29 Nov 2021 22:10:56 +0800 Subject: ASoC: mediatek: mt8195: add sof support on mt8195-mt6359-rt1019-rt5682 In the patch, widgets, routes and dai-link requrird by SOF are included, and late_probe is introduced for SOF route connection. Only when adsp phandle could be retrieved from DTS, the SOF related part of machine driver is executed. Additionally, supported dai-links could be specified from DTS, so that we can disable AP side hardware controls when DSP SOF controls the same audio FE. Signed-off-by: Trevor Wu Signed-off-by: YC Hung Link: https://lore.kernel.org/r/20211129141057.12422-4-trevor.wu@mediatek.com Signed-off-by: Mark Brown --- .../mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c | 317 ++++++++++++++++++++- 1 file changed, 305 insertions(+), 12 deletions(-) diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c index c33b69b4f8e7..b240610dcef0 100644 --- a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c +++ b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c @@ -1,11 +1,12 @@ // SPDX-License-Identifier: GPL-2.0 -// -// mt8195-mt6359-rt1019-rt5682.c -- -// MT8195-MT6359-RT1019-RT6358 ALSA SoC machine driver -// -// Copyright (c) 2021 MediaTek Inc. -// Author: Trevor Wu -// +/* + * mt8195-mt6359-rt1019-rt5682.c -- + * MT8195-MT6359-RT1019-RT5682 ALSA SoC machine driver + * + * Copyright (c) 2021 MediaTek Inc. + * Author: Trevor Wu + * YC Hung + */ #include #include @@ -13,6 +14,7 @@ #include #include #include +#include #include #include "../../codecs/mt6359.h" #include "../../codecs/rt5682.h" @@ -28,8 +30,21 @@ #define RT5682S_CODEC_DAI "rt5682s-aif1" #define RT5682S_DEV0_NAME "rt5682s.2-001a" +#define SOF_DMA_DL2 "SOF_DMA_DL2" +#define SOF_DMA_DL3 "SOF_DMA_DL3" +#define SOF_DMA_UL4 "SOF_DMA_UL4" +#define SOF_DMA_UL5 "SOF_DMA_UL5" + +struct sof_conn_stream { + const char *normal_link; + const char *sof_link; + const char *sof_dma; + int stream_dir; +}; + struct mt8195_mt6359_rt1019_rt5682_priv { struct device_node *platform_node; + struct device_node *adsp_node; struct device_node *hdmi_node; struct device_node *dp_node; struct snd_soc_jack headset_jack; @@ -42,6 +57,10 @@ static const struct snd_soc_dapm_widget SND_SOC_DAPM_SPK("Speakers", NULL), SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIXER(SOF_DMA_DL2, SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER(SOF_DMA_DL3, SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER(SOF_DMA_UL4, SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER(SOF_DMA_UL5, SND_SOC_NOPM, 0, 0, NULL, 0), }; static const struct snd_soc_dapm_route mt8195_mt6359_rt1019_rt5682_routes[] = { @@ -51,6 +70,16 @@ static const struct snd_soc_dapm_route mt8195_mt6359_rt1019_rt5682_routes[] = { { "Headphone Jack", NULL, "HPOL" }, { "Headphone Jack", NULL, "HPOR" }, { "IN1P", NULL, "Headset Mic" }, + /* SOF Uplink */ + {SOF_DMA_UL4, NULL, "O034"}, + {SOF_DMA_UL4, NULL, "O035"}, + {SOF_DMA_UL5, NULL, "O036"}, + {SOF_DMA_UL5, NULL, "O037"}, + /* SOF Downlink */ + {"I070", NULL, SOF_DMA_DL2}, + {"I071", NULL, SOF_DMA_DL2}, + {"I020", NULL, SOF_DMA_DL3}, + {"I021", NULL, SOF_DMA_DL3}, }; static const struct snd_kcontrol_new mt8195_mt6359_rt1019_rt5682_controls[] = { @@ -562,8 +591,17 @@ enum { DAI_LINK_PCM1_BE, DAI_LINK_UL_SRC1_BE, DAI_LINK_UL_SRC2_BE, + DAI_LINK_REGULAR_LAST = DAI_LINK_UL_SRC2_BE, + DAI_LINK_SOF_START, + DAI_LINK_SOF_DL2_BE = DAI_LINK_SOF_START, + DAI_LINK_SOF_DL3_BE, + DAI_LINK_SOF_UL4_BE, + DAI_LINK_SOF_UL5_BE, + DAI_LINK_SOF_END = DAI_LINK_SOF_UL5_BE, }; +#define DAI_LINK_REGULAR_NUM (DAI_LINK_REGULAR_LAST + 1) + /* FE */ SND_SOC_DAILINK_DEFS(DL2_FE, DAILINK_COMP_ARRAY(COMP_CPU("DL2")), @@ -702,6 +740,154 @@ SND_SOC_DAILINK_DEFS(UL_SRC2_BE, "mt6359-snd-codec-aif2")), DAILINK_COMP_ARRAY(COMP_EMPTY())); +SND_SOC_DAILINK_DEFS(AFE_SOF_DL2, + DAILINK_COMP_ARRAY(COMP_CPU("SOF_DL2")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(AFE_SOF_DL3, + DAILINK_COMP_ARRAY(COMP_CPU("SOF_DL3")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(AFE_SOF_UL4, + DAILINK_COMP_ARRAY(COMP_CPU("SOF_UL4")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(AFE_SOF_UL5, + DAILINK_COMP_ARRAY(COMP_CPU("SOF_UL5")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +static const struct sof_conn_stream g_sof_conn_streams[] = { + { "ETDM2_OUT_BE", "AFE_SOF_DL2", SOF_DMA_DL2, SNDRV_PCM_STREAM_PLAYBACK}, + { "ETDM1_OUT_BE", "AFE_SOF_DL3", SOF_DMA_DL3, SNDRV_PCM_STREAM_PLAYBACK}, + { "UL_SRC1_BE", "AFE_SOF_UL4", SOF_DMA_UL4, SNDRV_PCM_STREAM_CAPTURE}, + { "ETDM2_IN_BE", "AFE_SOF_UL5", SOF_DMA_UL5, SNDRV_PCM_STREAM_CAPTURE}, +}; + +/* fixup the BE DAI link to match any values from topology */ +static int mt8195_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_card *card = rtd->card; + struct snd_soc_dai_link *sof_dai_link = NULL; + struct snd_soc_pcm_runtime *runtime; + struct snd_soc_dai *cpu_dai; + int i, j, ret = 0; + + for (i = 0; i < ARRAY_SIZE(g_sof_conn_streams); i++) { + const struct sof_conn_stream *conn = &g_sof_conn_streams[i]; + + if (strcmp(rtd->dai_link->name, conn->normal_link)) + continue; + + for_each_card_rtds(card, runtime) { + if (strcmp(runtime->dai_link->name, conn->sof_link)) + continue; + + for_each_rtd_cpu_dais(runtime, j, cpu_dai) { + if (cpu_dai->stream_active[conn->stream_dir] > 0) { + sof_dai_link = runtime->dai_link; + break; + } + } + break; + } + + if (sof_dai_link && sof_dai_link->be_hw_params_fixup) + ret = sof_dai_link->be_hw_params_fixup(runtime, params); + + break; + } + + if (!strcmp(rtd->dai_link->name, "ETDM2_IN_BE") || + !strcmp(rtd->dai_link->name, "ETDM1_OUT_BE")) { + mt8195_etdm_hw_params_fixup(runtime, params); + } + + return ret; +} + +static int mt8195_mt6359_rt1019_rt5682_card_late_probe(struct snd_soc_card *card) +{ + struct snd_soc_pcm_runtime *runtime; + struct snd_soc_component *sof_comp; + int i; + + /* 1. find sof component */ + for_each_card_rtds(card, runtime) { + for (i = 0; i < runtime->num_components; i++) { + if (!runtime->components[i]->driver->name) + continue; + if (!strcmp(runtime->components[i]->driver->name, "sof-audio-component")) { + sof_comp = runtime->components[i]; + break; + } + } + } + + if (!sof_comp) { + dev_info(card->dev, " probe without component\n"); + return 0; + } + /* 2. add route path and fixup callback */ + for (i = 0; i < ARRAY_SIZE(g_sof_conn_streams); i++) { + const struct sof_conn_stream *conn = &g_sof_conn_streams[i]; + struct snd_soc_pcm_runtime *sof_rtd = NULL; + struct snd_soc_pcm_runtime *normal_rtd = NULL; + struct snd_soc_pcm_runtime *rtd = NULL; + + for_each_card_rtds(card, rtd) { + if (!strcmp(rtd->dai_link->name, conn->sof_link)) { + sof_rtd = rtd; + continue; + } + if (!strcmp(rtd->dai_link->name, conn->normal_link)) { + normal_rtd = rtd; + continue; + } + if (normal_rtd && sof_rtd) + break; + } + if (normal_rtd && sof_rtd) { + int j; + struct snd_soc_dai *cpu_dai; + + for_each_rtd_cpu_dais(sof_rtd, j, cpu_dai) { + struct snd_soc_dapm_route route; + struct snd_soc_dapm_path *p = NULL; + struct snd_soc_dapm_widget *play_widget = + cpu_dai->playback_widget; + struct snd_soc_dapm_widget *cap_widget = + cpu_dai->capture_widget; + memset(&route, 0, sizeof(route)); + if (conn->stream_dir == SNDRV_PCM_STREAM_CAPTURE && + cap_widget) { + snd_soc_dapm_widget_for_each_sink_path(cap_widget, p) { + route.source = conn->sof_dma; + route.sink = p->sink->name; + snd_soc_dapm_add_routes(&card->dapm, &route, 1); + } + } else if (conn->stream_dir == SNDRV_PCM_STREAM_PLAYBACK && + play_widget){ + snd_soc_dapm_widget_for_each_source_path(play_widget, p) { + route.source = p->source->name; + route.sink = conn->sof_dma; + snd_soc_dapm_add_routes(&card->dapm, &route, 1); + } + } else { + dev_err(cpu_dai->dev, "stream dir and widget not pair\n"); + } + } + normal_rtd->dai_link->be_hw_params_fixup = mt8195_dai_link_fixup; + } + } + + return 0; +} + static struct snd_soc_dai_link mt8195_mt6359_rt1019_rt5682_dai_links[] = { /* FE */ [DAI_LINK_DL2_FE] = { @@ -896,7 +1082,6 @@ static struct snd_soc_dai_link mt8195_mt6359_rt1019_rt5682_dai_links[] = { /* BE */ [DAI_LINK_DL_SRC_BE] = { .name = "DL_SRC_BE", - .init = mt8195_mt6359_init, .no_pcm = 1, .dpcm_playback = 1, SND_SOC_DAILINK_REG(DL_SRC_BE), @@ -980,6 +1165,31 @@ static struct snd_soc_dai_link mt8195_mt6359_rt1019_rt5682_dai_links[] = { .dpcm_capture = 1, SND_SOC_DAILINK_REG(UL_SRC2_BE), }, + /* SOF BE */ + [DAI_LINK_SOF_DL2_BE] = { + .name = "AFE_SOF_DL2", + .no_pcm = 1, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(AFE_SOF_DL2), + }, + [DAI_LINK_SOF_DL3_BE] = { + .name = "AFE_SOF_DL3", + .no_pcm = 1, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(AFE_SOF_DL3), + }, + [DAI_LINK_SOF_UL4_BE] = { + .name = "AFE_SOF_UL4", + .no_pcm = 1, + .dpcm_capture = 1, + SND_SOC_DAILINK_REG(AFE_SOF_UL4), + }, + [DAI_LINK_SOF_UL5_BE] = { + .name = "AFE_SOF_UL5", + .no_pcm = 1, + .dpcm_capture = 1, + SND_SOC_DAILINK_REG(AFE_SOF_UL5), + }, }; static struct snd_soc_card mt8195_mt6359_rt1019_rt5682_soc_card = { @@ -995,12 +1205,61 @@ static struct snd_soc_card mt8195_mt6359_rt1019_rt5682_soc_card = { .num_dapm_routes = ARRAY_SIZE(mt8195_mt6359_rt1019_rt5682_routes), }; +static int mt8195_dailink_parse_of(struct snd_soc_card *card, struct device_node *np, + const char *propname) +{ + struct device *dev = card->dev; + struct snd_soc_dai_link *link; + const char *dai_name = NULL; + int i, j, ret, num_links; + + num_links = of_property_count_strings(np, "mediatek,dai-link"); + + if (num_links < 0 || num_links > ARRAY_SIZE(mt8195_mt6359_rt1019_rt5682_dai_links)) { + dev_dbg(dev, "number of dai-link is invalid\n"); + return -EINVAL; + } + + card->dai_link = devm_kcalloc(dev, num_links, sizeof(*link), GFP_KERNEL); + if (!card->dai_link) + return -ENOMEM; + + card->num_links = 0; + link = card->dai_link; + + for (i = 0; i < num_links; i++) { + ret = of_property_read_string_index(np, propname, i, &dai_name); + if (ret) { + dev_dbg(dev, "ASoC: Property '%s' index %d could not be read: %d\n", + propname, i, ret); + return -EINVAL; + } + + for (j = 0; j < ARRAY_SIZE(mt8195_mt6359_rt1019_rt5682_dai_links); j++) { + if (!strcmp(dai_name, mt8195_mt6359_rt1019_rt5682_dai_links[j].name)) { + memcpy(link, &mt8195_mt6359_rt1019_rt5682_dai_links[j], + sizeof(struct snd_soc_dai_link)); + link++; + card->num_links++; + break; + } + } + } + + if (card->num_links != num_links) + return -EINVAL; + + return 0; +} + static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev) { struct snd_soc_card *card = &mt8195_mt6359_rt1019_rt5682_soc_card; struct snd_soc_dai_link *dai_link; struct mt8195_mt6359_rt1019_rt5682_priv *priv; int is5682s = 0; + int init6359 = 0; + int sof_on = 0; int ret, i; card->dev = &pdev->dev; @@ -1026,15 +1285,36 @@ static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev) return -EINVAL; } + /* dai link */ + priv->adsp_node = of_parse_phandle(pdev->dev.of_node, + "mediatek,adsp", 0); + if (priv->adsp_node) + sof_on = 1; + + if (of_property_read_bool(pdev->dev.of_node, "mediatek,dai-link")) { + ret = mt8195_dailink_parse_of(card, pdev->dev.of_node, + "mediatek,dai-link"); + if (ret) { + dev_dbg(&pdev->dev, "Parse dai-link fail\n"); + return -EINVAL; + } + } else { + if (!sof_on) + card->num_links = DAI_LINK_REGULAR_NUM; + } + for_each_card_prelinks(card, i, dai_link) { - if (!dai_link->platforms->name) - dai_link->platforms->of_node = priv->platform_node; + if (!dai_link->platforms->name) { + if (!strncmp(dai_link->name, "AFE_SOF", strlen("AFE_SOF")) && sof_on) + dai_link->platforms->of_node = priv->adsp_node; + else + dai_link->platforms->of_node = priv->platform_node; + } if (strcmp(dai_link->name, "DPTX_BE") == 0) { priv->dp_node = of_parse_phandle(pdev->dev.of_node, "mediatek,dptx-codec", 0); - if (!priv->dp_node) { dev_dbg(&pdev->dev, "No property 'dptx-codec'\n"); } else { @@ -1061,9 +1341,19 @@ static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev) is5682s ? RT5682S_DEV0_NAME : RT5682_DEV0_NAME; dai_link->codecs->dai_name = is5682s ? RT5682S_CODEC_DAI : RT5682_CODEC_DAI; + } else if (strcmp(dai_link->name, "DL_SRC_BE") == 0 || + strcmp(dai_link->name, "UL_SRC1_BE") == 0 || + strcmp(dai_link->name, "UL_SRC2_BE") == 0) { + if (!init6359) { + dai_link->init = mt8195_mt6359_init; + init6359 = 1; + } } } + if (sof_on) + card->late_probe = mt8195_mt6359_rt1019_rt5682_card_late_probe; + snd_soc_card_set_drvdata(card, priv); ret = devm_snd_soc_register_card(&pdev->dev, card); @@ -1073,6 +1363,7 @@ static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev) of_node_put(priv->hdmi_node); of_node_put(priv->dp_node); of_node_put(priv->platform_node); + of_node_put(priv->adsp_node); } return ret; @@ -1087,6 +1378,7 @@ static int mt8195_mt6359_rt1019_rt5682_dev_remove(struct platform_device *pdev) of_node_put(priv->hdmi_node); of_node_put(priv->dp_node); of_node_put(priv->platform_node); + of_node_put(priv->adsp_node); return 0; } @@ -1120,5 +1412,6 @@ module_platform_driver(mt8195_mt6359_rt1019_rt5682_driver); /* Module information */ MODULE_DESCRIPTION("MT8195-MT6359-RT1019-RT5682 ALSA SoC machine driver"); MODULE_AUTHOR("Trevor Wu "); -MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("YC Hung "); +MODULE_LICENSE("GPL"); MODULE_ALIAS("mt8195_mt6359_rt1019_rt5682 soc card"); -- cgit v1.2.3 From 6182ec4616d6ffc046bea798c683a0dee11ded67 Mon Sep 17 00:00:00 2001 From: Trevor Wu Date: Mon, 29 Nov 2021 22:10:57 +0800 Subject: ASoC: mediatek: mt8195: add adsp and dai-link property 1. adsp phandle can be assigned to the machine driver if adsp is enabled. 2. dai-link supported in the sound card can be specified from DTS. Signed-off-by: Trevor Wu Link: https://lore.kernel.org/r/20211129141057.12422-5-trevor.wu@mediatek.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/mt8195-mt6359-rt1019-rt5682.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/mt8195-mt6359-rt1019-rt5682.yaml b/Documentation/devicetree/bindings/sound/mt8195-mt6359-rt1019-rt5682.yaml index e6786dece9a3..8f177e02ad35 100644 --- a/Documentation/devicetree/bindings/sound/mt8195-mt6359-rt1019-rt5682.yaml +++ b/Documentation/devicetree/bindings/sound/mt8195-mt6359-rt1019-rt5682.yaml @@ -32,6 +32,16 @@ properties: $ref: "/schemas/types.yaml#/definitions/phandle" description: The phandle of MT8195 HDMI codec node. + mediatek,adsp: + $ref: "/schemas/types.yaml#/definitions/phandle" + description: The phandle of MT8195 ADSP platform. + + mediatek,dai-link: + $ref: /schemas/types.yaml#/definitions/string-array + description: + A list of the desired dai-links in the sound card. Each entry is a + name defined in the machine driver. + additionalProperties: false required: -- cgit v1.2.3 From 2da636247bb6f4fc3a9842ade04757790753fd2c Mon Sep 17 00:00:00 2001 From: Trevor Wu Date: Tue, 30 Nov 2021 13:39:05 +0800 Subject: ASoC: mediatek: mt8195: add memory-region property Add a required property "memory-region", which is used to specify memory for DMA usage. Signed-off-by: Trevor Wu Link: https://lore.kernel.org/r/20211130053905.28470-3-trevor.wu@mediatek.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/mt8195-afe-pcm.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/mt8195-afe-pcm.yaml b/Documentation/devicetree/bindings/sound/mt8195-afe-pcm.yaml index dcf790b053d2..6d0975b33d15 100644 --- a/Documentation/devicetree/bindings/sound/mt8195-afe-pcm.yaml +++ b/Documentation/devicetree/bindings/sound/mt8195-afe-pcm.yaml @@ -19,6 +19,12 @@ properties: interrupts: maxItems: 1 + memory-region: + maxItems: 1 + description: | + Shared memory region for AFE memif. A "shared-dma-pool". + See ../reserved-memory/reserved-memory.txt for details. + mediatek,topckgen: $ref: "/schemas/types.yaml#/definitions/phandle" description: The phandle of the mediatek topckgen controller @@ -125,6 +131,7 @@ required: - power-domains - clocks - clock-names + - memory-region additionalProperties: false @@ -139,6 +146,7 @@ examples: interrupts = ; mediatek,topckgen = <&topckgen>; power-domains = <&spm 7>; //MT8195_POWER_DOMAIN_AUDIO + memory-region = <&snd_dma_mem_reserved>; clocks = <&clk26m>, <&topckgen 163>, //CLK_TOP_APLL1 <&topckgen 166>, //CLK_TOP_APLL2 -- cgit v1.2.3 From c7d58971dbea0888b6328ed0ea61089a6d62253a Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 6 Dec 2021 22:29:41 -0800 Subject: ALSA: mixart: Reduce size of mixart_timer_notify The mixart_timer_notify structure was larger than could be represented by the mixart_msg_data array storage. Adjust the size to as large as possible to fix the warning seen with -Warray-bounds builds: sound/pci/mixart/mixart_core.c: In function 'snd_mixart_threaded_irq': sound/pci/mixart/mixart_core.c:447:50: error: array subscript 'struct mixart_timer_notify[0]' is partly outside array bounds of 'u32[128]' {aka 'unsigned int[128]'} [-Werror=array-bounds] 447 | for(i=0; istream_count; i++) { | ^~ sound/pci/mixart/mixart_core.c:328:12: note: while referencing 'mixart_msg_data' 328 | static u32 mixart_msg_data[MSG_DEFAULT_SIZE / 4]; | ^~~~~~~~~~~~~~~ Signed-off-by: Kees Cook Link: https://lore.kernel.org/r/20211207062941.2413679-1-keescook@chromium.org Signed-off-by: Takashi Iwai --- sound/pci/mixart/mixart_core.c | 3 +-- sound/pci/mixart/mixart_core.h | 10 +++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/sound/pci/mixart/mixart_core.c b/sound/pci/mixart/mixart_core.c index fb8895af0363..853083dd4bad 100644 --- a/sound/pci/mixart/mixart_core.c +++ b/sound/pci/mixart/mixart_core.c @@ -23,8 +23,6 @@ #define MSG_DESCRIPTOR_SIZE 0x24 #define MSG_HEADER_SIZE (MSG_DESCRIPTOR_SIZE + 4) -#define MSG_DEFAULT_SIZE 512 - #define MSG_TYPE_MASK 0x00000003 /* mask for following types */ #define MSG_TYPE_NOTIFY 0 /* embedded -> driver (only notification, do not get_msg() !) */ #define MSG_TYPE_COMMAND 1 /* driver <-> embedded (a command has no answer) */ @@ -444,6 +442,7 @@ irqreturn_t snd_mixart_threaded_irq(int irq, void *dev_id) struct mixart_timer_notify *notify; notify = (struct mixart_timer_notify *)mixart_msg_data; + BUILD_BUG_ON(sizeof(notify) > sizeof(mixart_msg_data)); for(i=0; istream_count; i++) { u32 buffer_id = notify->streams[i].buffer_id; diff --git a/sound/pci/mixart/mixart_core.h b/sound/pci/mixart/mixart_core.h index fbf4731a276d..2f0e29ed5d63 100644 --- a/sound/pci/mixart/mixart_core.h +++ b/sound/pci/mixart/mixart_core.h @@ -49,6 +49,7 @@ enum mixart_message_id { MSG_CLOCK_SET_PROPERTIES = 0x200002, }; +#define MSG_DEFAULT_SIZE 512 struct mixart_msg { @@ -251,10 +252,17 @@ struct mixart_sample_pos u32 sample_pos_low_part; } __attribute__((packed)); +/* + * This structure is limited by the size of MSG_DEFAULT_SIZE. Instead of + * having MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS many streams, + * this is capped to have a total size below MSG_DEFAULT_SIZE. + */ +#define MIXART_MAX_TIMER_NOTIFY_STREAMS \ + ((MSG_DEFAULT_SIZE - sizeof(u32)) / sizeof(struct mixart_sample_pos)) struct mixart_timer_notify { u32 stream_count; - struct mixart_sample_pos streams[MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS]; + struct mixart_sample_pos streams[MIXART_MAX_TIMER_NOTIFY_STREAMS]; } __attribute__((packed)); -- cgit v1.2.3 From a98478f825862ddc1686a3335f9f1cc278fc5733 Mon Sep 17 00:00:00 2001 From: Anders Roxell Date: Tue, 7 Dec 2021 12:00:53 +0100 Subject: ALSA: ppc: beep: fix clang -Wimplicit-fallthrough Clang warns: sound/ppc/beep.c:103:2: warning: unannotated fall-through between switch labels [-Wimplicit-fallthrough] case SND_TONE: break; ^ sound/ppc/beep.c:103:2: note: insert 'break;' to avoid fall-through case SND_TONE: break; ^ break; 1 warning generated. Clang is more pedantic than GCC, which does not warn when failing through to a case that is just break or return. Clang's version is more in line with the kernel's own stance in deprecated.rst. Add athe missing break to silence the warning. Reported-by: Naresh Kamboju Signed-off-by: Anders Roxell Link: https://lore.kernel.org/r/20211207110053.695712-1-anders.roxell@linaro.org Signed-off-by: Takashi Iwai --- sound/ppc/beep.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/ppc/beep.c b/sound/ppc/beep.c index 0f4bce1c0d4f..bf289783eafd 100644 --- a/sound/ppc/beep.c +++ b/sound/ppc/beep.c @@ -99,7 +99,7 @@ static int snd_pmac_beep_event(struct input_dev *dev, unsigned int type, return -1; switch (code) { - case SND_BELL: if (hz) hz = 1000; + case SND_BELL: if (hz) hz = 1000; break; case SND_TONE: break; default: return -1; } -- cgit v1.2.3 From 403c521003a1364fd2d7c01a2a1f66ed025fb94a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 7 Dec 2021 16:33:23 +0100 Subject: ALSA: mixart: Add sanity check for timer notify streams The miXart timer notification is a variable length, and if a hardware is screwed up, we may access over the actual data size. Let's add a sanity check and bail out if an invalid value is received. Link: https://lore.kernel.org/r/20211207153323.27098-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/mixart/mixart_core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/pci/mixart/mixart_core.c b/sound/pci/mixart/mixart_core.c index 853083dd4bad..a047ed0f84e9 100644 --- a/sound/pci/mixart/mixart_core.c +++ b/sound/pci/mixart/mixart_core.c @@ -443,6 +443,8 @@ irqreturn_t snd_mixart_threaded_irq(int irq, void *dev_id) notify = (struct mixart_timer_notify *)mixart_msg_data; BUILD_BUG_ON(sizeof(notify) > sizeof(mixart_msg_data)); + if (snd_BUG_ON(notify->stream_count > ARRAY_SIZE(notify->streams))) + break; for(i=0; istream_count; i++) { u32 buffer_id = notify->streams[i].buffer_id; -- cgit v1.2.3 From 6fadb494a638d8b8a55864ecc6ac58194f03f327 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 7 Dec 2021 17:51:46 +0100 Subject: ALSA: seq: Set upper limit of processed events Currently ALSA sequencer core tries to process the queued events as much as possible when they become dispatchable. If applications try to queue too massive events to be processed at the very same timing, the sequencer core would still try to process such all events, either in the interrupt context or via some notifier; in either away, it might be a cause of RCU stall or such problems. As a potential workaround for those problems, this patch adds the upper limit of the amount of events to be processed. The remaining events are processed in the next batch, so they won't be lost. For the time being, it's limited up to 1000 events per queue, which should be high enough for any normal usages. Reported-by: Zqiang Reported-by: syzbot+bb950e68b400ab4f65f8@syzkaller.appspotmail.com Link: https://lore.kernel.org/r/20211102033222.3849-1-qiang.zhang1211@gmail.com Link: https://lore.kernel.org/r/20211207165146.2888-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/core/seq/seq_queue.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c index d6c02dea976c..bc933104c3ee 100644 --- a/sound/core/seq/seq_queue.c +++ b/sound/core/seq/seq_queue.c @@ -235,12 +235,15 @@ struct snd_seq_queue *snd_seq_queue_find_name(char *name) /* -------------------------------------------------------- */ +#define MAX_CELL_PROCESSES_IN_QUEUE 1000 + void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop) { unsigned long flags; struct snd_seq_event_cell *cell; snd_seq_tick_time_t cur_tick; snd_seq_real_time_t cur_time; + int processed = 0; if (q == NULL) return; @@ -263,6 +266,8 @@ void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop) if (!cell) break; snd_seq_dispatch_event(cell, atomic, hop); + if (++processed >= MAX_CELL_PROCESSES_IN_QUEUE) + goto out; /* the rest processed at the next batch */ } /* Process time queue... */ @@ -272,14 +277,19 @@ void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop) if (!cell) break; snd_seq_dispatch_event(cell, atomic, hop); + if (++processed >= MAX_CELL_PROCESSES_IN_QUEUE) + goto out; /* the rest processed at the next batch */ } + out: /* free lock */ spin_lock_irqsave(&q->check_lock, flags); if (q->check_again) { q->check_again = 0; - spin_unlock_irqrestore(&q->check_lock, flags); - goto __again; + if (processed < MAX_CELL_PROCESSES_IN_QUEUE) { + spin_unlock_irqrestore(&q->check_lock, flags); + goto __again; + } } q->check_blocked = 0; spin_unlock_irqrestore(&q->check_lock, flags); -- cgit v1.2.3 From 639cd58be7a4bfdf3514877b064b3308bb7800ba Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 7 Dec 2021 15:17:00 -0600 Subject: ASoC: Intel: boards: add 'static' qualifiers for max98390 routes Sparse warnings: sound/soc/intel/boards/sof_maxim_common.c:140:33: error: symbol 'max_98390_dapm_routes' was not declared. Should it be static? sound/soc/intel/boards/sof_maxim_common.c:156:33: error: symbol 'max_98390_tt_dapm_routes' was not declared. Should it be static? Fixes: f316c9d9ba8ea ('ASoC: Intel: boards: add max98390 2/4 speakers support') Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20211207211700.115319-1-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_maxim_common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/boards/sof_maxim_common.c b/sound/soc/intel/boards/sof_maxim_common.c index 9171d9cd179e..112e89951da0 100644 --- a/sound/soc/intel/boards/sof_maxim_common.c +++ b/sound/soc/intel/boards/sof_maxim_common.c @@ -137,7 +137,7 @@ EXPORT_SYMBOL_NS(max_98373_set_codec_conf, SND_SOC_INTEL_SOF_MAXIM_COMMON); /* * Maxim MAX98390 */ -const struct snd_soc_dapm_route max_98390_dapm_routes[] = { +static const struct snd_soc_dapm_route max_98390_dapm_routes[] = { /* speaker */ { "Left Spk", NULL, "Left BE_OUT" }, { "Right Spk", NULL, "Right BE_OUT" }, @@ -153,7 +153,7 @@ static const struct snd_soc_dapm_widget max_98390_tt_dapm_widgets[] = { SND_SOC_DAPM_SPK("TR Spk", NULL), }; -const struct snd_soc_dapm_route max_98390_tt_dapm_routes[] = { +static const struct snd_soc_dapm_route max_98390_tt_dapm_routes[] = { /* Tweeter speaker */ { "TL Spk", NULL, "Tweeter Left BE_OUT" }, { "TR Spk", NULL, "Tweeter Right BE_OUT" }, -- cgit v1.2.3 From 7cfa3d00730a4c0694b55fb1974823baeab8815b Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Wed, 8 Dec 2021 18:17:18 +0800 Subject: ASoC: rt5682s: add delay time to fix pop sound issue There is a pop noise at the beginning of the capture data. This patch adds the delay time before stereo1 ADC unmute to fix the pop sound issue. Signed-off-by: Shuming Fan Link: https://lore.kernel.org/r/20211208101718.28945-1-shumingf@realtek.com Signed-off-by: Mark Brown --- include/sound/rt5682s.h | 1 + sound/soc/codecs/rt5682s.c | 34 +++++++++++++++++++++++++++++++--- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/include/sound/rt5682s.h b/include/sound/rt5682s.h index accfbc2dcdd2..f18d91308b9a 100644 --- a/include/sound/rt5682s.h +++ b/include/sound/rt5682s.h @@ -40,6 +40,7 @@ struct rt5682s_platform_data { enum rt5682s_jd_src jd_src; unsigned int dmic_clk_rate; unsigned int dmic_delay; + unsigned int amic_delay; bool dmic_clk_driving_high; const char *dai_clk_names[RT5682S_DAI_NUM_CLKS]; diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c index d49a4f68566d..efa1016831dd 100644 --- a/sound/soc/codecs/rt5682s.c +++ b/sound/soc/codecs/rt5682s.c @@ -1367,6 +1367,31 @@ static int rt5682s_hp_amp_event(struct snd_soc_dapm_widget *w, return 0; } +static int rt5682s_stereo1_adc_mixl_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); + unsigned int delay = 0; + + if (rt5682s->pdata.amic_delay) + delay = rt5682s->pdata.amic_delay; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + msleep(delay); + snd_soc_component_update_bits(component, RT5682S_STO1_ADC_DIG_VOL, + RT5682S_L_MUTE, 0); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, RT5682S_STO1_ADC_DIG_VOL, + RT5682S_L_MUTE, RT5682S_L_MUTE); + break; + } + + return 0; +} + static int sar_power_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -1680,9 +1705,10 @@ static const struct snd_soc_dapm_widget rt5682s_dapm_widgets[] = { /* ADC Mixer */ SND_SOC_DAPM_SUPPLY("ADC Stereo1 Filter", RT5682S_PWR_DIG_2, RT5682S_PWR_ADC_S1F_BIT, 0, set_filter_clk, SND_SOC_DAPM_PRE_PMU), - SND_SOC_DAPM_MIXER("Stereo1 ADC MIXL", RT5682S_STO1_ADC_DIG_VOL, - RT5682S_L_MUTE_SFT, 1, rt5682s_sto1_adc_l_mix, - ARRAY_SIZE(rt5682s_sto1_adc_l_mix)), + SND_SOC_DAPM_MIXER_E("Stereo1 ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5682s_sto1_adc_l_mix, ARRAY_SIZE(rt5682s_sto1_adc_l_mix), + rt5682s_stereo1_adc_mixl_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", RT5682S_STO1_ADC_DIG_VOL, RT5682S_R_MUTE_SFT, 1, rt5682s_sto1_adc_r_mix, ARRAY_SIZE(rt5682s_sto1_adc_r_mix)), @@ -2885,6 +2911,8 @@ static int rt5682s_parse_dt(struct rt5682s_priv *rt5682s, struct device *dev) &rt5682s->pdata.dmic_clk_rate); device_property_read_u32(dev, "realtek,dmic-delay-ms", &rt5682s->pdata.dmic_delay); + device_property_read_u32(dev, "realtek,amic-delay-ms", + &rt5682s->pdata.amic_delay); rt5682s->pdata.ldo1_en = of_get_named_gpio(dev->of_node, "realtek,ldo1-en-gpios", 0); -- cgit v1.2.3 From 77659872be233e56019041d05b44d134022296b7 Mon Sep 17 00:00:00 2001 From: Yong Zhi Date: Tue, 7 Dec 2021 13:24:58 -0600 Subject: ASoC: Intel: sof_rt5682: Move rt1015 speaker amp to common file Move rt1015 driver code to common file to be consistent with rt1011 and rt1015p. No functional change. Reviewed-by: Bard Liao Signed-off-by: Yong Zhi Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20211207192458.44007-1-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_realtek_common.c | 119 +++++++++++++++++++++++++++- sound/soc/intel/boards/sof_realtek_common.h | 7 ++ sound/soc/intel/boards/sof_rt5682.c | 107 +------------------------ 3 files changed, 127 insertions(+), 106 deletions(-) diff --git a/sound/soc/intel/boards/sof_realtek_common.c b/sound/soc/intel/boards/sof_realtek_common.c index 2ec34f8df9e1..4cf131310ad3 100644 --- a/sound/soc/intel/boards/sof_realtek_common.c +++ b/sound/soc/intel/boards/sof_realtek_common.c @@ -12,12 +12,13 @@ #include #include #include "../../codecs/rt1011.h" +#include "../../codecs/rt1015.h" #include "sof_realtek_common.h" /* * Current only 2-amp configuration is supported for rt1011 */ -static const struct snd_soc_dapm_route rt1011_dapm_routes[] = { +static const struct snd_soc_dapm_route speaker_map_lr[] = { /* speaker */ { "Left Spk", NULL, "Left SPO" }, { "Right Spk", NULL, "Right SPO" }, @@ -117,8 +118,8 @@ static int rt1011_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_card *card = rtd->card; int ret; - ret = snd_soc_dapm_add_routes(&card->dapm, rt1011_dapm_routes, - ARRAY_SIZE(rt1011_dapm_routes)); + ret = snd_soc_dapm_add_routes(&card->dapm, speaker_map_lr, + ARRAY_SIZE(speaker_map_lr)); if (ret) dev_err(rtd->dev, "Speaker map addition failed: %d\n", ret); return ret; @@ -241,3 +242,115 @@ void sof_rt1015p_codec_conf(struct snd_soc_card *card) card->codec_conf = rt1015p_codec_confs; card->num_configs = ARRAY_SIZE(rt1015p_codec_confs); } + +/* + * RT1015 audio amplifier + */ + +static int rt1015_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai; + int i, fs = 64, ret; + + for_each_rtd_codec_dais(rtd, i, codec_dai) { + ret = snd_soc_dai_set_pll(codec_dai, 0, RT1015_PLL_S_BCLK, + params_rate(params) * fs, + params_rate(params) * 256); + if (ret) + return ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, RT1015_SCLK_S_PLL, + params_rate(params) * 256, + SND_SOC_CLOCK_IN); + if (ret) + return ret; + } + + return 0; +} + +static int rt1015_hw_params_pll_and_tdm(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai; + int i, fs = 100, ret; + + for_each_rtd_codec_dais(rtd, i, codec_dai) { + ret = snd_soc_dai_set_pll(codec_dai, 0, RT1015_PLL_S_BCLK, + params_rate(params) * fs, + params_rate(params) * 256); + if (ret) + return ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, RT1015_SCLK_S_PLL, + params_rate(params) * 256, + SND_SOC_CLOCK_IN); + if (ret) + return ret; + } + /* rx slot 1 for RT1015_DEV0_NAME */ + ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_codec(rtd, 0), + 0x0, 0x1, 4, 24); + if (ret) + return ret; + + /* rx slot 2 for RT1015_DEV1_NAME */ + ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_codec(rtd, 1), + 0x0, 0x2, 4, 24); + if (ret) + return ret; + + return 0; +} + +static struct snd_soc_ops rt1015_ops = { + .hw_params = rt1015_hw_params, +}; + +static struct snd_soc_codec_conf rt1015_amp_conf[] = { + { + .dlc = COMP_CODEC_CONF(RT1015_DEV0_NAME), + .name_prefix = "Left", + }, + { + .dlc = COMP_CODEC_CONF(RT1015_DEV1_NAME), + .name_prefix = "Right", + }, +}; + +static struct snd_soc_dai_link_component rt1015_components[] = { + { + .name = RT1015_DEV0_NAME, + .dai_name = RT1015_CODEC_DAI, + }, + { + .name = RT1015_DEV1_NAME, + .dai_name = RT1015_CODEC_DAI, + }, +}; + +static int speaker_codec_init_lr(struct snd_soc_pcm_runtime *rtd) +{ + return snd_soc_dapm_add_routes(&rtd->card->dapm, speaker_map_lr, + ARRAY_SIZE(speaker_map_lr)); +} + +void sof_rt1015_codec_conf(struct snd_soc_card *card) +{ + card->codec_conf = rt1015_amp_conf; + card->num_configs = ARRAY_SIZE(rt1015_amp_conf); +} + +void sof_rt1015_dai_link(struct snd_soc_dai_link *link, unsigned int fs) +{ + link->codecs = rt1015_components; + link->num_codecs = ARRAY_SIZE(rt1015_components); + link->init = speaker_codec_init_lr; + link->ops = &rt1015_ops; + + if (fs == 100) + rt1015_ops.hw_params = rt1015_hw_params_pll_and_tdm; +} diff --git a/sound/soc/intel/boards/sof_realtek_common.h b/sound/soc/intel/boards/sof_realtek_common.h index cb0b49b2855c..228ac9c08430 100644 --- a/sound/soc/intel/boards/sof_realtek_common.h +++ b/sound/soc/intel/boards/sof_realtek_common.h @@ -28,4 +28,11 @@ void sof_rt1011_codec_conf(struct snd_soc_card *card); void sof_rt1015p_dai_link(struct snd_soc_dai_link *link); void sof_rt1015p_codec_conf(struct snd_soc_card *card); +#define RT1015_CODEC_DAI "rt1015-aif" +#define RT1015_DEV0_NAME "i2c-10EC1015:00" +#define RT1015_DEV1_NAME "i2c-10EC1015:01" + +void sof_rt1015_dai_link(struct snd_soc_dai_link *link, unsigned int fs); +void sof_rt1015_codec_conf(struct snd_soc_card *card); + #endif /* __SOF_REALTEK_COMMON_H */ diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index 6cadb5fb72e0..bd6d2e7dea53 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -20,7 +20,6 @@ #include #include #include -#include "../../codecs/rt1015.h" #include "../../codecs/rt5682.h" #include "../../codecs/rt5682s.h" #include "../../codecs/hdac_hdmi.h" @@ -400,67 +399,6 @@ static struct snd_soc_ops sof_rt5682_ops = { .hw_params = sof_rt5682_hw_params, }; -static int sof_rt1015_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_soc_card *card = rtd->card; - struct snd_soc_dai *codec_dai; - int i, fs, ret; - - if (!snd_soc_card_get_codec_dai(card, "rt1015-aif")) - return 0; - - if (sof_rt5682_quirk & SOF_RT1015_SPEAKER_AMP_100FS) - fs = 100; - else - fs = 64; - - for_each_rtd_codec_dais(rtd, i, codec_dai) { - ret = snd_soc_dai_set_pll(codec_dai, 0, RT1015_PLL_S_BCLK, - params_rate(params) * fs, - params_rate(params) * 256); - if (ret < 0) { - dev_err(card->dev, "failed to set pll\n"); - return ret; - } - /* Configure sysclk for codec */ - ret = snd_soc_dai_set_sysclk(codec_dai, RT1015_SCLK_S_PLL, - params_rate(params) * 256, - SND_SOC_CLOCK_IN); - if (ret < 0) { - dev_err(card->dev, "failed to set sysclk\n"); - return ret; - } - - if (sof_rt5682_quirk & SOF_RT1015_SPEAKER_AMP_100FS) { - if (!strcmp(codec_dai->component->name, "i2c-10EC1015:00")) { - ret = snd_soc_dai_set_tdm_slot(codec_dai, - 0x0, 0x1, 4, 24); - if (ret < 0) { - dev_err(card->dev, "failed to set tdm slot\n"); - return ret; - } - } - - if (!strcmp(codec_dai->component->name, "i2c-10EC1015:01")) { - ret = snd_soc_dai_set_tdm_slot(codec_dai, - 0x0, 0x2, 4, 24); - if (ret < 0) { - dev_err(card->dev, "failed to set tdm slot\n"); - return ret; - } - } - } - } - - return 0; -} - -static struct snd_soc_ops sof_rt1015_ops = { - .hw_params = sof_rt1015_hw_params, -}; - static struct snd_soc_dai_link_component platform_component[] = { { /* name might be overridden during probe */ @@ -551,22 +489,11 @@ static const struct snd_soc_dapm_route sof_map[] = { { "IN1P", NULL, "Headset Mic" }, }; -static const struct snd_soc_dapm_route speaker_map_lr[] = { - { "Left Spk", NULL, "Left SPO" }, - { "Right Spk", NULL, "Right SPO" }, -}; - static const struct snd_soc_dapm_route dmic_map[] = { /* digital mics */ {"DMic", NULL, "SoC DMIC"}, }; -static int speaker_codec_init_lr(struct snd_soc_pcm_runtime *rtd) -{ - return snd_soc_dapm_add_routes(&rtd->card->dapm, speaker_map_lr, - ARRAY_SIZE(speaker_map_lr)); -} - static int dmic_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; @@ -589,17 +516,6 @@ static int dmic_init(struct snd_soc_pcm_runtime *rtd) return ret; } -static struct snd_soc_codec_conf rt1015_amp_conf[] = { - { - .dlc = COMP_CODEC_CONF("i2c-10EC1015:00"), - .name_prefix = "Left", - }, - { - .dlc = COMP_CODEC_CONF("i2c-10EC1015:01"), - .name_prefix = "Right", - }, -}; - /* sof audio machine driver for rt5682 codec */ static struct snd_soc_card sof_audio_card_rt5682 = { .name = "rt5682", /* the sof- prefix is added by the core */ @@ -635,17 +551,6 @@ static struct snd_soc_dai_link_component dmic_component[] = { } }; -static struct snd_soc_dai_link_component rt1015_components[] = { - { - .name = "i2c-10EC1015:00", - .dai_name = "rt1015-aif", - }, - { - .name = "i2c-10EC1015:01", - .dai_name = "rt1015-aif", - }, -}; - static struct snd_soc_dai_link_component dummy_component[] = { { .name = "snd-soc-dummy", @@ -798,10 +703,8 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, links[id].id = id; if (sof_rt5682_quirk & SOF_RT1015_SPEAKER_AMP_PRESENT) { - links[id].codecs = rt1015_components; - links[id].num_codecs = ARRAY_SIZE(rt1015_components); - links[id].init = speaker_codec_init_lr; - links[id].ops = &sof_rt1015_ops; + sof_rt1015_dai_link(&links[id], (sof_rt5682_quirk & + SOF_RT1015_SPEAKER_AMP_100FS) ? 100 : 64); } else if (sof_rt5682_quirk & SOF_RT1015P_SPEAKER_AMP_PRESENT) { sof_rt1015p_dai_link(&links[id]); } else if (sof_rt5682_quirk & @@ -995,10 +898,8 @@ static int sof_audio_probe(struct platform_device *pdev) sof_audio_card_rt5682.dai_link = dai_links; - if (sof_rt5682_quirk & SOF_RT1015_SPEAKER_AMP_PRESENT) { - sof_audio_card_rt5682.codec_conf = rt1015_amp_conf; - sof_audio_card_rt5682.num_configs = ARRAY_SIZE(rt1015_amp_conf); - } + if (sof_rt5682_quirk & SOF_RT1015_SPEAKER_AMP_PRESENT) + sof_rt1015_codec_conf(&sof_audio_card_rt5682); INIT_LIST_HEAD(&ctx->hdmi_pcm_list); -- cgit v1.2.3 From 6c7ac18cd82108a0cd58e21b9814503e631dbb5d Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Wed, 8 Dec 2021 18:16:54 +0800 Subject: ASoC: dt-bindings: rt5682s: add AMIC delay time property Add the AMIC delay time to control how much delay time (ms) to unmute the stereo1 ADC. Signed-off-by: Shuming Fan Link: https://lore.kernel.org/r/20211208101654.28925-1-shumingf@realtek.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/realtek,rt5682s.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/realtek,rt5682s.yaml b/Documentation/devicetree/bindings/sound/realtek,rt5682s.yaml index 2b8b7b51fe55..d65c0ed5060c 100644 --- a/Documentation/devicetree/bindings/sound/realtek,rt5682s.yaml +++ b/Documentation/devicetree/bindings/sound/realtek,rt5682s.yaml @@ -61,6 +61,10 @@ properties: description: | Set the delay time (ms) for the requirement of the particular DMIC. + realtek,amic-delay-ms: + description: | + Set the delay time (ms) for the requirement of the particular platform or AMIC. + realtek,dmic-clk-driving-high: type: boolean description: | -- cgit v1.2.3 From d9b994cd7641ad8eda97aa8633f4e2f35d7d0a79 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 7 Dec 2021 13:23:09 -0600 Subject: ASoC: AMD: acp-config: fix missing dependency on SND_SOC_ACPI With a custom .config, the following error is thrown: ERROR: modpost: "snd_soc_acpi_codec_list" [sound/soc/amd/snd-acp-config.ko] undefined! Fix by adding a clear dependency on SND_SOC_ACPI Reported-by: kernel test robot Signed-off-by: Pierre-Louis Bossart Reviewed-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211207192309.43883-1-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/amd/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig index 8961b8fd23eb..bcfeb3fc2592 100644 --- a/sound/soc/amd/Kconfig +++ b/sound/soc/amd/Kconfig @@ -98,6 +98,7 @@ config SND_SOC_AMD_YC_MACH config SND_AMD_ACP_CONFIG tristate "AMD ACP configuration selection" + depends on SND_SOC_ACPI help This option adds an auto detection to determine which ACP driver modules to use -- cgit v1.2.3 From 9abc21c966619d6ead27fd48481966014fdc680f Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 8 Dec 2021 18:11:45 +0300 Subject: ASoC: mediatek: mt8195: silence uninitialized variable warning Smatch complains that we might hit the continue path on every iteration through the loop. sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c:831 mt8195_mt6359_rt1019_rt5682_card_late_probe() error: uninitialized symbol 'sof_comp'. Initialize "sof_comp" to NULL to silence this warning. Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/20211208151145.GA29257@kili Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c index b240610dcef0..11a185da0d96 100644 --- a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c +++ b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c @@ -813,7 +813,7 @@ static int mt8195_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, static int mt8195_mt6359_rt1019_rt5682_card_late_probe(struct snd_soc_card *card) { struct snd_soc_pcm_runtime *runtime; - struct snd_soc_component *sof_comp; + struct snd_soc_component *sof_comp = NULL; int i; /* 1. find sof component */ -- cgit v1.2.3 From 62df22396bea321435153cdba37585ad8ff9c567 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 15 Sep 2021 19:09:57 +0100 Subject: ASoC: amd: Convert to new style DAI format definitions Convert the AMD machine drivers to use the new style defines for clocking in DAI formats. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20210915180957.39996-1-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/amd/acp-da7219-max98357a.c | 20 ++++++++++---------- sound/soc/amd/acp-rt5645.c | 4 ++-- sound/soc/amd/acp3x-rt5682-max9836.c | 8 ++++---- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c index b2065f3fe42c..3bf86c2424ae 100644 --- a/sound/soc/amd/acp-da7219-max98357a.c +++ b/sound/soc/amd/acp-da7219-max98357a.c @@ -522,7 +522,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = { .name = "amd-da7219-play", .stream_name = "Playback", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .init = cz_da7219_init, .dpcm_playback = 1, .stop_dma_first = 1, @@ -533,7 +533,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = { .name = "amd-da7219-cap", .stream_name = "Capture", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .dpcm_capture = 1, .stop_dma_first = 1, .ops = &cz_da7219_cap_ops, @@ -543,7 +543,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = { .name = "amd-max98357-play", .stream_name = "HiFi Playback", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .dpcm_playback = 1, .stop_dma_first = 1, .ops = &cz_max_play_ops, @@ -554,7 +554,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = { .name = "dmic0", .stream_name = "DMIC0 Capture", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .dpcm_capture = 1, .stop_dma_first = 1, .ops = &cz_dmic0_cap_ops, @@ -565,7 +565,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = { .name = "dmic1", .stream_name = "DMIC1 Capture", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .dpcm_capture = 1, .stop_dma_first = 1, .ops = &cz_dmic1_cap_ops, @@ -578,7 +578,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = { .name = "amd-rt5682-play", .stream_name = "Playback", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .init = cz_rt5682_init, .dpcm_playback = 1, .stop_dma_first = 1, @@ -589,7 +589,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = { .name = "amd-rt5682-cap", .stream_name = "Capture", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .dpcm_capture = 1, .stop_dma_first = 1, .ops = &cz_rt5682_cap_ops, @@ -599,7 +599,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = { .name = "amd-max98357-play", .stream_name = "HiFi Playback", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .dpcm_playback = 1, .stop_dma_first = 1, .ops = &cz_rt5682_max_play_ops, @@ -610,7 +610,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = { .name = "dmic0", .stream_name = "DMIC0 Capture", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .dpcm_capture = 1, .stop_dma_first = 1, .ops = &cz_rt5682_dmic0_cap_ops, @@ -621,7 +621,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = { .name = "dmic1", .stream_name = "DMIC1 Capture", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .dpcm_capture = 1, .stop_dma_first = 1, .ops = &cz_rt5682_dmic1_cap_ops, diff --git a/sound/soc/amd/acp-rt5645.c b/sound/soc/amd/acp-rt5645.c index 6d5c547a32de..a79a46646d50 100644 --- a/sound/soc/amd/acp-rt5645.c +++ b/sound/soc/amd/acp-rt5645.c @@ -111,7 +111,7 @@ static struct snd_soc_dai_link cz_dai_rt5650[] = { .name = "amd-rt5645-play", .stream_name = "RT5645_AIF1", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .init = cz_init, .ops = &cz_aif1_ops, SND_SOC_DAILINK_REG(designware1, codec, platform), @@ -120,7 +120,7 @@ static struct snd_soc_dai_link cz_dai_rt5650[] = { .name = "amd-rt5645-cap", .stream_name = "RT5645_AIF1", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .ops = &cz_aif1_ops, SND_SOC_DAILINK_REG(designware2, codec, platform), }, diff --git a/sound/soc/amd/acp3x-rt5682-max9836.c b/sound/soc/amd/acp3x-rt5682-max9836.c index e561464f7d60..dad70436d063 100644 --- a/sound/soc/amd/acp3x-rt5682-max9836.c +++ b/sound/soc/amd/acp3x-rt5682-max9836.c @@ -51,7 +51,7 @@ static int acp3x_5682_init(struct snd_soc_pcm_runtime *rtd) /* set rt5682 dai fmt */ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM); + | SND_SOC_DAIFMT_CBP_CFP); if (ret < 0) { dev_err(rtd->card->dev, "Failed to set rt5682 dai fmt: %d\n", ret); @@ -302,7 +302,7 @@ static struct snd_soc_dai_link acp3x_dai[] = { .name = "acp3x-5682-play", .stream_name = "Playback", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .init = acp3x_5682_init, .dpcm_playback = 1, .dpcm_capture = 1, @@ -313,7 +313,7 @@ static struct snd_soc_dai_link acp3x_dai[] = { .name = "acp3x-max98357-play", .stream_name = "HiFi Playback", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBS_CFS, + | SND_SOC_DAIFMT_CBC_CFC, .dpcm_playback = 1, .ops = &acp3x_max_play_ops, .cpus = acp3x_bt, @@ -325,7 +325,7 @@ static struct snd_soc_dai_link acp3x_dai[] = { .name = "acp3x-ec-dmic0-capture", .stream_name = "Capture DMIC0", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBS_CFS, + | SND_SOC_DAIFMT_CBC_CFC, .dpcm_capture = 1, .ops = &acp3x_ec_cap0_ops, SND_SOC_DAILINK_REG(acp3x_bt, cros_ec, platform), -- cgit v1.2.3 From 5f9155a7d2dc067d72a95b42168f944c7710c0d5 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 6 Dec 2021 16:46:24 +0100 Subject: ASoC: dt-bindings: tegra: Document interconnects property Add the interconnects and interconnect-names properties to the bindings for the sound card on various NVIDIA Tegra based boards. These are used to describe the device's memory paths to and from memory. Signed-off-by: Thierry Reding Acked-by: Rob Herring Link: https://lore.kernel.org/r/20211206154624.229018-1-thierry.reding@gmail.com Signed-off-by: Mark Brown --- .../bindings/sound/nvidia,tegra-audio-graph-card.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-graph-card.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-graph-card.yaml index 5bdd30a8a404..b4bee466d67a 100644 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-graph-card.yaml +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-graph-card.yaml @@ -44,6 +44,16 @@ properties: minItems: 1 maxItems: 3 + interconnects: + items: + - description: APE read memory client + - description: APE write memory client + + interconnect-names: + items: + - const: dma-mem # read + - const: write + iommus: maxItems: 1 -- cgit v1.2.3 From befe304536eeef71f8529ff877444ae2b72a37db Mon Sep 17 00:00:00 2001 From: Ameer Hamza Date: Tue, 7 Dec 2021 19:23:09 +0500 Subject: ASoC: test-component: fix null pointer dereference. Dereferncing of_id pointer will result in exception in current implementation since of_match_device() will assign it to NULL. Adding NULL check for protection. Signed-off-by: Ameer Hamza Link: https://lore.kernel.org/r/20211207142309.222820-1-amhamza.mgc@gmail.com Signed-off-by: Mark Brown --- sound/soc/generic/test-component.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/sound/soc/generic/test-component.c b/sound/soc/generic/test-component.c index 8fc97d3ff011..5da4725d9e16 100644 --- a/sound/soc/generic/test-component.c +++ b/sound/soc/generic/test-component.c @@ -531,17 +531,13 @@ static int test_driver_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *node = dev->of_node; struct device_node *ep; - const struct of_device_id *of_id = of_match_device(test_of_match, &pdev->dev); - const struct test_adata *adata; + const struct test_adata *adata = of_device_get_match_data(&pdev->dev); struct snd_soc_component_driver *cdriv; struct snd_soc_dai_driver *ddriv; struct test_dai_name *dname; struct test_priv *priv; int num, ret, i; - if (!of_id) - return -EINVAL; - adata = of_id->data; num = of_graph_get_endpoint_count(node); if (!num) { dev_err(dev, "no port exits\n"); @@ -552,7 +548,7 @@ static int test_driver_probe(struct platform_device *pdev) cdriv = devm_kzalloc(dev, sizeof(*cdriv), GFP_KERNEL); ddriv = devm_kzalloc(dev, sizeof(*ddriv) * num, GFP_KERNEL); dname = devm_kzalloc(dev, sizeof(*dname) * num, GFP_KERNEL); - if (!priv || !cdriv || !ddriv || !dname) + if (!priv || !cdriv || !ddriv || !dname || !adata) return -EINVAL; priv->dev = dev; -- cgit v1.2.3 From 808709d7675dc0707a9fd6a08077c2b29dca0d60 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Sun, 12 Dec 2021 15:04:22 +0800 Subject: ALSA: sparc: no need to initialise statics to 0 Static variables do not need to be initialised to 0, because compiler will initialise all uninitialised statics to 0. Thus, remove the unneeded initializations. Signed-off-by: Jason Wang Link: https://lore.kernel.org/r/20211212070422.281924-1-wangborong@cdjrlc.com Signed-off-by: Takashi Iwai --- sound/sparc/dbri.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c index 6b84f66e4af4..3881e1c1b08a 100644 --- a/sound/sparc/dbri.c +++ b/sound/sparc/dbri.c @@ -688,7 +688,7 @@ static void dbri_cmdsend(struct snd_dbri *dbri, s32 *cmd, int len) { u32 dvma_addr = (u32)dbri->dma_dvma; s32 tmp, addr; - static int wait_id = 0; + static int wait_id; wait_id++; wait_id &= 0xffff; /* restrict it to a 16 bit counter. */ @@ -1926,7 +1926,7 @@ static void dbri_process_interrupt_buffer(struct snd_dbri *dbri) static irqreturn_t snd_dbri_interrupt(int irq, void *dev_id) { struct snd_dbri *dbri = dev_id; - static int errcnt = 0; + static int errcnt; int x; if (dbri == NULL) @@ -2591,7 +2591,7 @@ static int dbri_probe(struct platform_device *op) struct snd_dbri *dbri; struct resource *rp; struct snd_card *card; - static int dev = 0; + static int dev; int irq; int err; -- cgit v1.2.3 From 5aaf9efffc57ea31a13af6f0bf41e96f073ed6d5 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 10 Dec 2021 18:54:08 +0000 Subject: kselftest: alsa: Add simplistic test for ALSA mixer controls kselftest Add a basic test for the mixer control interface. For every control on every sound card in the system it checks that it can read and write the default value where the control supports that and for writeable controls attempts to write all valid values, restoring the default values after each test to minimise disruption for users. There are quite a few areas for improvement - currently no coverage of the generation of notifications, several of the control types don't have any coverage for the values and we don't have any testing of error handling when we attempt to write out of range values - but this provides some basic coverage. This is added as a kselftest since unlike other ALSA test programs it does not require either physical setup of the device or interactive monitoring by users and kselftest is one of the test suites that is frequently run by people doing general automated testing so should increase coverage. It is written in terms of alsa-lib since tinyalsa is not generally packaged for distributions which makes things harder for general users interested in kselftest as a whole but it will be a barrier to people with Android. Signed-off-by: Mark Brown Reviewed-by: Shuah Khan Link: https://lore.kernel.org/r/20211210185410.740009-2-broonie@kernel.org Signed-off-by: Takashi Iwai --- MAINTAINERS | 8 + tools/testing/selftests/Makefile | 3 +- tools/testing/selftests/alsa/.gitignore | 1 + tools/testing/selftests/alsa/Makefile | 9 + tools/testing/selftests/alsa/mixer-test.c | 605 ++++++++++++++++++++++++++++++ 5 files changed, 625 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/alsa/.gitignore create mode 100644 tools/testing/selftests/alsa/Makefile create mode 100644 tools/testing/selftests/alsa/mixer-test.c diff --git a/MAINTAINERS b/MAINTAINERS index 7a2345ce8521..8ded9f48b432 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17805,6 +17805,7 @@ F: Documentation/sound/ F: include/sound/ F: include/uapi/sound/ F: sound/ +F: tools/testing/selftests/alsa SOUND - COMPRESSED AUDIO M: Vinod Koul @@ -17824,6 +17825,13 @@ F: include/sound/dmaengine_pcm.h F: sound/core/pcm_dmaengine.c F: sound/soc/soc-generic-dmaengine-pcm.c +SOUND - ALSA SELFTESTS +M: Mark Brown +L: alsa-devel@alsa-project.org (moderated for non-subscribers) +L: linux-kselftest@vger.kernel.org +S: Supported +F: tools/testing/selftests/alsa + SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT (ASoC) M: Liam Girdwood M: Mark Brown diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index c852eb40c4f7..d08fe4cfe811 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 -TARGETS = arm64 +TARGETS += alsa +TARGETS += arm64 TARGETS += bpf TARGETS += breakpoints TARGETS += capabilities diff --git a/tools/testing/selftests/alsa/.gitignore b/tools/testing/selftests/alsa/.gitignore new file mode 100644 index 000000000000..3bb7c41266a8 --- /dev/null +++ b/tools/testing/selftests/alsa/.gitignore @@ -0,0 +1 @@ +mixer-test diff --git a/tools/testing/selftests/alsa/Makefile b/tools/testing/selftests/alsa/Makefile new file mode 100644 index 000000000000..f64d9090426d --- /dev/null +++ b/tools/testing/selftests/alsa/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 +# + +CFLAGS += $(shell pkg-config --cflags alsa) +LDLIBS += $(shell pkg-config --libs alsa) + +TEST_GEN_PROGS := mixer-test + +include ../lib.mk diff --git a/tools/testing/selftests/alsa/mixer-test.c b/tools/testing/selftests/alsa/mixer-test.c new file mode 100644 index 000000000000..ab51cf7b9e03 --- /dev/null +++ b/tools/testing/selftests/alsa/mixer-test.c @@ -0,0 +1,605 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// kselftest for the ALSA mixer API +// +// Original author: Mark Brown +// Copyright (c) 2021 Arm Limited + +// This test will iterate over all cards detected in the system, exercising +// every mixer control it can find. This may conflict with other system +// software if there is audio activity so is best run on a system with a +// minimal active userspace. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../kselftest.h" + +#define TESTS_PER_CONTROL 3 + +struct card_data { + snd_ctl_t *handle; + int card; + int num_ctls; + snd_ctl_elem_list_t *ctls; + struct card_data *next; +}; + +struct ctl_data { + const char *name; + snd_ctl_elem_id_t *id; + snd_ctl_elem_info_t *info; + snd_ctl_elem_value_t *def_val; + int elem; + struct card_data *card; + struct ctl_data *next; +}; + +int num_cards = 0; +int num_controls = 0; +struct card_data *card_list = NULL; +struct ctl_data *ctl_list = NULL; + +void find_controls(void) +{ + char name[32]; + int card, ctl, err; + struct card_data *card_data; + struct ctl_data *ctl_data; + + card = -1; + if (snd_card_next(&card) < 0 || card < 0) + return; + + while (card >= 0) { + sprintf(name, "hw:%d", card); + + card_data = malloc(sizeof(*card_data)); + if (!card_data) + ksft_exit_fail_msg("Out of memory\n"); + + err = snd_ctl_open(&card_data->handle, name, 0); + if (err < 0) { + ksft_print_msg("Failed to get hctl for card %d: %s\n", + card, snd_strerror(err)); + goto next_card; + } + + /* Count controls */ + snd_ctl_elem_list_malloc(&card_data->ctls); + snd_ctl_elem_list(card_data->handle, card_data->ctls); + card_data->num_ctls = snd_ctl_elem_list_get_count(card_data->ctls); + + /* Enumerate control information */ + snd_ctl_elem_list_alloc_space(card_data->ctls, card_data->num_ctls); + snd_ctl_elem_list(card_data->handle, card_data->ctls); + + card_data->card = num_cards++; + card_data->next = card_list; + card_list = card_data; + + num_controls += card_data->num_ctls; + + for (ctl = 0; ctl < card_data->num_ctls; ctl++) { + ctl_data = malloc(sizeof(*ctl_data)); + if (!ctl_data) + ksft_exit_fail_msg("Out of memory\n"); + + ctl_data->card = card_data; + ctl_data->elem = ctl; + ctl_data->name = snd_ctl_elem_list_get_name(card_data->ctls, + ctl); + + err = snd_ctl_elem_id_malloc(&ctl_data->id); + if (err < 0) + ksft_exit_fail_msg("Out of memory\n"); + + err = snd_ctl_elem_info_malloc(&ctl_data->info); + if (err < 0) + ksft_exit_fail_msg("Out of memory\n"); + + err = snd_ctl_elem_value_malloc(&ctl_data->def_val); + if (err < 0) + ksft_exit_fail_msg("Out of memory\n"); + + snd_ctl_elem_list_get_id(card_data->ctls, ctl, + ctl_data->id); + snd_ctl_elem_info_set_id(ctl_data->info, ctl_data->id); + err = snd_ctl_elem_info(card_data->handle, + ctl_data->info); + if (err < 0) { + ksft_print_msg("%s getting info for %d\n", + snd_strerror(err), + ctl_data->name); + } + + snd_ctl_elem_value_set_id(ctl_data->def_val, + ctl_data->id); + + ctl_data->next = ctl_list; + ctl_list = ctl_data; + } + + next_card: + if (snd_card_next(&card) < 0) { + ksft_print_msg("snd_card_next"); + break; + } + } +} + +/* + * Check that we can read the default value and it is valid. Write + * tests use the read value to restore the default. + */ +void test_ctl_get_value(struct ctl_data *ctl) +{ + int err; + long int_val; + long long int64_val; + + /* If the control is turned off let's be polite */ + if (snd_ctl_elem_info_is_inactive(ctl->info)) { + ksft_print_msg("%s is inactive\n", ctl->name); + ksft_test_result_skip("get_value.%d.%d\n", + ctl->card->card, ctl->elem); + return; + } + + /* Can't test reading on an unreadable control */ + if (!snd_ctl_elem_info_is_readable(ctl->info)) { + ksft_print_msg("%s is not readable\n", ctl->name); + ksft_test_result_skip("get_value.%d.%d\n", + ctl->card->card, ctl->elem); + return; + } + + err = snd_ctl_elem_read(ctl->card->handle, ctl->def_val); + if (err < 0) { + ksft_print_msg("snd_ctl_elem_read() failed: %s\n", + snd_strerror(err)); + goto out; + } + + switch (snd_ctl_elem_info_get_type(ctl->info)) { + case SND_CTL_ELEM_TYPE_NONE: + ksft_print_msg("%s Invalid control type NONE\n", ctl->name); + err = -1; + break; + + case SND_CTL_ELEM_TYPE_BOOLEAN: + int_val = snd_ctl_elem_value_get_boolean(ctl->def_val, 0); + switch (int_val) { + case 0: + case 1: + break; + default: + ksft_print_msg("%s Invalid boolean value %ld\n", + ctl->name, int_val); + err = -1; + break; + } + break; + + case SND_CTL_ELEM_TYPE_INTEGER: + int_val = snd_ctl_elem_value_get_integer(ctl->def_val, 0); + + if (int_val < snd_ctl_elem_info_get_min(ctl->info)) { + ksft_print_msg("%s value %ld less than minimum %ld\n", + ctl->name, int_val, + snd_ctl_elem_info_get_min(ctl->info)); + err = -1; + } + + if (int_val > snd_ctl_elem_info_get_max(ctl->info)) { + ksft_print_msg("%s value %ld more than maximum %ld\n", + ctl->name, int_val, + snd_ctl_elem_info_get_max(ctl->info)); + err = -1; + } + + /* Only check step size if there is one and we're in bounds */ + if (err >= 0 && snd_ctl_elem_info_get_step(ctl->info) && + (int_val - snd_ctl_elem_info_get_min(ctl->info) % + snd_ctl_elem_info_get_step(ctl->info))) { + ksft_print_msg("%s value %ld invalid for step %ld minimum %ld\n", + ctl->name, int_val, + snd_ctl_elem_info_get_step(ctl->info), + snd_ctl_elem_info_get_min(ctl->info)); + err = -1; + } + break; + + case SND_CTL_ELEM_TYPE_INTEGER64: + int64_val = snd_ctl_elem_value_get_integer64(ctl->def_val, 0); + + if (int64_val < snd_ctl_elem_info_get_min64(ctl->info)) { + ksft_print_msg("%s value %lld less than minimum %lld\n", + ctl->name, int64_val, + snd_ctl_elem_info_get_min64(ctl->info)); + err = -1; + } + + if (int64_val > snd_ctl_elem_info_get_max64(ctl->info)) { + ksft_print_msg("%s value %lld more than maximum %lld\n", + ctl->name, int64_val, + snd_ctl_elem_info_get_max(ctl->info)); + err = -1; + } + + /* Only check step size if there is one and we're in bounds */ + if (err >= 0 && snd_ctl_elem_info_get_step64(ctl->info) && + (int64_val - snd_ctl_elem_info_get_min64(ctl->info)) % + snd_ctl_elem_info_get_step64(ctl->info)) { + ksft_print_msg("%s value %lld invalid for step %lld minimum %lld\n", + ctl->name, int64_val, + snd_ctl_elem_info_get_step64(ctl->info), + snd_ctl_elem_info_get_min64(ctl->info)); + err = -1; + } + break; + + default: + /* No tests for other types */ + ksft_test_result_skip("get_value.%d.%d\n", + ctl->card->card, ctl->elem); + return; + } + +out: + ksft_test_result(err >= 0, "get_value.%d.%d\n", + ctl->card->card, ctl->elem); +} + +bool show_mismatch(struct ctl_data *ctl, int index, + snd_ctl_elem_value_t *read_val, + snd_ctl_elem_value_t *expected_val) +{ + long long expected_int, read_int; + + /* + * We factor out the code to compare values representable as + * integers, ensure that check doesn't log otherwise. + */ + expected_int = 0; + read_int = 0; + + switch (snd_ctl_elem_info_get_type(ctl->info)) { + case SND_CTL_ELEM_TYPE_BOOLEAN: + expected_int = snd_ctl_elem_value_get_boolean(expected_val, + index); + read_int = snd_ctl_elem_value_get_boolean(read_val, index); + break; + + case SND_CTL_ELEM_TYPE_INTEGER: + expected_int = snd_ctl_elem_value_get_integer(expected_val, + index); + read_int = snd_ctl_elem_value_get_integer(read_val, index); + break; + + case SND_CTL_ELEM_TYPE_INTEGER64: + expected_int = snd_ctl_elem_value_get_integer64(expected_val, + index); + read_int = snd_ctl_elem_value_get_integer64(read_val, + index); + break; + + case SND_CTL_ELEM_TYPE_ENUMERATED: + expected_int = snd_ctl_elem_value_get_enumerated(expected_val, + index); + read_int = snd_ctl_elem_value_get_enumerated(read_val, + index); + break; + + default: + break; + } + + if (expected_int != read_int) { + ksft_print_msg("%s.%d expected %lld but read %lld\n", + ctl->name, index, expected_int, read_int); + return true; + } else { + return false; + } +} + +/* + * Write a value then if possible verify that we get the expected + * result. An optional expected value can be provided if we expect + * the write to fail, for verifying that invalid writes don't corrupt + * anything. + */ +int write_and_verify(struct ctl_data *ctl, + snd_ctl_elem_value_t *write_val, + snd_ctl_elem_value_t *expected_val) +{ + int err, i; + bool error_expected, mismatch_shown; + snd_ctl_elem_value_t *read_val, *w_val; + snd_ctl_elem_value_alloca(&read_val); + snd_ctl_elem_value_alloca(&w_val); + + /* + * We need to copy the write value since writing can modify + * the value which causes surprises, and allocate an expected + * value if we expect to read back what we wrote. + */ + snd_ctl_elem_value_copy(w_val, write_val); + if (expected_val) { + error_expected = true; + } else { + error_expected = false; + snd_ctl_elem_value_alloca(&expected_val); + snd_ctl_elem_value_copy(expected_val, write_val); + } + + /* + * Do the write, if we have an expected value ignore the error + * and carry on to validate the expected value. + */ + err = snd_ctl_elem_write(ctl->card->handle, w_val); + if (err < 0 && !error_expected) { + ksft_print_msg("snd_ctl_elem_write() failed: %s\n", + snd_strerror(err)); + return err; + } + + /* Can we do the verification part? */ + if (!snd_ctl_elem_info_is_readable(ctl->info)) + return err; + + snd_ctl_elem_value_set_id(read_val, ctl->id); + + err = snd_ctl_elem_read(ctl->card->handle, read_val); + if (err < 0) { + ksft_print_msg("snd_ctl_elem_read() failed: %s\n", + snd_strerror(err)); + return err; + } + + /* + * Use the libray to compare values, if there's a mismatch + * carry on and try to provide a more useful diagnostic than + * just "mismatch". + */ + if (!snd_ctl_elem_value_compare(expected_val, read_val)) + return 0; + + mismatch_shown = false; + for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) + if (show_mismatch(ctl, i, read_val, expected_val)) + mismatch_shown = true; + + if (!mismatch_shown) + ksft_print_msg("%s read and written values differ\n", + ctl->name); + + return -1; +} + +/* + * Make sure we can write the default value back to the control, this + * should validate that at least some write works. + */ +void test_ctl_write_default(struct ctl_data *ctl) +{ + int err; + + /* If the control is turned off let's be polite */ + if (snd_ctl_elem_info_is_inactive(ctl->info)) { + ksft_print_msg("%s is inactive\n", ctl->name); + ksft_test_result_skip("write_default.%d.%d\n", + ctl->card->card, ctl->elem); + return; + } + + if (!snd_ctl_elem_info_is_writable(ctl->info)) { + ksft_print_msg("%s is not writeable\n", ctl->name); + ksft_test_result_skip("write_default.%d.%d\n", + ctl->card->card, ctl->elem); + return; + } + + /* No idea what the default was for unreadable controls */ + if (!snd_ctl_elem_info_is_readable(ctl->info)) { + ksft_print_msg("%s couldn't read default\n", ctl->name); + ksft_test_result_skip("write_default.%d.%d\n", + ctl->card->card, ctl->elem); + return; + } + + err = write_and_verify(ctl, ctl->def_val, NULL); + + ksft_test_result(err >= 0, "write_default.%d.%d\n", + ctl->card->card, ctl->elem); +} + +bool test_ctl_write_valid_boolean(struct ctl_data *ctl) +{ + int err, i, j; + bool fail = false; + snd_ctl_elem_value_t *val; + snd_ctl_elem_value_alloca(&val); + + snd_ctl_elem_value_set_id(val, ctl->id); + + for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { + for (j = 0; j < 2; j++) { + snd_ctl_elem_value_set_boolean(val, i, j); + err = write_and_verify(ctl, val, NULL); + if (err != 0) + fail = true; + } + } + + return !fail; +} + +bool test_ctl_write_valid_integer(struct ctl_data *ctl) +{ + int err; + int i; + long j, step; + bool fail = false; + snd_ctl_elem_value_t *val; + snd_ctl_elem_value_alloca(&val); + + snd_ctl_elem_value_set_id(val, ctl->id); + + step = snd_ctl_elem_info_get_step(ctl->info); + if (!step) + step = 1; + + for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { + for (j = snd_ctl_elem_info_get_min(ctl->info); + j <= snd_ctl_elem_info_get_max(ctl->info); j += step) { + + snd_ctl_elem_value_set_integer(val, i, j); + err = write_and_verify(ctl, val, NULL); + if (err != 0) + fail = true; + } + } + + + return !fail; +} + +bool test_ctl_write_valid_integer64(struct ctl_data *ctl) +{ + int err, i; + long long j, step; + bool fail = false; + snd_ctl_elem_value_t *val; + snd_ctl_elem_value_alloca(&val); + + snd_ctl_elem_value_set_id(val, ctl->id); + + step = snd_ctl_elem_info_get_step64(ctl->info); + if (!step) + step = 1; + + for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { + for (j = snd_ctl_elem_info_get_min64(ctl->info); + j <= snd_ctl_elem_info_get_max64(ctl->info); j += step) { + + snd_ctl_elem_value_set_integer64(val, i, j); + err = write_and_verify(ctl, val, NULL); + if (err != 0) + fail = true; + } + } + + return !fail; +} + +bool test_ctl_write_valid_enumerated(struct ctl_data *ctl) +{ + int err, i, j; + bool fail = false; + snd_ctl_elem_value_t *val; + snd_ctl_elem_value_alloca(&val); + + snd_ctl_elem_value_set_id(val, ctl->id); + + for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { + for (j = 0; j < snd_ctl_elem_info_get_items(ctl->info); j++) { + snd_ctl_elem_value_set_enumerated(val, i, j); + err = write_and_verify(ctl, val, NULL); + if (err != 0) + fail = true; + } + } + + return !fail; +} + +void test_ctl_write_valid(struct ctl_data *ctl) +{ + bool pass; + int err; + + /* If the control is turned off let's be polite */ + if (snd_ctl_elem_info_is_inactive(ctl->info)) { + ksft_print_msg("%s is inactive\n", ctl->name); + ksft_test_result_skip("write_valid.%d.%d\n", + ctl->card->card, ctl->elem); + return; + } + + if (!snd_ctl_elem_info_is_writable(ctl->info)) { + ksft_print_msg("%s is not writeable\n", ctl->name); + ksft_test_result_skip("write_valid.%d.%d\n", + ctl->card->card, ctl->elem); + return; + } + + switch (snd_ctl_elem_info_get_type(ctl->info)) { + case SND_CTL_ELEM_TYPE_BOOLEAN: + pass = test_ctl_write_valid_boolean(ctl); + break; + + case SND_CTL_ELEM_TYPE_INTEGER: + pass = test_ctl_write_valid_integer(ctl); + break; + + case SND_CTL_ELEM_TYPE_INTEGER64: + pass = test_ctl_write_valid_integer64(ctl); + break; + + case SND_CTL_ELEM_TYPE_ENUMERATED: + pass = test_ctl_write_valid_enumerated(ctl); + break; + + default: + /* No tests for this yet */ + ksft_test_result_skip("write_valid.%d.%d\n", + ctl->card->card, ctl->elem); + return; + } + + /* Restore the default value to minimise disruption */ + err = write_and_verify(ctl, ctl->def_val, NULL); + if (err < 0) + pass = false; + + ksft_test_result(pass, "write_valid.%d.%d\n", + ctl->card->card, ctl->elem); +} + +int main(void) +{ + struct ctl_data *ctl; + + ksft_print_header(); + + find_controls(); + + ksft_set_plan(num_controls * TESTS_PER_CONTROL); + + for (ctl = ctl_list; ctl != NULL; ctl = ctl->next) { + /* + * Must test get_value() before we write anything, the + * test stores the default value for later cleanup. + */ + test_ctl_get_value(ctl); + test_ctl_write_default(ctl); + test_ctl_write_valid(ctl); + } + + ksft_exit_pass(); + + return 0; +} -- cgit v1.2.3 From 7cc994f27e84cc94ce612d201c78763f93eab2c4 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 10 Dec 2021 18:54:09 +0000 Subject: kselftest: alsa: optimization for SNDRV_CTL_ELEM_ACCESS_VOLATILE The volatile attribute of control element means that the hardware can voluntarily change the state of control element independent of any operation by software. ALSA control core necessarily sends notification to userspace subscribers for any change from userspace application, while it doesn't for the hardware's voluntary change. This commit adds optimization for the attribute. Even if read value is different from written value, the test reports success as long as the target control element has the attribute. On the other hand, the difference is itself reported for developers' convenience. Signed-off-by: Takashi Sakamoto Link: https://lore.kernel.org/r/Ya7TAHdMe9i41bsC@workstation [Fix comment style as suggested by Shuah -- broonie] Reviewed-by: Shuah Khan Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20211210185410.740009-3-broonie@kernel.org Signed-off-by: Takashi Iwai --- tools/testing/selftests/alsa/mixer-test.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/alsa/mixer-test.c b/tools/testing/selftests/alsa/mixer-test.c index ab51cf7b9e03..f65a9046e708 100644 --- a/tools/testing/selftests/alsa/mixer-test.c +++ b/tools/testing/selftests/alsa/mixer-test.c @@ -307,9 +307,15 @@ bool show_mismatch(struct ctl_data *ctl, int index, } if (expected_int != read_int) { - ksft_print_msg("%s.%d expected %lld but read %lld\n", - ctl->name, index, expected_int, read_int); - return true; + /* + * NOTE: The volatile attribute means that the hardware + * can voluntarily change the state of control element + * independent of any operation by software. + */ + bool is_volatile = snd_ctl_elem_info_is_volatile(ctl->info); + ksft_print_msg("%s.%d expected %lld but read %lld, is_volatile %d\n", + ctl->name, index, expected_int, read_int, is_volatile); + return !is_volatile; } else { return false; } -- cgit v1.2.3 From b73dad806533cad55df41a9c0349969b56d4ff7f Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 10 Dec 2021 18:54:10 +0000 Subject: kselftest: alsa: Use private alsa-lib configuration in mixer test As mentined by Takashi Sakamoto, the system-wide alsa-lib configuration may override the standard device declarations. This patch use the private alsa-lib configuration to set the predictable environment. Signed-off-by: Jaroslav Kysela Link: https://lore.kernel.org/r/20211208095209.1772296-1-perex@perex.cz [Restructure version test to keep the preprocessor happy -- broonie] Reviewed-by: Shuah Khan Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20211210185410.740009-4-broonie@kernel.org Signed-off-by: Takashi Iwai --- tools/testing/selftests/alsa/mixer-test.c | 56 ++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/alsa/mixer-test.c b/tools/testing/selftests/alsa/mixer-test.c index f65a9046e708..b798a76f6825 100644 --- a/tools/testing/selftests/alsa/mixer-test.c +++ b/tools/testing/selftests/alsa/mixer-test.c @@ -46,22 +46,74 @@ struct ctl_data { struct ctl_data *next; }; +static const char *alsa_config = +"ctl.hw {\n" +" @args [ CARD ]\n" +" @args.CARD.type string\n" +" type hw\n" +" card $CARD\n" +"}\n" +; + int num_cards = 0; int num_controls = 0; struct card_data *card_list = NULL; struct ctl_data *ctl_list = NULL; +#ifdef SND_LIB_VER +#if SND_LIB_VERSION >= SND_LIB_VER(1, 2, 6) +#define LIB_HAS_LOAD_STRING +#endif +#endif + +#ifndef LIB_HAS_LOAD_STRING +int snd_config_load_string(snd_config_t **config, const char *s, size_t size) +{ + snd_input_t *input; + snd_config_t *dst; + int err; + + assert(config && s); + if (size == 0) + size = strlen(s); + err = snd_input_buffer_open(&input, s, size); + if (err < 0) + return err; + err = snd_config_top(&dst); + if (err < 0) { + snd_input_close(input); + return err; + } + err = snd_config_load(dst, input); + snd_input_close(input); + if (err < 0) { + snd_config_delete(dst); + return err; + } + *config = dst; + return 0; +} +#endif + void find_controls(void) { char name[32]; int card, ctl, err; struct card_data *card_data; struct ctl_data *ctl_data; + snd_config_t *config; card = -1; if (snd_card_next(&card) < 0 || card < 0) return; + err = snd_config_load_string(&config, alsa_config, strlen(alsa_config)); + if (err < 0) { + ksft_print_msg("Unable to parse custom alsa-lib configuration: %s\n", + snd_strerror(err)); + ksft_exit_fail(); + } + while (card >= 0) { sprintf(name, "hw:%d", card); @@ -69,7 +121,7 @@ void find_controls(void) if (!card_data) ksft_exit_fail_msg("Out of memory\n"); - err = snd_ctl_open(&card_data->handle, name, 0); + err = snd_ctl_open_lconf(&card_data->handle, name, 0, config); if (err < 0) { ksft_print_msg("Failed to get hctl for card %d: %s\n", card, snd_strerror(err)); @@ -137,6 +189,8 @@ void find_controls(void) break; } } + + snd_config_delete(config); } /* -- cgit v1.2.3 From fb6723daf89083a0d2290f3a0abc777e40766c84 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 29 May 2021 12:33:53 +0900 Subject: ALSA: pcm: comment about relation between msbits hw parameter and [S|U]32 formats Regarding to handling [U|S][32|24] PCM formats, many userspace application developers and driver developers have confusion, since they require them to understand justification or padding. It easily loses consistency and soundness to operate with many type of devices. In this commit, I attempt to solve the situation by adding comment about relation between [S|U]32 formats and 'msbits' hardware parameter. The formats are used for 'left-justified' sample format, and the available bit count in most significant bit is delivered to userspace in msbits hardware parameter (struct snd_pcm_hw_params.msbits), which is decided by msbits constraint added by pcm drivers (snd_pcm_hw_constraint_msbits()). In driver side, the msbits constraint includes two elements; the physical width of format and the available width of the format in most significant bit. The former is used to match SAMPLE_BITS of format. (For my convenience, I ignore wildcard in the usage of the constraint.) As a result of interaction between ALSA pcm core and ALSA pcm application, when the format in which SAMPLE_BITS equals to physical width of the msbits constaint, the msbits parameter is set by referring to the available width of the constraint. When the msbits parameter is not changed in the above process, ALSA pcm core set it alternatively with SAMPLE_BIT of chosen format. In userspace application side, the msbits is only available after calling ioctl(2) with SNDRV_PCM_IOCTL_HW_PARAMS request. Even if the hardware parameter structure includes somewhat value of SAMPLE_BITS interval parameter as width of format, all of the width is not always available since msbits can be less than the width. I note that [S|U]24 formats are used for 'right-justified' 24 bit sample formats within 32 bit frame. The first byte in most significant bit should be invalidated. Although the msbits exposed to userspace should be zero as invalid value, actually it is 32 from physical width of format. [ corrected typos -- tiwai ] Signed-off-by: Takashi Sakamoto Link: https://lore.kernel.org/r/20210529033353.21641-1-o-takashi@sakamocchi.jp Signed-off-by: Takashi Iwai --- include/sound/pcm.h | 3 +++ include/uapi/sound/asound.h | 3 +++ 2 files changed, 6 insertions(+) diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 33451f8ff755..9b187d86e1bd 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -147,6 +147,9 @@ struct snd_pcm_ops { #define SNDRV_PCM_FMTBIT_S24_BE _SNDRV_PCM_FMTBIT(S24_BE) #define SNDRV_PCM_FMTBIT_U24_LE _SNDRV_PCM_FMTBIT(U24_LE) #define SNDRV_PCM_FMTBIT_U24_BE _SNDRV_PCM_FMTBIT(U24_BE) +// For S32/U32 formats, 'msbits' hardware parameter is often used to deliver information about the +// available bit count in most significant bit. It's for the case of so-called 'left-justified' or +// `right-padding` sample which has less width than 32 bit. #define SNDRV_PCM_FMTBIT_S32_LE _SNDRV_PCM_FMTBIT(S32_LE) #define SNDRV_PCM_FMTBIT_S32_BE _SNDRV_PCM_FMTBIT(S32_BE) #define SNDRV_PCM_FMTBIT_U32_LE _SNDRV_PCM_FMTBIT(U32_LE) diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index 5fbb79e30819..cf1d20e34167 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -202,6 +202,9 @@ typedef int __bitwise snd_pcm_format_t; #define SNDRV_PCM_FORMAT_S24_BE ((__force snd_pcm_format_t) 7) /* low three bytes */ #define SNDRV_PCM_FORMAT_U24_LE ((__force snd_pcm_format_t) 8) /* low three bytes */ #define SNDRV_PCM_FORMAT_U24_BE ((__force snd_pcm_format_t) 9) /* low three bytes */ +// For S32/U32 formats, 'msbits' hardware parameter is often used to deliver information about the +// available bit count in most significant bit. It's for the case of so-called 'left-justified' or +// `right-padding` sample which has less width than 32 bit. #define SNDRV_PCM_FORMAT_S32_LE ((__force snd_pcm_format_t) 10) #define SNDRV_PCM_FORMAT_S32_BE ((__force snd_pcm_format_t) 11) #define SNDRV_PCM_FORMAT_U32_LE ((__force snd_pcm_format_t) 12) -- cgit v1.2.3 From 55b71f6c29f2a78af42dd453dfed895eba516cb4 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 13 Dec 2021 17:12:57 +0900 Subject: ALSA: uapi: use C90 comment style instead of C99 style UAPI headers are built with compiler option for C90, thus double-slashes comment introduced in C99 is not preferable. Fixes: fb6723daf890 ("ALSA: pcm: comment about relation between msbits hw parameter and [S|U]32 formats") Signed-off-by: Takashi Sakamoto Link: https://lore.kernel.org/r/20211213081257.36097-1-o-takashi@sakamocchi.jp Signed-off-by: Takashi Iwai --- include/uapi/sound/asound.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index cf1d20e34167..1834f58b8ede 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -202,9 +202,11 @@ typedef int __bitwise snd_pcm_format_t; #define SNDRV_PCM_FORMAT_S24_BE ((__force snd_pcm_format_t) 7) /* low three bytes */ #define SNDRV_PCM_FORMAT_U24_LE ((__force snd_pcm_format_t) 8) /* low three bytes */ #define SNDRV_PCM_FORMAT_U24_BE ((__force snd_pcm_format_t) 9) /* low three bytes */ -// For S32/U32 formats, 'msbits' hardware parameter is often used to deliver information about the -// available bit count in most significant bit. It's for the case of so-called 'left-justified' or -// `right-padding` sample which has less width than 32 bit. +/* + * For S32/U32 formats, 'msbits' hardware parameter is often used to deliver information about the + * available bit count in most significant bit. It's for the case of so-called 'left-justified' or + * `right-padding` sample which has less width than 32 bit. + */ #define SNDRV_PCM_FORMAT_S32_LE ((__force snd_pcm_format_t) 10) #define SNDRV_PCM_FORMAT_S32_BE ((__force snd_pcm_format_t) 11) #define SNDRV_PCM_FORMAT_U32_LE ((__force snd_pcm_format_t) 12) -- cgit v1.2.3 From 78977fd5b11cc90668c0dec6109d2f6572c9601c Mon Sep 17 00:00:00 2001 From: Xiaoke Wang Date: Mon, 13 Dec 2021 18:52:32 +0800 Subject: ALSA: sound/isa/gus: check the return value of kstrdup() kstrdup() returns NULL when some internal memory errors happen, it is better to check the return value of it. Otherwise, we may not to be able to catch some memory errors in time. Signed-off-by: Xiaoke Wang Link: https://lore.kernel.org/r/tencent_1E3950293AC22395ACFE99404C985D738309@qq.com Signed-off-by: Takashi Iwai --- sound/isa/gus/gus_mem.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sound/isa/gus/gus_mem.c b/sound/isa/gus/gus_mem.c index ff9480f249fe..4c691dbf2721 100644 --- a/sound/isa/gus/gus_mem.c +++ b/sound/isa/gus/gus_mem.c @@ -199,6 +199,10 @@ struct snd_gf1_mem_block *snd_gf1_mem_alloc(struct snd_gf1_mem * alloc, int owne memcpy(&block.share_id, share_id, sizeof(block.share_id)); block.owner = owner; block.name = kstrdup(name, GFP_KERNEL); + if (block.name == NULL) { + snd_gf1_mem_lock(alloc, 1); + return NULL; + } nblock = snd_gf1_mem_xalloc(alloc, &block); snd_gf1_mem_lock(alloc, 1); return nblock; @@ -237,13 +241,13 @@ int snd_gf1_mem_init(struct snd_gus_card * gus) block.ptr = 0; block.size = 1024; block.name = kstrdup("InterWave LFOs", GFP_KERNEL); - if (snd_gf1_mem_xalloc(alloc, &block) == NULL) + if (block.name == NULL || snd_gf1_mem_xalloc(alloc, &block) == NULL) return -ENOMEM; } block.ptr = gus->gf1.default_voice_address; block.size = 4; block.name = kstrdup("Voice default (NULL's)", GFP_KERNEL); - if (snd_gf1_mem_xalloc(alloc, &block) == NULL) + if (block.name == NULL || snd_gf1_mem_xalloc(alloc, &block) == NULL) return -ENOMEM; #ifdef CONFIG_SND_DEBUG snd_card_ro_proc_new(gus->card, "gusmem", gus, snd_gf1_mem_info_read); -- cgit v1.2.3 From c2f51415401cb8e9b7991e828ae12ab2972f2ca7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 13 Dec 2021 14:24:43 +0100 Subject: ALSA: gus: Fix erroneous memory allocation snd_gf1_mem_xalloc() returns NULL incorrectly when the memory chunk is allocated in the middle of the chain. This patch corrects the return value to treat it properly. Reviewed-by: Jaroslav Kysela Link: https://lore.kernel.org/r/20211213132444.22385-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/isa/gus/gus_mem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/isa/gus/gus_mem.c b/sound/isa/gus/gus_mem.c index 4c691dbf2721..5e3ff3137dd7 100644 --- a/sound/isa/gus/gus_mem.c +++ b/sound/isa/gus/gus_mem.c @@ -44,7 +44,7 @@ static struct snd_gf1_mem_block *snd_gf1_mem_xalloc(struct snd_gf1_mem * alloc, else nblock->prev->next = nblock; mutex_unlock(&alloc->memory_mutex); - return NULL; + return nblock; } pblock = pblock->next; } -- cgit v1.2.3 From dec242b6a8380c08e41e02fb54f1282894fb45cc Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 13 Dec 2021 15:15:12 +0100 Subject: ALSA: gus: Fix memory leaks at memory allocator error paths When snd_gf1_mem_xalloc() returns NULL, the current code still leaves the formerly allocated block.name string but returns an error immediately. This patch does code-refactoring to move the kstrdup() call itself into snd_gf1_mem_xalloc() and deals with the resource free in the helper code by itself for fixing those memory leaks. Suggested-by: Jaroslav Kysela Reviewed-by: Jaroslav Kysela Link: https://lore.kernel.org/r/20211213132444.22385-2-tiwai@suse.de Link: https://lore.kernel.org/r/20211213141512.27359-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/isa/gus/gus_mem.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/sound/isa/gus/gus_mem.c b/sound/isa/gus/gus_mem.c index 5e3ff3137dd7..3e56c01c4544 100644 --- a/sound/isa/gus/gus_mem.c +++ b/sound/isa/gus/gus_mem.c @@ -24,8 +24,9 @@ void snd_gf1_mem_lock(struct snd_gf1_mem * alloc, int xup) } } -static struct snd_gf1_mem_block *snd_gf1_mem_xalloc(struct snd_gf1_mem * alloc, - struct snd_gf1_mem_block * block) +static struct snd_gf1_mem_block * +snd_gf1_mem_xalloc(struct snd_gf1_mem *alloc, struct snd_gf1_mem_block *block, + const char *name) { struct snd_gf1_mem_block *pblock, *nblock; @@ -33,6 +34,12 @@ static struct snd_gf1_mem_block *snd_gf1_mem_xalloc(struct snd_gf1_mem * alloc, if (nblock == NULL) return NULL; *nblock = *block; + nblock->name = kstrdup(name, GFP_KERNEL); + if (!nblock->name) { + kfree(nblock); + return NULL; + } + pblock = alloc->first; while (pblock) { if (pblock->ptr > nblock->ptr) { @@ -198,12 +205,7 @@ struct snd_gf1_mem_block *snd_gf1_mem_alloc(struct snd_gf1_mem * alloc, int owne if (share_id != NULL) memcpy(&block.share_id, share_id, sizeof(block.share_id)); block.owner = owner; - block.name = kstrdup(name, GFP_KERNEL); - if (block.name == NULL) { - snd_gf1_mem_lock(alloc, 1); - return NULL; - } - nblock = snd_gf1_mem_xalloc(alloc, &block); + nblock = snd_gf1_mem_xalloc(alloc, &block, name); snd_gf1_mem_lock(alloc, 1); return nblock; } @@ -240,14 +242,12 @@ int snd_gf1_mem_init(struct snd_gus_card * gus) if (gus->gf1.enh_mode) { block.ptr = 0; block.size = 1024; - block.name = kstrdup("InterWave LFOs", GFP_KERNEL); - if (block.name == NULL || snd_gf1_mem_xalloc(alloc, &block) == NULL) + if (!snd_gf1_mem_xalloc(alloc, &block, "InterWave LFOs")) return -ENOMEM; } block.ptr = gus->gf1.default_voice_address; block.size = 4; - block.name = kstrdup("Voice default (NULL's)", GFP_KERNEL); - if (block.name == NULL || snd_gf1_mem_xalloc(alloc, &block) == NULL) + if (!snd_gf1_mem_xalloc(alloc, &block, "Voice default (NULL's)")) return -ENOMEM; #ifdef CONFIG_SND_DEBUG snd_card_ro_proc_new(gus->card, "gusmem", gus, snd_gf1_mem_info_read); -- cgit v1.2.3 From c697ef868f596aba7a5e90be8eb10bf4d4a98990 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 7 Dec 2021 13:39:41 -0600 Subject: ASoC: SOF: Intel: ICL: move ICL-specific ops to icl.c Move the ICL specific ops to icl.c. Also introduce a macro ICL_DSP_HPRO_CORE_ID to define the core that should be powered up when HPRO is enabled. Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Signed-off-by: Ranjani Sridharan Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20211207193947.71080-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-loader.c | 64 -------------------------------------- sound/soc/sof/intel/hda.h | 2 -- sound/soc/sof/intel/icl.c | 67 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 65 insertions(+), 68 deletions(-) diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 40201e5ac201..bfb0e374ebab 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -481,49 +481,6 @@ int hda_dsp_post_fw_run(struct snd_sof_dev *sdev) return hda_dsp_ctrl_clock_power_gating(sdev, true); } -/* - * post fw run operations for ICL, - * Core 3 will be powered up and in stall when HPRO is enabled - */ -int hda_dsp_post_fw_run_icl(struct snd_sof_dev *sdev) -{ - struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; - int ret; - - if (sdev->first_boot) { - ret = hda_sdw_startup(sdev); - if (ret < 0) { - dev_err(sdev->dev, - "error: could not startup SoundWire links\n"); - return ret; - } - } - - hda_sdw_int_enable(sdev, true); - - /* - * The recommended HW programming sequence for ICL is to - * power up core 3 and keep it in stall if HPRO is enabled. - * Major difference between ICL and TGL, on ICL core 3 is managed by - * the host whereas on TGL it is handled by the firmware. - */ - if (!hda->clk_config_lpro) { - ret = hda_dsp_enable_core(sdev, BIT(3)); - if (ret < 0) { - dev_err(sdev->dev, "error: dsp core power up failed on core 3\n"); - return ret; - } - - sdev->enabled_cores_mask |= BIT(3); - sdev->dsp_core_ref_count[3]++; - - snd_sof_dsp_stall(sdev, BIT(3)); - } - - /* re-enable clock gating and power gating */ - return hda_dsp_ctrl_clock_power_gating(sdev, true); -} - int hda_dsp_ext_man_get_cavs_config_data(struct snd_sof_dev *sdev, const struct sof_ext_man_elem_header *hdr) { @@ -561,24 +518,3 @@ int hda_dsp_ext_man_get_cavs_config_data(struct snd_sof_dev *sdev, return 0; } - -int hda_dsp_core_stall_icl(struct snd_sof_dev *sdev, unsigned int core_mask) -{ - struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; - const struct sof_intel_dsp_desc *chip = hda->desc; - - /* make sure core_mask in host managed cores */ - core_mask &= chip->host_managed_cores_mask; - if (!core_mask) { - dev_err(sdev->dev, "error: core_mask is not in host managed cores\n"); - return -EINVAL; - } - - /* stall core */ - snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, - HDA_DSP_REG_ADSPCS, - HDA_DSP_ADSPCS_CSTALL_MASK(core_mask), - HDA_DSP_ADSPCS_CSTALL_MASK(core_mask)); - - return 0; -} diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 72e78c449aa8..e2055b6c8139 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -618,8 +618,6 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev); /* pre and post fw run ops */ int hda_dsp_pre_fw_run(struct snd_sof_dev *sdev); int hda_dsp_post_fw_run(struct snd_sof_dev *sdev); -int hda_dsp_post_fw_run_icl(struct snd_sof_dev *sdev); -int hda_dsp_core_stall_icl(struct snd_sof_dev *sdev, unsigned int core_mask); /* parse platform specific ext manifest ops */ int hda_dsp_ext_man_get_cavs_config_data(struct snd_sof_dev *sdev, diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c index 343c1af7c453..f75e3983969f 100644 --- a/sound/soc/sof/intel/icl.c +++ b/sound/soc/sof/intel/icl.c @@ -18,12 +18,75 @@ #include "hda-ipc.h" #include "../sof-audio.h" +#define ICL_DSP_HPRO_CORE_ID 3 + static const struct snd_sof_debugfs_map icl_dsp_debugfs[] = { {"hda", HDA_DSP_HDA_BAR, 0, 0x4000, SOF_DEBUGFS_ACCESS_ALWAYS}, {"pp", HDA_DSP_PP_BAR, 0, 0x1000, SOF_DEBUGFS_ACCESS_ALWAYS}, {"dsp", HDA_DSP_BAR, 0, 0x10000, SOF_DEBUGFS_ACCESS_ALWAYS}, }; +static int icl_dsp_core_stall(struct snd_sof_dev *sdev, unsigned int core_mask) +{ + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + const struct sof_intel_dsp_desc *chip = hda->desc; + + /* make sure core_mask in host managed cores */ + core_mask &= chip->host_managed_cores_mask; + if (!core_mask) { + dev_err(sdev->dev, "error: core_mask is not in host managed cores\n"); + return -EINVAL; + } + + /* stall core */ + snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS, + HDA_DSP_ADSPCS_CSTALL_MASK(core_mask), + HDA_DSP_ADSPCS_CSTALL_MASK(core_mask)); + + return 0; +} + +/* + * post fw run operation for ICL. + * Core 3 will be powered up and in stall when HPRO is enabled + */ +static int icl_dsp_post_fw_run(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + int ret; + + if (sdev->first_boot) { + ret = hda_sdw_startup(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: could not startup SoundWire links\n"); + return ret; + } + } + + hda_sdw_int_enable(sdev, true); + + /* + * The recommended HW programming sequence for ICL is to + * power up core 3 and keep it in stall if HPRO is enabled. + */ + if (!hda->clk_config_lpro) { + ret = hda_dsp_enable_core(sdev, BIT(ICL_DSP_HPRO_CORE_ID)); + if (ret < 0) { + dev_err(sdev->dev, "error: dsp core power up failed on core %d\n", + ICL_DSP_HPRO_CORE_ID); + return ret; + } + + sdev->enabled_cores_mask |= BIT(ICL_DSP_HPRO_CORE_ID); + sdev->dsp_core_ref_count[ICL_DSP_HPRO_CORE_ID]++; + + snd_sof_dsp_stall(sdev, BIT(ICL_DSP_HPRO_CORE_ID)); + } + + /* re-enable clock gating and power gating */ + return hda_dsp_ctrl_clock_power_gating(sdev, true); +} + /* Icelake ops */ const struct snd_sof_dsp_ops sof_icl_ops = { /* probe/remove/shutdown */ @@ -93,7 +156,7 @@ const struct snd_sof_dsp_ops sof_icl_ops = { /* pre/post fw run */ .pre_fw_run = hda_dsp_pre_fw_run, - .post_fw_run = hda_dsp_post_fw_run_icl, + .post_fw_run = icl_dsp_post_fw_run, /* parse platform specific extended manifest */ .parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data, @@ -103,7 +166,7 @@ const struct snd_sof_dsp_ops sof_icl_ops = { /* firmware run */ .run = hda_dsp_cl_boot_firmware_iccmax, - .stall = hda_dsp_core_stall_icl, + .stall = icl_dsp_core_stall, /* trace callback */ .trace_init = hda_dsp_trace_init, -- cgit v1.2.3 From a792bfc1c2bc4b5e2311edc62e0efe5adec5d079 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 7 Dec 2021 13:39:42 -0600 Subject: ASoC: SOF: Intel: hda-stream: limit PROCEN workaround MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The work-around enabled in hda-stream.c is only required on earlier versions of SOCs/PCH (Skylake, KabyLake, ApolloLake, GeminiLake). Before setting the format on the host DMA, it is required to couple the host and link DMA - which as a consequence shall use the same format. This patch introduces a quirk field in the platform descriptor and makes the work-around conditional. Newer platforms have no limitations on the use of host and link DMA, which can use different formats. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Reviewed-by: Rander Wang Link: https://lore.kernel.org/r/20211207193947.71080-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/apl.c | 1 + sound/soc/sof/intel/hda-stream.c | 18 ++++++++++++------ sound/soc/sof/intel/shim.h | 4 ++++ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index 8778f46f1d37..810b8b6748a0 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -147,5 +147,6 @@ const struct sof_intel_dsp_desc apl_chip_info = { .rom_init_timeout = 150, .ssp_count = APL_SSP_COUNT, .ssp_base_offset = APL_SSP_BASE_OFFSET, + .quirks = SOF_INTEL_PROCEN_FMT_QUIRK, }; EXPORT_SYMBOL_NS(apl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON); diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 440827ce390d..5f9eb5bdcdba 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -472,6 +472,7 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, struct snd_pcm_hw_params *params) { + const struct sof_intel_dsp_desc *chip = get_chip_info(sdev->pdata); struct hdac_bus *bus = sof_to_bus(sdev); struct hdac_stream *hstream = &stream->hstream; int sd_offset = SOF_STREAM_SD_OFFSET(hstream); @@ -584,6 +585,7 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, /* * Recommended hardware programming sequence for HDAudio DMA format + * on earlier platforms - this is not needed on newer platforms * * 1. Put DMA into coupled mode by clearing PPCTL.PROCEN bit * for corresponding stream index before the time of writing @@ -593,9 +595,11 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, * enable decoupled mode */ - /* couple host and link DMA, disable DSP features */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, - mask, 0); + if (chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK) { + /* couple host and link DMA, disable DSP features */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + mask, 0); + } /* program stream format */ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, @@ -603,9 +607,11 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, SOF_HDA_ADSP_REG_CL_SD_FORMAT, 0xffff, hstream->format_val); - /* decouple host and link DMA, enable DSP features */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, - mask, mask); + if (chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK) { + /* decouple host and link DMA, enable DSP features */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + mask, mask); + } /* program last valid index */ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, diff --git a/sound/soc/sof/intel/shim.h b/sound/soc/sof/intel/shim.h index 08c53cb41ea7..f36cd9d5eb94 100644 --- a/sound/soc/sof/intel/shim.h +++ b/sound/soc/sof/intel/shim.h @@ -151,6 +151,9 @@ #define PCI_PMCS 0x84 #define PCI_PMCS_PS_MASK 0x3 +/* Intel quirks */ +#define SOF_INTEL_PROCEN_FMT_QUIRK BIT(0) + /* DSP hardware descriptor */ struct sof_intel_dsp_desc { int cores_num; @@ -166,6 +169,7 @@ struct sof_intel_dsp_desc { int ssp_base_offset; /* base address of the SSPs */ u32 sdw_shim_base; u32 sdw_alh_base; + u32 quirks; bool (*check_sdw_irq)(struct snd_sof_dev *sdev); }; -- cgit v1.2.3 From 12ce213821b77242b2217d08850ff972e1fb50bb Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 7 Dec 2021 13:39:43 -0600 Subject: ASoC: SOF: Intel: hda-ctrl: apply symmetry for DPIB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit we use 'bus->use_posbuf && bus->posbuf.addr' in hda_dsp_ctrl_init_chip(), use the same for hda_dsp_ctrl_stop_chip() Signed-off-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Link: https://lore.kernel.org/r/20211207193947.71080-4-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-ctrl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c index fa5f0a718901..0c29bb196e59 100644 --- a/sound/soc/sof/intel/hda-ctrl.c +++ b/sound/soc/sof/intel/hda-ctrl.c @@ -353,7 +353,7 @@ void hda_dsp_ctrl_stop_chip(struct snd_sof_dev *sdev) snd_hdac_bus_stop_cmd_io(bus); #endif /* disable position buffer */ - if (bus->posbuf.addr) { + if (bus->use_posbuf && bus->posbuf.addr) { snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE, 0); snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, -- cgit v1.2.3 From ae81d8fd57ff7d2b421c80f0f9426d9e775023b5 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 7 Dec 2021 13:39:44 -0600 Subject: ASoC: SOF: hda-stream: only enable DPIB if needed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existing code is inconsistent, we should only enable DPIB if the 'use_posbuf' field is true. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Link: https://lore.kernel.org/r/20211207193947.71080-5-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-stream.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 5f9eb5bdcdba..e910f68706d9 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -626,9 +626,10 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, upper_32_bits(hstream->bdl.addr)); - /* enable position buffer */ - if (!(snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE) - & SOF_HDA_ADSP_DPLBASE_ENABLE)) { + /* enable position buffer, if needed */ + if (bus->use_posbuf && bus->posbuf.addr && + !(snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE) + & SOF_HDA_ADSP_DPLBASE_ENABLE)) { snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPUBASE, upper_32_bits(bus->posbuf.addr)); snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE, -- cgit v1.2.3 From 288fad2f71fa0b989c075d4984879c26d47cfb06 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 7 Dec 2021 13:39:45 -0600 Subject: ASoC: SOF: Intel: hda: add quirks for HDAudio DMA position information MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code inherited from the Skylake driver does not seem to follow any known hardware recommendations. The only two recommended options are a) use DPIB registers if VC1 traffic is not allowed b) use DPIB DDR update if VC1 traffic is used In all of SOF-based updated, VC1 is not supported so we can 'safely' move to using DPIB registers only. This patch keeps the legacy code, in case there was an undocumented issue lost to history, and adds the DPIB DDR update for additional debug. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Link: https://lore.kernel.org/r/20211207193947.71080-6-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-pcm.c | 86 ++++++++++++++++++++++++++++++------------- sound/soc/sof/intel/hda.c | 9 ++++- sound/soc/sof/intel/hda.h | 6 +++ 3 files changed, 75 insertions(+), 26 deletions(-) diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index 974383cd0440..d78aa5d8552d 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -202,38 +202,74 @@ snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev, goto found; } - /* - * DPIB/posbuf position mode: - * For Playback, Use DPIB register from HDA space which - * reflects the actual data transferred. - * For Capture, Use the position buffer for pointer, as DPIB - * is not accurate enough, its update may be completed - * earlier than the data written to DDR. - */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + switch (sof_hda_position_quirk) { + case SOF_HDA_POSITION_QUIRK_USE_SKYLAKE_LEGACY: + /* + * This legacy code, inherited from the Skylake driver, + * mixes DPIB registers and DPIB DDR updates and + * does not seem to follow any known hardware recommendations. + * It's not clear e.g. why there is a different flow + * for capture and playback, the only information that matters is + * what traffic class is used, and on all SOF-enabled platforms + * only VC0 is supported so the work-around was likely not necessary + * and quite possibly wrong. + */ + + /* DPIB/posbuf position mode: + * For Playback, Use DPIB register from HDA space which + * reflects the actual data transferred. + * For Capture, Use the position buffer for pointer, as DPIB + * is not accurate enough, its update may be completed + * earlier than the data written to DDR. + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + pos = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, + AZX_REG_VS_SDXDPIB_XBASE + + (AZX_REG_VS_SDXDPIB_XINTERVAL * + hstream->index)); + } else { + /* + * For capture stream, we need more workaround to fix the + * position incorrect issue: + * + * 1. Wait at least 20us before reading position buffer after + * the interrupt generated(IOC), to make sure position update + * happens on frame boundary i.e. 20.833uSec for 48KHz. + * 2. Perform a dummy Read to DPIB register to flush DMA + * position value. + * 3. Read the DMA Position from posbuf. Now the readback + * value should be >= period boundary. + */ + usleep_range(20, 21); + snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, + AZX_REG_VS_SDXDPIB_XBASE + + (AZX_REG_VS_SDXDPIB_XINTERVAL * + hstream->index)); + pos = snd_hdac_stream_get_pos_posbuf(hstream); + } + break; + case SOF_HDA_POSITION_QUIRK_USE_DPIB_REGISTERS: + /* + * In case VC1 traffic is disabled this is the recommended option + */ pos = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, AZX_REG_VS_SDXDPIB_XBASE + (AZX_REG_VS_SDXDPIB_XINTERVAL * hstream->index)); - } else { + break; + case SOF_HDA_POSITION_QUIRK_USE_DPIB_DDR_UPDATE: /* - * For capture stream, we need more workaround to fix the - * position incorrect issue: - * - * 1. Wait at least 20us before reading position buffer after - * the interrupt generated(IOC), to make sure position update - * happens on frame boundary i.e. 20.833uSec for 48KHz. - * 2. Perform a dummy Read to DPIB register to flush DMA - * position value. - * 3. Read the DMA Position from posbuf. Now the readback - * value should be >= period boundary. + * This is the recommended option when VC1 is enabled. + * While this isn't needed for SOF platforms it's added for + * consistency and debug. */ - usleep_range(20, 21); - snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, - AZX_REG_VS_SDXDPIB_XBASE + - (AZX_REG_VS_SDXDPIB_XINTERVAL * - hstream->index)); pos = snd_hdac_stream_get_pos_posbuf(hstream); + break; + default: + dev_err_once(sdev->dev, "hda_position_quirk value %d not supported\n", + sof_hda_position_quirk); + pos = 0; + break; } if (pos >= hstream->bufsize) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index cfe026dbf124..dabbd5d908f6 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -432,6 +432,10 @@ MODULE_PARM_DESC(use_msi, "SOF HDA use PCI MSI mode"); #define hda_use_msi (1) #endif +int sof_hda_position_quirk = SOF_HDA_POSITION_QUIRK_USE_DPIB_REGISTERS; +module_param_named(position_quirk, sof_hda_position_quirk, int, 0444); +MODULE_PARM_DESC(position_quirk, "SOF HDaudio position quirk"); + static char *hda_model; module_param(hda_model, charp, 0444); MODULE_PARM_DESC(hda_model, "Use the given HDA board model."); @@ -610,7 +614,10 @@ static int hda_init(struct snd_sof_dev *sdev) /* HDA bus init */ sof_hda_bus_init(bus, &pci->dev); - bus->use_posbuf = 1; + if (sof_hda_position_quirk == SOF_HDA_POSITION_QUIRK_USE_DPIB_REGISTERS) + bus->use_posbuf = 0; + else + bus->use_posbuf = 1; bus->bdl_pos_adj = 0; bus->sync_write = 1; diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index e2055b6c8139..cb71d9d5cf6c 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -740,4 +740,10 @@ struct sof_ipc_dai_config; int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags); int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags); +#define SOF_HDA_POSITION_QUIRK_USE_SKYLAKE_LEGACY (0) /* previous implementation */ +#define SOF_HDA_POSITION_QUIRK_USE_DPIB_REGISTERS (1) /* recommended if VC0 only */ +#define SOF_HDA_POSITION_QUIRK_USE_DPIB_DDR_UPDATE (2) /* recommended with VC0 or VC1 */ + +extern int sof_hda_position_quirk; + #endif -- cgit v1.2.3 From 924631df4134d62b51a9442d97355eeba7ff613c Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 7 Dec 2021 13:39:46 -0600 Subject: ASoC: SOF: Intel: hda-dai: remove unused fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existing code does not use the 'host_dma_id', 'link_dma_id', 'host_bps' fields remove them. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Kai Vehmanen Reviewed-by: Péter Ujfalusi Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20211207193947.71080-7-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-dai.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 8c1d7ddb00e2..35ffb71116c6 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -21,8 +21,6 @@ #endif struct hda_pipe_params { - u8 host_dma_id; - u8 link_dma_id; u32 ch; u32 s_freq; u32 s_fmt; @@ -30,7 +28,6 @@ struct hda_pipe_params { snd_pcm_format_t format; int link_index; int stream; - unsigned int host_bps; unsigned int link_bps; }; @@ -256,7 +253,6 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream, p_params.ch = params_channels(params); p_params.s_freq = params_rate(params); p_params.stream = substream->stream; - p_params.link_dma_id = stream_tag - 1; p_params.link_index = link->index; p_params.format = params_format(params); -- cgit v1.2.3 From 290a7c5509b6f14c28e959392f3cbc4d5b2c9318 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 7 Dec 2021 13:39:47 -0600 Subject: ASoC: SOF: Intel: add comment on JasperLake support Explain why JasperLake is exposed in cnl.c instead of icl.c No functionality change. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Kai Vehmanen Reviewed-by: Bard Liao Link: https://lore.kernel.org/r/20211207193947.71080-8-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/cnl.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 04daaa6100f1..3da158d08980 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -358,6 +358,13 @@ const struct sof_intel_dsp_desc cnl_chip_info = { }; EXPORT_SYMBOL_NS(cnl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON); +/* + * JasperLake is technically derived from IceLake, and should be in + * described in icl.c. However since JasperLake was designed with + * two cores, it cannot support the IceLake-specific power-up sequences + * which rely on core3. To simplify, JasperLake uses the CannonLake ops and + * is described in cnl.c + */ const struct sof_intel_dsp_desc jsl_chip_info = { /* Jasperlake */ .cores_num = 2, -- cgit v1.2.3 From c55676ec292e0ff83261eb61efaf99a91079a3b8 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Thu, 2 Dec 2021 15:55:01 +0100 Subject: ASoC: dt-bindings: qcom: sm8250: Drop redundant MultiMedia routes The MultiMedia audio routes can be deduced from other parts of the device tree (e.g. the definitions of the MultiMedia DAIs) and therefore specifying them again in "audio-routing" is redundant and prone to mistakes. This is no longer necessary since commit 6fd8d2d275f7 ("ASoC: qcom: qdsp6: Move frontend AIFs to q6asm-dai"). Let's drop them from the example in the DT schema as well to avoid confusion. Cc: Srinivas Kandagatla Signed-off-by: Stephan Gerhold Acked-by: Rob Herring Link: https://lore.kernel.org/r/20211202145505.58852-2-stephan@gerhold.net Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/qcom,sm8250.yaml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml index 7d57eb91657a..a0f1d7340eb5 100644 --- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml @@ -86,10 +86,7 @@ examples: audio-routing = "SpkrLeft IN", "WSA_SPK1 OUT", "SpkrRight IN", "WSA_SPK2 OUT", "VA DMIC0", "vdd-micb", - "VA DMIC1", "vdd-micb", - "MM_DL1", "MultiMedia1 Playback", - "MM_DL2", "MultiMedia2 Playback", - "MultiMedia3 Capture", "MM_UL3"; + "VA DMIC1", "vdd-micb"; mm1-dai-link { link-name = "MultiMedia0"; -- cgit v1.2.3 From 1875ae76f82c5c9acd7b7f44bd9226fbcbe858b7 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Thu, 2 Dec 2021 15:55:02 +0100 Subject: ASoC: dt-bindings: qcom: sm8250: Document "aux-devs" The sm8250 audio driver uses the common Qualcomm device tree parser and therefore already supports the "aux-devs" property that allows adding additional auxiliary devices to the sound card (e.g. analog speaker amplifiers that can be connected using "audio-routing"). Document the property in the DT schema for sm8250 as well. The description is taken from simple-card.yaml which has a very similar property. Cc: Srinivas Kandagatla Signed-off-by: Stephan Gerhold Acked-by: Rob Herring Link: https://lore.kernel.org/r/20211202145505.58852-3-stephan@gerhold.net Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/qcom,sm8250.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml index a0f1d7340eb5..3123382297b8 100644 --- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml @@ -27,6 +27,12 @@ properties: being the connection's source. Valid names could be power supplies, MicBias of codec and the jacks on the board. + aux-devs: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: | + List of phandles pointing to auxiliary devices, such + as amplifiers, to be added to the sound card. + model: $ref: /schemas/types.yaml#/definitions/string description: User visible long sound card name -- cgit v1.2.3 From b7875d88bf70100d2fe0dc08072018f994ccd6c4 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Thu, 2 Dec 2021 15:55:03 +0100 Subject: ASoC: dt-bindings: qcom: apq8016-sbc: Move to qcom,sm8250 DT schema All the Qualcomm sound card drivers use the same common device tree parsing code, so the allowed device tree nodes are almost the same for all of them. Convert the qcom,apq8016-sbc-sndcard documentation to a DT schema by adding it to the existing qcom,sm8250 schema. The only speciality of qcom,apq8016-sbc-sndcard is that it has memory resources for setting up an I/O mux. This can be handled using a conditional if statement that only requires it for the apq8016-sbc compatible. Cc: Srinivas Kandagatla Signed-off-by: Stephan Gerhold Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20211202145505.58852-4-stephan@gerhold.net Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/qcom,apq8016-sbc.txt | 96 ---------------------- .../devicetree/bindings/sound/qcom,sm8250.yaml | 83 ++++++++++++++++++- 2 files changed, 81 insertions(+), 98 deletions(-) delete mode 100644 Documentation/devicetree/bindings/sound/qcom,apq8016-sbc.txt diff --git a/Documentation/devicetree/bindings/sound/qcom,apq8016-sbc.txt b/Documentation/devicetree/bindings/sound/qcom,apq8016-sbc.txt deleted file mode 100644 index 23998262a0a7..000000000000 --- a/Documentation/devicetree/bindings/sound/qcom,apq8016-sbc.txt +++ /dev/null @@ -1,96 +0,0 @@ -* Qualcomm Technologies APQ8016 SBC ASoC machine driver - -This node models the Qualcomm Technologies APQ8016 SBC ASoC machine driver - -Required properties: - -- compatible : "qcom,apq8016-sbc-sndcard" - -- pinctrl-N : One property must exist for each entry in - pinctrl-names. See ../pinctrl/pinctrl-bindings.txt - for details of the property values. -- pinctrl-names : Must contain a "default" entry. -- reg : Must contain an address for each entry in reg-names. -- reg-names : A list which must include the following entries: - * "mic-iomux" - * "spkr-iomux" -- qcom,model : Name of the sound card. - -- qcom,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. Valid names could be power supplies, MicBias - of msm8x16_wcd codec and the jacks on the board: - - Power supplies: - * MIC BIAS External1 - * MIC BIAS External2 - * MIC BIAS Internal1 - * MIC BIAS Internal2 - - Board connectors: - * Headset Mic - * Secondary Mic - * DMIC - * Ext Spk - -Optional properties: - -- aux-devs : A list of phandles for auxiliary devices (e.g. analog - amplifiers) that do not appear directly within the DAI - links. Should be connected to another audio component - using "qcom,audio-routing". - -Dai-link subnode properties and subnodes: - -Required dai-link subnodes: - -- cpu : CPU sub-node -- codec : CODEC sub-node - -Required CPU/CODEC subnodes properties: - --link-name : Name of the dai link. --sound-dai : phandle/s and port of CPU/CODEC - -Example: - -sound: sound { - compatible = "qcom,apq8016-sbc-sndcard"; - reg = <0x07702000 0x4>, <0x07702004 0x4>; - reg-names = "mic-iomux", "spkr-iomux"; - qcom,model = "DB410c"; - - qcom,audio-routing = - "MIC BIAS External1", "Handset Mic", - "MIC BIAS Internal2", "Headset Mic", - "MIC BIAS External1", "Secondary Mic", - "AMIC1", "MIC BIAS External1", - "AMIC2", "MIC BIAS Internal2", - "AMIC3", "MIC BIAS External1", - "DMIC1", "MIC BIAS Internal1", - "MIC BIAS Internal1", "Digital Mic1", - "DMIC2", "MIC BIAS Internal1", - "MIC BIAS Internal1", "Digital Mic2"; - - /* I2S - Internal codec */ - internal-dai-link@0 { - cpu { /* PRIMARY */ - sound-dai = <&lpass MI2S_PRIMARY>; - }; - codec { - sound-dai = <&lpass_codec 0>, <&wcd_codec 0>; - }; - }; - - /* External Primary or External Secondary -ADV7533 HDMI */ - external-dai-link@0 { - link-name = "ADV7533"; - cpu { /* QUAT */ - sound-dai = <&lpass MI2S_QUATERNARY>; - }; - codec { - sound-dai = <&adv_bridge 0>; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml index 3123382297b8..97f13a0a71a9 100644 --- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml @@ -4,18 +4,19 @@ $id: http://devicetree.org/schemas/sound/qcom,sm8250.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Qualcomm Technologies Inc. SM8250 ASoC sound card driver +title: Qualcomm Technologies Inc. ASoC sound card drivers maintainers: - Srinivas Kandagatla description: - This bindings describes SC8250 SoC based sound cards + This bindings describes Qualcomm SoC based sound cards which uses LPASS internal codec for audio. properties: compatible: enum: + - qcom,apq8016-sbc-sndcard - qcom,sm8250-sndcard - qcom,qrb5165-rb5-sndcard @@ -37,6 +38,10 @@ properties: $ref: /schemas/types.yaml#/definitions/string description: User visible long sound card name + # Only valid for some compatibles (see allOf if below) + reg: true + reg-names: true + patternProperties: ".*-dai-link$": description: @@ -79,6 +84,33 @@ required: - compatible - model +allOf: + - if: + properties: + compatible: + contains: + enum: + - qcom,apq8016-sbc-sndcard + then: + properties: + reg: + items: + - description: Microphone I/O mux register address + - description: Speaker I/O mux register address + reg-names: + items: + - const: mic-iomux + - const: spkr-iomux + required: + - compatible + - model + - reg + - reg-names + else: + properties: + reg: false + reg-names: false + additionalProperties: false examples: @@ -160,3 +192,50 @@ examples: }; }; }; + + - | + #include + sound@7702000 { + compatible = "qcom,apq8016-sbc-sndcard"; + reg = <0x07702000 0x4>, <0x07702004 0x4>; + reg-names = "mic-iomux", "spkr-iomux"; + + model = "DB410c"; + audio-routing = + "AMIC2", "MIC BIAS Internal2", + "AMIC3", "MIC BIAS External1"; + + pinctrl-0 = <&cdc_pdm_lines_act &ext_sec_tlmm_lines_act &ext_mclk_tlmm_lines_act>; + pinctrl-1 = <&cdc_pdm_lines_sus &ext_sec_tlmm_lines_sus &ext_mclk_tlmm_lines_sus>; + pinctrl-names = "default", "sleep"; + + quaternary-dai-link { + link-name = "ADV7533"; + cpu { + sound-dai = <&lpass MI2S_QUATERNARY>; + }; + codec { + sound-dai = <&adv_bridge 0>; + }; + }; + + primary-dai-link { + link-name = "WCD"; + cpu { + sound-dai = <&lpass MI2S_PRIMARY>; + }; + codec { + sound-dai = <&lpass_codec 0>, <&wcd_codec 0>; + }; + }; + + tertiary-dai-link { + link-name = "WCD-Capture"; + cpu { + sound-dai = <&lpass MI2S_TERTIARY>; + }; + codec { + sound-dai = <&lpass_codec 1>, <&wcd_codec 1>; + }; + }; + }; -- cgit v1.2.3 From 38192dc36f1fe1615b7a12cc78b9354d6b4ba8b7 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Thu, 2 Dec 2021 15:55:04 +0100 Subject: ASoC: dt-bindings: qcom: Document qcom,msm8916-qdsp6-sndcard compatible There are two possible audio setups on MSM8916/APQ8016: Normally the audio is routed through the audio/modem DSP (covered by the qdsp6 driver). During upstreaming for the DragonBoard 410c it was decided to bypass it and instead talk directly to the audio controller using the "lpass" driver. Bypassing the DSP gives more control about the audio configuration but limits the functionality: For example, routing audio through the audio/modem DSP is strictly required for voice call audio. Also, without the special changes in the DB410c firmware other MSM8916 devices can only use the bypass as long as the modem DSP is not started. Otherwise, the firmware will assume control of the LPASS hardware block and audio is no longer functional. Add support for using the DSP audio setup instead using a new "qcom,msm8916-qdsp6-sndcard" compatible. It is basically a mixture of the apq8016-sbc-sndcard and the newer sm8250-sndcard, which uses indirect QDSP6 DAI links instead of the direct LPASS DAI links. Cc: Srinivas Kandagatla Signed-off-by: Stephan Gerhold Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20211202145505.58852-5-stephan@gerhold.net Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/qcom,sm8250.yaml | 42 ++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml index 97f13a0a71a9..e50964c54bb9 100644 --- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml @@ -17,6 +17,7 @@ properties: compatible: enum: - qcom,apq8016-sbc-sndcard + - qcom,msm8916-qdsp6-sndcard - qcom,sm8250-sndcard - qcom,qrb5165-rb5-sndcard @@ -91,6 +92,7 @@ allOf: contains: enum: - qcom,apq8016-sbc-sndcard + - qcom,msm8916-qdsp6-sndcard then: properties: reg: @@ -239,3 +241,43 @@ examples: }; }; }; + + - | + #include + #include + sound@7702000 { + compatible = "qcom,msm8916-qdsp6-sndcard"; + reg = <0x07702000 0x4>, <0x07702004 0x4>; + reg-names = "mic-iomux", "spkr-iomux"; + + model = "msm8916"; + audio-routing = + "AMIC1", "MIC BIAS Internal1", + "AMIC2", "MIC BIAS Internal2", + "AMIC3", "MIC BIAS Internal3"; + aux-devs = <&speaker_amp>; + + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&cdc_pdm_lines_act>; + pinctrl-1 = <&cdc_pdm_lines_sus>; + + mm1-dai-link { + link-name = "MultiMedia1"; + cpu { + sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA1>; + }; + }; + + primary-dai-link { + link-name = "Primary MI2S"; + cpu { + sound-dai = <&q6afedai PRIMARY_MI2S_RX>; + }; + platform { + sound-dai = <&q6routing>; + }; + codec { + sound-dai = <&lpass_codec 0>, <&wcd_codec 0>; + }; + }; + }; -- cgit v1.2.3 From a78a42fb48b8f261ab122c929f78c272ffc26d1b Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Thu, 2 Dec 2021 15:55:05 +0100 Subject: ASoC: qcom: apq8016_sbc: Allow routing audio through QDSP6 The apq8016-sbc-sndcard is designed to be used with the LPASS drivers (bypassing the combined audio/modem DSP in MSM8916/APQ8016). Make it possible to use QDSP6 audio instead for the msm8916-qdsp6-sndcard. This only requires adding some additional hooks that set up the DPCM backends correctly. Similar code is already used in drivers for newer SoCs such as apq8096.c, sdm845.c and sm8250.c. A slightly different initialization sequence is used for the apq8016-sbc and msm8916-qdsp6 sound card by defining the apq8016_sbc_add_ops() function as device match data. Cc: Srinivas Kandagatla Signed-off-by: Stephan Gerhold Reviewed-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20211202145505.58852-6-stephan@gerhold.net Signed-off-by: Mark Brown --- sound/soc/qcom/apq8016_sbc.c | 134 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 129 insertions(+), 5 deletions(-) diff --git a/sound/soc/qcom/apq8016_sbc.c b/sound/soc/qcom/apq8016_sbc.c index ba2a98268ee4..f9d69375320e 100644 --- a/sound/soc/qcom/apq8016_sbc.c +++ b/sound/soc/qcom/apq8016_sbc.c @@ -17,6 +17,9 @@ #include #include #include "common.h" +#include "qdsp6/q6afe.h" + +#define MI2S_COUNT (MI2S_QUATERNARY + 1) struct apq8016_sbc_data { struct snd_soc_card card; @@ -24,6 +27,7 @@ struct apq8016_sbc_data { void __iomem *spkr_iomux; struct snd_soc_jack jack; bool jack_setup; + int mi2s_clk_count[MI2S_COUNT]; }; #define MIC_CTRL_TER_WS_SLAVE_SEL BIT(21) @@ -38,10 +42,10 @@ struct apq8016_sbc_data { #define SPKR_CTL_TLMM_WS_EN_SEL_MASK GENMASK(19, 18) #define SPKR_CTL_TLMM_WS_EN_SEL_SEC BIT(18) #define DEFAULT_MCLK_RATE 9600000 +#define MI2S_BCLK_RATE 1536000 -static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd) +static int apq8016_dai_init(struct snd_soc_pcm_runtime *rtd, int mi2s) { - struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct snd_soc_dai *codec_dai; struct snd_soc_component *component; struct snd_soc_card *card = rtd->card; @@ -49,7 +53,7 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd) int i, rval; u32 value; - switch (cpu_dai->id) { + switch (mi2s) { case MI2S_PRIMARY: writel(readl(pdata->spkr_iomux) | SPKR_CTL_PRI_WS_SLAVE_SEL_11, pdata->spkr_iomux); @@ -128,6 +132,13 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd) return 0; } +static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + + return apq8016_dai_init(rtd, cpu_dai->id); +} + static void apq8016_sbc_add_ops(struct snd_soc_card *card) { struct snd_soc_dai_link *link; @@ -137,6 +148,113 @@ static void apq8016_sbc_add_ops(struct snd_soc_card *card) link->init = apq8016_sbc_dai_init; } +static int qdsp6_dai_get_lpass_id(struct snd_soc_dai *cpu_dai) +{ + switch (cpu_dai->id) { + case PRIMARY_MI2S_RX: + case PRIMARY_MI2S_TX: + return MI2S_PRIMARY; + case SECONDARY_MI2S_RX: + case SECONDARY_MI2S_TX: + return MI2S_SECONDARY; + case TERTIARY_MI2S_RX: + case TERTIARY_MI2S_TX: + return MI2S_TERTIARY; + case QUATERNARY_MI2S_RX: + case QUATERNARY_MI2S_TX: + return MI2S_QUATERNARY; + default: + return -EINVAL; + } +} + +static int msm8916_qdsp6_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + + snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS); + return apq8016_dai_init(rtd, qdsp6_dai_get_lpass_id(cpu_dai)); +} + +static int msm8916_qdsp6_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct apq8016_sbc_data *data = snd_soc_card_get_drvdata(card); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + int mi2s, ret; + + mi2s = qdsp6_dai_get_lpass_id(cpu_dai); + if (mi2s < 0) + return mi2s; + + if (++data->mi2s_clk_count[mi2s] > 1) + return 0; + + ret = snd_soc_dai_set_sysclk(cpu_dai, LPAIF_BIT_CLK, MI2S_BCLK_RATE, 0); + if (ret) + dev_err(card->dev, "Failed to enable LPAIF bit clk: %d\n", ret); + return ret; +} + +static void msm8916_qdsp6_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct apq8016_sbc_data *data = snd_soc_card_get_drvdata(card); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + int mi2s, ret; + + mi2s = qdsp6_dai_get_lpass_id(cpu_dai); + if (mi2s < 0) + return; + + if (--data->mi2s_clk_count[mi2s] > 0) + return; + + ret = snd_soc_dai_set_sysclk(cpu_dai, LPAIF_BIT_CLK, 0, 0); + if (ret) + dev_err(card->dev, "Failed to disable LPAIF bit clk: %d\n", ret); +} + +static const struct snd_soc_ops msm8916_qdsp6_be_ops = { + .startup = msm8916_qdsp6_startup, + .shutdown = msm8916_qdsp6_shutdown, +}; + +static int msm8916_qdsp6_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); + + return 0; +} + +static void msm8916_qdsp6_add_ops(struct snd_soc_card *card) +{ + struct snd_soc_dai_link *link; + int i; + + /* Make it obvious to userspace that QDSP6 is used */ + card->components = "qdsp6"; + + for_each_card_prelinks(card, i, link) { + if (link->no_pcm) { + link->init = msm8916_qdsp6_dai_init; + link->ops = &msm8916_qdsp6_be_ops; + link->be_hw_params_fixup = msm8916_qdsp6_be_hw_params_fixup; + } + } +} + static const struct snd_soc_dapm_widget apq8016_sbc_dapm_widgets[] = { SND_SOC_DAPM_MIC("Handset Mic", NULL), @@ -148,11 +266,16 @@ static const struct snd_soc_dapm_widget apq8016_sbc_dapm_widgets[] = { static int apq8016_sbc_platform_probe(struct platform_device *pdev) { + void (*add_ops)(struct snd_soc_card *card); struct device *dev = &pdev->dev; struct snd_soc_card *card; struct apq8016_sbc_data *data; int ret; + add_ops = device_get_match_data(&pdev->dev); + if (!add_ops) + return -EINVAL; + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -177,12 +300,13 @@ static int apq8016_sbc_platform_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(card, data); - apq8016_sbc_add_ops(card); + add_ops(card); return devm_snd_soc_register_card(&pdev->dev, card); } static const struct of_device_id apq8016_sbc_device_id[] __maybe_unused = { - { .compatible = "qcom,apq8016-sbc-sndcard" }, + { .compatible = "qcom,apq8016-sbc-sndcard", .data = apq8016_sbc_add_ops }, + { .compatible = "qcom,msm8916-qdsp6-sndcard", .data = msm8916_qdsp6_add_ops }, {}, }; MODULE_DEVICE_TABLE(of, apq8016_sbc_device_id); -- cgit v1.2.3 From 475b17b4a875ef31246c6a038ee60d5ca4982ea5 Mon Sep 17 00:00:00 2001 From: Allen-KH Cheng Date: Thu, 9 Dec 2021 22:08:30 +0200 Subject: ASoC: SOF: Remove pm_runtime_put_autosuspend() for SOF OF device In SOF OF device, pm_runtime_put_autosuspend() is not matching any pm_runtime_get_sync(). This is imbalanced for PM runtime. Also, for consistency we call pm_runtime_mark_last_busy() before enabling PM runtime. 1. Remove pm_runtime_put_autosuspend() in probe_complete 2. Reorder PM runtime calls int probe_complete Signed-off-by: Allen-KH Cheng Reviewed-by: Daniel Baluta Reviewed-by: Paul Olaru Reviewed-by: Pierre-Louis Bossart Signed-off-by: Daniel Baluta Link: https://lore.kernel.org/r/20211209200830.145005-1-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-of-dev.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/soc/sof/sof-of-dev.c b/sound/soc/sof/sof-of-dev.c index 412cbb824b84..b0089698eecb 100644 --- a/sound/soc/sof/sof-of-dev.c +++ b/sound/soc/sof/sof-of-dev.c @@ -74,11 +74,9 @@ static void sof_of_probe_complete(struct device *dev) /* allow runtime_pm */ pm_runtime_set_autosuspend_delay(dev, SND_SOF_SUSPEND_DELAY_MS); pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); - - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); } static int sof_of_probe(struct platform_device *pdev) -- cgit v1.2.3 From ec247fea7380244626d7095dfc1a0bb6c1f84f29 Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Sat, 4 Dec 2021 21:32:20 +0100 Subject: ASoC: SOF: sof-probes: Constify sof_probe_compr_ops The only usage of sof_probe_compr_ops is to assign its address to the cops field in the snd_soc_dai_driver struct (in sound/soc/sof/intel/hda-dai.c). Make it const to allow the compiler to put it in read-only memory. Signed-off-by: Rikard Falkeborn Link: https://lore.kernel.org/r/20211204203220.54712-1-rikard.falkeborn@gmail.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-probes.c | 2 +- sound/soc/sof/sof-probes.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/sof-probes.c b/sound/soc/sof/sof-probes.c index 5586af9f1a25..c79026cdb8c7 100644 --- a/sound/soc/sof/sof-probes.c +++ b/sound/soc/sof/sof-probes.c @@ -321,7 +321,7 @@ static int sof_probe_compr_pointer(struct snd_compr_stream *cstream, return snd_sof_probe_compr_pointer(sdev, cstream, tstamp, dai); } -struct snd_soc_cdai_ops sof_probe_compr_ops = { +const struct snd_soc_cdai_ops sof_probe_compr_ops = { .startup = sof_probe_compr_startup, .shutdown = sof_probe_compr_shutdown, .set_params = sof_probe_compr_set_params, diff --git a/sound/soc/sof/sof-probes.h b/sound/soc/sof/sof-probes.h index 35e1dd8d9e03..4a1ed2942d28 100644 --- a/sound/soc/sof/sof-probes.h +++ b/sound/soc/sof/sof-probes.h @@ -32,7 +32,7 @@ int sof_ipc_probe_points_add(struct snd_sof_dev *sdev, int sof_ipc_probe_points_remove(struct snd_sof_dev *sdev, unsigned int *buffer_id, size_t num_buffer_id); -extern struct snd_soc_cdai_ops sof_probe_compr_ops; +extern const struct snd_soc_cdai_ops sof_probe_compr_ops; extern const struct snd_compress_ops sof_probe_compressed_ops; #endif -- cgit v1.2.3 From 03c2192ab636987db72e99f319a942cc4f3cb352 Mon Sep 17 00:00:00 2001 From: Jiaxin Yu Date: Thu, 9 Dec 2021 15:32:24 +0800 Subject: ASoC: mediatek: assign correct type to argument Fix the following sparse warning: (new ones prefixed by >>) >> sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c:370:33: sparse: sparse: incorrect type in argument 3 (different base types) sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c:370:33: sparse: expected unsigned int to sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c:370:33: sparse: got restricted snd_pcm_format_t [usertype] Correct discription of format, use S32_LE and S24_LE to distinguish the different 32bit. Signed-off-by: Jiaxin Yu Reported-by: kernel test robot Reviewed-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20211209073224.21793-1-jiaxin.yu@mediatek.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c | 8 ++++---- sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c | 12 ++++++------ sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c | 4 ++-- sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c | 4 ++-- sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c | 4 ++-- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c index a4d26a6fc849..f8a72a5102ad 100644 --- a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c @@ -155,9 +155,9 @@ static const struct snd_soc_ops mt8183_da7219_rt1015_i2s_ops = { static int mt8183_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - /* fix BE i2s format to 32bit, clean param mask first */ + /* fix BE i2s format to S32_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, SNDRV_PCM_FORMAT_LAST); + 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S32_LE); @@ -167,9 +167,9 @@ static int mt8183_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, static int mt8183_rt1015_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - /* fix BE i2s format to 32bit, clean param mask first */ + /* fix BE i2s format to S24_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, SNDRV_PCM_FORMAT_LAST); + 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); diff --git a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c index aeb1af86047e..d5fc86132b49 100644 --- a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c @@ -94,11 +94,11 @@ static const struct snd_soc_ops mt8183_mt6358_rt1015_i2s_ops = { static int mt8183_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - dev_dbg(rtd->dev, "%s(), fix format to 32bit\n", __func__); + dev_dbg(rtd->dev, "%s(), fix format to S32_LE\n", __func__); - /* fix BE i2s format to 32bit, clean param mask first */ + /* fix BE i2s format to S32_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, SNDRV_PCM_FORMAT_LAST); + 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S32_LE); return 0; @@ -107,11 +107,11 @@ static int mt8183_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, static int mt8183_rt1015_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - dev_dbg(rtd->dev, "%s(), fix format to 32bit\n", __func__); + dev_dbg(rtd->dev, "%s(), fix format to S24_LE\n", __func__); - /* fix BE i2s format to 32bit, clean param mask first */ + /* fix BE i2s format to S24_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, SNDRV_PCM_FORMAT_LAST); + 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); return 0; diff --git a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c index a606133951b7..1d16939f80e3 100644 --- a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c +++ b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c @@ -350,9 +350,9 @@ static int mt8192_mt6359_hdmi_init(struct snd_soc_pcm_runtime *rtd) static int mt8192_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - /* fix BE i2s format to 32bit, clean param mask first */ + /* fix BE i2s format to S24_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, SNDRV_PCM_FORMAT_LAST); + 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c index cca1c739e690..5cdbfaafd479 100644 --- a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c +++ b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c @@ -359,7 +359,7 @@ static int mt8195_rt5682_init(struct snd_soc_pcm_runtime *rtd) static int mt8195_etdm_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - /* fix BE i2s format to 32bit, clean param mask first */ + /* fix BE i2s format to S24_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); @@ -464,7 +464,7 @@ static int mt8195_dptx_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - /* fix BE i2s format to 32bit, clean param mask first */ + /* fix BE i2s format to S24_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c index 11a185da0d96..fa50a31e9718 100644 --- a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c +++ b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c @@ -355,7 +355,7 @@ static int mt8195_rt5682_init(struct snd_soc_pcm_runtime *rtd) static int mt8195_etdm_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - /* fix BE i2s format to 32bit, clean param mask first */ + /* fix BE i2s format to S24_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); @@ -463,7 +463,7 @@ static int mt8195_dptx_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - /* fix BE i2s format to 32bit, clean param mask first */ + /* fix BE i2s format to S24_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); -- cgit v1.2.3 From d8a9c6e1f6766a16cf02b4e99a629f3c5512c183 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 7 Dec 2021 11:37:40 -0600 Subject: ASoC: soc-pcm: use GFP_ATOMIC for dpcm structure We allocate a structure in dpcm_be_connect(), which may be called in atomic context. Using GFP_KERNEL is not quite right, we have to use GFP_ATOMIC to prevent the allocator from sleeping. Suggested-by: Guennadi Liakhovetski Signed-off-by: Pierre-Louis Bossart Reviewed-by: Kai Vehmanen Reviewed-by: Bard Liao Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20211207173745.15850-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/soc-pcm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 3b4412183344..f66808dfb508 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1113,7 +1113,7 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe, return 0; } - dpcm = kzalloc(sizeof(struct snd_soc_dpcm), GFP_KERNEL); + dpcm = kzalloc(sizeof(struct snd_soc_dpcm), GFP_ATOMIC); if (!dpcm) return -ENOMEM; -- cgit v1.2.3 From bbf7d3b1c4f40eb02dd1dffb500ba00b0bff0303 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 7 Dec 2021 11:37:41 -0600 Subject: ASoC: soc-pcm: align BE 'atomicity' with that of the FE Since the flow for DPCM is based on taking a lock for the FE first, we need to make sure during the connection between a BE and an FE that they both use the same 'atomicity', otherwise we may sleep in atomic context. If the FE is nonatomic, this patch forces the BE to be nonatomic as well. That should have no negative impact since the BE 'inherits' the FE properties. However, if the FE is atomic and the BE is not, then the configuration is flagged as invalid. Signed-off-by: Pierre-Louis Bossart [ removed FE stream lock by tiwai ] Signed-off-by: Takashi Iwai Reviewed-by: Kai Vehmanen Reviewed-by: Bard Liao Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20211207173745.15850-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/soc-pcm.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index f66808dfb508..3a34b71fd3c1 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1104,6 +1104,8 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe, struct snd_soc_pcm_runtime *be, int stream) { + struct snd_pcm_substream *fe_substream; + struct snd_pcm_substream *be_substream; struct snd_soc_dpcm *dpcm; unsigned long flags; @@ -1113,6 +1115,20 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe, return 0; } + fe_substream = snd_soc_dpcm_get_substream(fe, stream); + be_substream = snd_soc_dpcm_get_substream(be, stream); + + if (!fe_substream->pcm->nonatomic && be_substream->pcm->nonatomic) { + dev_err(be->dev, "%s: FE is atomic but BE is nonatomic, invalid configuration\n", + __func__); + return -EINVAL; + } + if (fe_substream->pcm->nonatomic && !be_substream->pcm->nonatomic) { + dev_warn(be->dev, "%s: FE is nonatomic but BE is not, forcing BE as nonatomic\n", + __func__); + be_substream->pcm->nonatomic = 1; + } + dpcm = kzalloc(sizeof(struct snd_soc_dpcm), GFP_ATOMIC); if (!dpcm) return -ENOMEM; -- cgit v1.2.3 From b7898396f4bbe160f546d0c5e9fa17cca9a7d153 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 7 Dec 2021 11:37:42 -0600 Subject: ASoC: soc-pcm: Fix and cleanup DPCM locking The existing locking for DPCM has several issues a) a confusing mix of card->mutex and card->pcm_mutex. b) a dpcm_lock spinlock added inconsistently and on paths that could be recursively taken. The use of irqsave/irqrestore was also overkill. The suggested model is: 1) The pcm_mutex is the top-most protection of BE links in the FE. The pcm_mutex is applied always on either the top PCM callbacks or the external call from DAPM, not taken in the internal functions. 2) the FE stream lock is taken in higher levels before invoking dpcm_be_dai_trigger() 3) when adding and deleting a BE, both the pcm_mutex and FE stream lock are taken. Signed-off-by: Takashi Iwai [clarification of commit message by plbossart] Signed-off-by: Pierre-Louis Bossart Reviewed-by: Kai Vehmanen Reviewed-by: Bard Liao Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20211207173745.15850-4-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- include/sound/soc.h | 2 - sound/soc/soc-core.c | 1 - sound/soc/soc-pcm.c | 229 ++++++++++++++++++++++++++++++++++----------------- 3 files changed, 152 insertions(+), 80 deletions(-) diff --git a/include/sound/soc.h b/include/sound/soc.h index 8e6dd8a257c5..5872a8864f3b 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -893,8 +893,6 @@ struct snd_soc_card { struct mutex pcm_mutex; enum snd_soc_pcm_subclass pcm_subclass; - spinlock_t dpcm_lock; - int (*probe)(struct snd_soc_card *card); int (*late_probe)(struct snd_soc_card *card); int (*remove)(struct snd_soc_card *card); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index dcf6be4c4aaa..1d62160f96b1 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2315,7 +2315,6 @@ int snd_soc_register_card(struct snd_soc_card *card) mutex_init(&card->mutex); mutex_init(&card->dapm_mutex); mutex_init(&card->pcm_mutex); - spin_lock_init(&card->dpcm_lock); return snd_soc_bind_card(card); } diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 3a34b71fd3c1..2e282c42bac2 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -27,6 +27,31 @@ #include #include +static inline void snd_soc_dpcm_mutex_lock(struct snd_soc_pcm_runtime *rtd) +{ + mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); +} + +static inline void snd_soc_dpcm_mutex_unlock(struct snd_soc_pcm_runtime *rtd) +{ + mutex_unlock(&rtd->card->pcm_mutex); +} + +#define snd_soc_dpcm_mutex_assert_held(rtd) \ + lockdep_assert_held(&(rtd)->card->pcm_mutex) + +static inline void snd_soc_dpcm_stream_lock_irq(struct snd_soc_pcm_runtime *rtd, + int stream) +{ + snd_pcm_stream_lock_irq(snd_soc_dpcm_get_substream(rtd, stream)); +} + +static inline void snd_soc_dpcm_stream_unlock_irq(struct snd_soc_pcm_runtime *rtd, + int stream) +{ + snd_pcm_stream_unlock_irq(snd_soc_dpcm_get_substream(rtd, stream)); +} + #define DPCM_MAX_BE_USERS 8 static inline const char *soc_cpu_dai_name(struct snd_soc_pcm_runtime *rtd) @@ -73,7 +98,6 @@ static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe, struct snd_pcm_hw_params *params = &fe->dpcm[stream].hw_params; struct snd_soc_dpcm *dpcm; ssize_t offset = 0; - unsigned long flags; /* FE state */ offset += scnprintf(buf + offset, size - offset, @@ -101,7 +125,6 @@ static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe, goto out; } - spin_lock_irqsave(&fe->card->dpcm_lock, flags); for_each_dpcm_be(fe, stream, dpcm) { struct snd_soc_pcm_runtime *be = dpcm->be; params = &dpcm->hw_params; @@ -122,7 +145,6 @@ static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe, params_channels(params), params_rate(params)); } - spin_unlock_irqrestore(&fe->card->dpcm_lock, flags); out: return offset; } @@ -145,11 +167,13 @@ static ssize_t dpcm_state_read_file(struct file *file, char __user *user_buf, if (!buf) return -ENOMEM; + snd_soc_dpcm_mutex_lock(fe); for_each_pcm_streams(stream) if (snd_soc_dai_stream_valid(asoc_rtd_to_cpu(fe, 0), stream)) offset += dpcm_show_state(fe, stream, buf + offset, out_count - offset); + snd_soc_dpcm_mutex_unlock(fe); ret = simple_read_from_buffer(user_buf, count, ppos, buf, offset); @@ -221,14 +245,14 @@ static void dpcm_set_fe_update_state(struct snd_soc_pcm_runtime *fe, struct snd_pcm_substream *substream = snd_soc_dpcm_get_substream(fe, stream); - snd_pcm_stream_lock_irq(substream); + snd_soc_dpcm_stream_lock_irq(fe, stream); if (state == SND_SOC_DPCM_UPDATE_NO && fe->dpcm[stream].trigger_pending) { dpcm_fe_dai_do_trigger(substream, fe->dpcm[stream].trigger_pending - 1); fe->dpcm[stream].trigger_pending = 0; } fe->dpcm[stream].runtime_update = state; - snd_pcm_stream_unlock_irq(substream); + snd_soc_dpcm_stream_unlock_irq(fe, stream); } static void dpcm_set_be_update_state(struct snd_soc_pcm_runtime *be, @@ -256,7 +280,7 @@ void snd_soc_runtime_action(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai; int i; - lockdep_assert_held(&rtd->card->pcm_mutex); + snd_soc_dpcm_mutex_assert_held(rtd); for_each_rtd_dais(rtd, i, dai) snd_soc_dai_action(dai, stream, action); @@ -309,6 +333,8 @@ int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, { struct snd_soc_dpcm *dpcm; + snd_soc_dpcm_mutex_assert_held(fe); + for_each_dpcm_be(fe, dir, dpcm) { struct snd_soc_pcm_runtime *be = dpcm->be; @@ -646,14 +672,14 @@ static int soc_pcm_components_close(struct snd_pcm_substream *substream, return ret; } -static int soc_pcm_clean(struct snd_pcm_substream *substream, int rollback) +static int soc_pcm_clean(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream, int rollback) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_component *component; struct snd_soc_dai *dai; int i; - mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); + snd_soc_dpcm_mutex_assert_held(rtd); if (!rollback) snd_soc_runtime_deactivate(rtd, substream->stream); @@ -665,9 +691,6 @@ static int soc_pcm_clean(struct snd_pcm_substream *substream, int rollback) soc_pcm_components_close(substream, rollback); - - mutex_unlock(&rtd->card->pcm_mutex); - snd_soc_pcm_component_pm_runtime_put(rtd, substream, rollback); for_each_rtd_components(rtd, i, component) @@ -682,9 +705,21 @@ static int soc_pcm_clean(struct snd_pcm_substream *substream, int rollback) * freed here. The cpu DAI, codec DAI, machine and components are also * shutdown. */ +static int __soc_pcm_close(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream) +{ + return soc_pcm_clean(rtd, substream, 0); +} + +/* PCM close ops for non-DPCM streams */ static int soc_pcm_close(struct snd_pcm_substream *substream) { - return soc_pcm_clean(substream, 0); + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + + snd_soc_dpcm_mutex_lock(rtd); + soc_pcm_clean(rtd, substream, 0); + snd_soc_dpcm_mutex_unlock(rtd); + return 0; } static int soc_hw_sanity_check(struct snd_pcm_substream *substream) @@ -730,21 +765,21 @@ config_err: * then initialized and any private data can be allocated. This also calls * startup for the cpu DAI, component, machine and codec DAI. */ -static int soc_pcm_open(struct snd_pcm_substream *substream) +static int __soc_pcm_open(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_component *component; struct snd_soc_dai *dai; int i, ret = 0; + snd_soc_dpcm_mutex_assert_held(rtd); + for_each_rtd_components(rtd, i, component) pinctrl_pm_select_default_state(component->dev); ret = snd_soc_pcm_component_pm_runtime_get(rtd, substream); if (ret < 0) - goto pm_err; - - mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); + goto err; ret = soc_pcm_components_open(substream); if (ret < 0) @@ -791,16 +826,26 @@ dynamic: snd_soc_runtime_activate(rtd, substream->stream); ret = 0; err: - mutex_unlock(&rtd->card->pcm_mutex); -pm_err: if (ret < 0) { - soc_pcm_clean(substream, 1); + soc_pcm_clean(rtd, substream, 1); dev_err(rtd->dev, "%s() failed (%d)", __func__, ret); } return ret; } +/* PCM open ops for non-DPCM streams */ +static int soc_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + int ret; + + snd_soc_dpcm_mutex_lock(rtd); + ret = __soc_pcm_open(rtd, substream); + snd_soc_dpcm_mutex_unlock(rtd); + return ret; +} + static void codec2codec_close_delayed_work(struct snd_soc_pcm_runtime *rtd) { /* @@ -816,13 +861,13 @@ static void codec2codec_close_delayed_work(struct snd_soc_pcm_runtime *rtd) * rate, etc. This function is non atomic and can be called multiple times, * it can refer to the runtime info. */ -static int soc_pcm_prepare(struct snd_pcm_substream *substream) +static int __soc_pcm_prepare(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *dai; int i, ret = 0; - mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); + snd_soc_dpcm_mutex_assert_held(rtd); ret = snd_soc_link_prepare(substream); if (ret < 0) @@ -850,14 +895,24 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) snd_soc_dai_digital_mute(dai, 0, substream->stream); out: - mutex_unlock(&rtd->card->pcm_mutex); - if (ret < 0) dev_err(rtd->dev, "ASoC: %s() failed (%d)\n", __func__, ret); return ret; } +/* PCM prepare ops for non-DPCM streams */ +static int soc_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + int ret; + + snd_soc_dpcm_mutex_lock(rtd); + ret = __soc_pcm_prepare(rtd, substream); + snd_soc_dpcm_mutex_unlock(rtd); + return ret; +} + static void soc_pcm_codec_params_fixup(struct snd_pcm_hw_params *params, unsigned int mask) { @@ -869,13 +924,13 @@ static void soc_pcm_codec_params_fixup(struct snd_pcm_hw_params *params, interval->max = channels; } -static int soc_pcm_hw_clean(struct snd_pcm_substream *substream, int rollback) +static int soc_pcm_hw_clean(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream, int rollback) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *dai; int i; - mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); + snd_soc_dpcm_mutex_assert_held(rtd); /* clear the corresponding DAIs parameters when going to be inactive */ for_each_rtd_dais(rtd, i, dai) { @@ -900,16 +955,28 @@ static int soc_pcm_hw_clean(struct snd_pcm_substream *substream, int rollback) if (snd_soc_dai_stream_valid(dai, substream->stream)) snd_soc_dai_hw_free(dai, substream, rollback); - mutex_unlock(&rtd->card->pcm_mutex); return 0; } /* * Frees resources allocated by hw_params, can be called multiple times */ +static int __soc_pcm_hw_free(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream) +{ + return soc_pcm_hw_clean(rtd, substream, 0); +} + +/* hw_free PCM ops for non-DPCM streams */ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) { - return soc_pcm_hw_clean(substream, 0); + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + int ret; + + snd_soc_dpcm_mutex_lock(rtd); + ret = __soc_pcm_hw_free(rtd, substream); + snd_soc_dpcm_mutex_unlock(rtd); + return ret; } /* @@ -917,15 +984,15 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) * function can also be called multiple times and can allocate buffers * (using snd_pcm_lib_* ). It's non-atomic. */ -static int soc_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; int i, ret = 0; - mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); + snd_soc_dpcm_mutex_assert_held(rtd); ret = soc_pcm_params_symmetry(substream, params); if (ret) @@ -997,16 +1064,27 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, ret = snd_soc_pcm_component_hw_params(substream, params); out: - mutex_unlock(&rtd->card->pcm_mutex); - if (ret < 0) { - soc_pcm_hw_clean(substream, 1); + soc_pcm_hw_clean(rtd, substream, 1); dev_err(rtd->dev, "ASoC: %s() failed (%d)\n", __func__, ret); } return ret; } +/* hw_params PCM ops for non-DPCM streams */ +static int soc_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + int ret; + + snd_soc_dpcm_mutex_lock(rtd); + ret = __soc_pcm_hw_params(rtd, substream, params); + snd_soc_dpcm_mutex_unlock(rtd); + return ret; +} + static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); @@ -1107,7 +1185,8 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe, struct snd_pcm_substream *fe_substream; struct snd_pcm_substream *be_substream; struct snd_soc_dpcm *dpcm; - unsigned long flags; + + snd_soc_dpcm_mutex_assert_held(fe); /* only add new dpcms */ for_each_dpcm_be(fe, stream, dpcm) { @@ -1137,10 +1216,10 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe, dpcm->fe = fe; be->dpcm[stream].runtime = fe->dpcm[stream].runtime; dpcm->state = SND_SOC_DPCM_LINK_STATE_NEW; - spin_lock_irqsave(&fe->card->dpcm_lock, flags); + snd_soc_dpcm_stream_lock_irq(fe, stream); list_add(&dpcm->list_be, &fe->dpcm[stream].be_clients); list_add(&dpcm->list_fe, &be->dpcm[stream].fe_clients); - spin_unlock_irqrestore(&fe->card->dpcm_lock, flags); + snd_soc_dpcm_stream_unlock_irq(fe, stream); dev_dbg(fe->dev, "connected new DPCM %s path %s %s %s\n", stream ? "capture" : "playback", fe->dai_link->name, @@ -1183,8 +1262,10 @@ static void dpcm_be_reparent(struct snd_soc_pcm_runtime *fe, void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm, *d; - unsigned long flags; + snd_soc_dpcm_mutex_assert_held(fe); + + snd_soc_dpcm_stream_lock_irq(fe, stream); for_each_dpcm_be_safe(fe, stream, dpcm, d) { dev_dbg(fe->dev, "ASoC: BE %s disconnect check for %s\n", stream ? "capture" : "playback", @@ -1202,12 +1283,11 @@ void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream) dpcm_remove_debugfs_state(dpcm); - spin_lock_irqsave(&fe->card->dpcm_lock, flags); list_del(&dpcm->list_be); list_del(&dpcm->list_fe); - spin_unlock_irqrestore(&fe->card->dpcm_lock, flags); kfree(dpcm); } + snd_soc_dpcm_stream_unlock_irq(fe, stream); } /* get BE for DAI widget and stream */ @@ -1431,12 +1511,9 @@ int dpcm_process_paths(struct snd_soc_pcm_runtime *fe, void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm; - unsigned long flags; - spin_lock_irqsave(&fe->card->dpcm_lock, flags); for_each_dpcm_be(fe, stream, dpcm) dpcm_set_be_update_state(dpcm->be, stream, SND_SOC_DPCM_UPDATE_NO); - spin_unlock_irqrestore(&fe->card->dpcm_lock, flags); } void dpcm_be_dai_stop(struct snd_soc_pcm_runtime *fe, int stream, @@ -1472,12 +1549,12 @@ void dpcm_be_dai_stop(struct snd_soc_pcm_runtime *fe, int stream, continue; if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) { - soc_pcm_hw_free(be_substream); + __soc_pcm_hw_free(be, be_substream); be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE; } } - soc_pcm_close(be_substream); + __soc_pcm_close(be, be_substream); be_substream->runtime = NULL; be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; } @@ -1525,7 +1602,7 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) stream ? "capture" : "playback", be->dai_link->name); be_substream->runtime = be->dpcm[stream].runtime; - err = soc_pcm_open(be_substream); + err = __soc_pcm_open(be, be_substream); if (err < 0) { be->dpcm[stream].users--; if (be->dpcm[stream].users < 0) @@ -1769,7 +1846,7 @@ static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream) dev_dbg(fe->dev, "ASoC: open FE %s\n", fe->dai_link->name); /* start the DAI frontend */ - ret = soc_pcm_open(fe_substream); + ret = __soc_pcm_open(fe, fe_substream); if (ret < 0) goto unwind; @@ -1800,6 +1877,8 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); int stream = substream->stream; + snd_soc_dpcm_mutex_assert_held(fe); + dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE); /* shutdown the BEs */ @@ -1808,7 +1887,7 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream) dev_dbg(fe->dev, "ASoC: close FE %s\n", fe->dai_link->name); /* now shutdown the frontend */ - soc_pcm_close(substream); + __soc_pcm_close(fe, substream); /* run the stream stop event */ dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP); @@ -1853,7 +1932,7 @@ void dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream) dev_dbg(be->dev, "ASoC: hw_free BE %s\n", be->dai_link->name); - soc_pcm_hw_free(be_substream); + __soc_pcm_hw_free(be, be_substream); be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE; } @@ -1864,13 +1943,13 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); int stream = substream->stream; - mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + snd_soc_dpcm_mutex_lock(fe); dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE); dev_dbg(fe->dev, "ASoC: hw_free FE %s\n", fe->dai_link->name); /* call hw_free on the frontend */ - soc_pcm_hw_free(substream); + soc_pcm_hw_clean(fe, substream, 0); /* only hw_params backends that are either sinks or sources * to this frontend DAI */ @@ -1879,7 +1958,7 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream) fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE; dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); - mutex_unlock(&fe->card->mutex); + snd_soc_dpcm_mutex_unlock(fe); return 0; } @@ -1923,7 +2002,7 @@ int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream) dev_dbg(be->dev, "ASoC: hw_params BE %s\n", be->dai_link->name); - ret = soc_pcm_hw_params(be_substream, &dpcm->hw_params); + ret = __soc_pcm_hw_params(be, be_substream, &dpcm->hw_params); if (ret < 0) goto unwind; @@ -1953,7 +2032,7 @@ unwind: (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)) continue; - soc_pcm_hw_free(be_substream); + __soc_pcm_hw_free(be, be_substream); } return ret; @@ -1965,7 +2044,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); int ret, stream = substream->stream; - mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + snd_soc_dpcm_mutex_lock(fe); dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE); memcpy(&fe->dpcm[stream].hw_params, params, @@ -1979,7 +2058,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream, params_channels(params), params_format(params)); /* call hw_params on the frontend */ - ret = soc_pcm_hw_params(substream, params); + ret = __soc_pcm_hw_params(fe, substream, params); if (ret < 0) dpcm_be_dai_hw_free(fe, stream); else @@ -1987,7 +2066,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream, out: dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); - mutex_unlock(&fe->card->mutex); + snd_soc_dpcm_mutex_unlock(fe); if (ret < 0) dev_err(fe->dev, "ASoC: %s failed (%d)\n", __func__, ret); @@ -2258,7 +2337,7 @@ int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream) dev_dbg(be->dev, "ASoC: prepare BE %s\n", be->dai_link->name); - ret = soc_pcm_prepare(be_substream); + ret = __soc_pcm_prepare(be, be_substream); if (ret < 0) break; @@ -2276,7 +2355,7 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); int stream = substream->stream, ret = 0; - mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + snd_soc_dpcm_mutex_lock(fe); dev_dbg(fe->dev, "ASoC: prepare FE %s\n", fe->dai_link->name); @@ -2295,7 +2374,7 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream) goto out; /* call prepare on the frontend */ - ret = soc_pcm_prepare(substream); + ret = __soc_pcm_prepare(fe, substream); if (ret < 0) goto out; @@ -2303,7 +2382,7 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream) out: dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); - mutex_unlock(&fe->card->mutex); + snd_soc_dpcm_mutex_unlock(fe); if (ret < 0) dev_err(fe->dev, "ASoC: %s() failed (%d)\n", __func__, ret); @@ -2354,7 +2433,6 @@ static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream) struct snd_soc_dpcm *dpcm; enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream]; int ret = 0; - unsigned long flags; dev_dbg(fe->dev, "ASoC: runtime %s open on FE %s\n", stream ? "capture" : "playback", fe->dai_link->name); @@ -2423,7 +2501,6 @@ close: dpcm_be_dai_shutdown(fe, stream); disconnect: /* disconnect any pending BEs */ - spin_lock_irqsave(&fe->card->dpcm_lock, flags); for_each_dpcm_be(fe, stream, dpcm) { struct snd_soc_pcm_runtime *be = dpcm->be; @@ -2435,7 +2512,6 @@ disconnect: be->dpcm[stream].state == SND_SOC_DPCM_STATE_NEW) dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; } - spin_unlock_irqrestore(&fe->card->dpcm_lock, flags); if (ret < 0) dev_err(fe->dev, "ASoC: %s() failed (%d)\n", __func__, ret); @@ -2510,7 +2586,7 @@ int snd_soc_dpcm_runtime_update(struct snd_soc_card *card) struct snd_soc_pcm_runtime *fe; int ret = 0; - mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + mutex_lock_nested(&card->pcm_mutex, card->pcm_subclass); /* shutdown all old paths first */ for_each_card_rtds(card, fe) { ret = soc_dpcm_fe_runtime_update(fe, 0); @@ -2526,7 +2602,7 @@ int snd_soc_dpcm_runtime_update(struct snd_soc_card *card) } out: - mutex_unlock(&card->mutex); + mutex_unlock(&card->pcm_mutex); return ret; } EXPORT_SYMBOL_GPL(snd_soc_dpcm_runtime_update); @@ -2537,6 +2613,8 @@ static void dpcm_fe_dai_cleanup(struct snd_pcm_substream *fe_substream) struct snd_soc_dpcm *dpcm; int stream = fe_substream->stream; + snd_soc_dpcm_mutex_assert_held(fe); + /* mark FE's links ready to prune */ for_each_dpcm_be(fe, stream, dpcm) dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; @@ -2551,12 +2629,12 @@ static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream) struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(fe_substream); int ret; - mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + snd_soc_dpcm_mutex_lock(fe); ret = dpcm_fe_dai_shutdown(fe_substream); dpcm_fe_dai_cleanup(fe_substream); - mutex_unlock(&fe->card->mutex); + snd_soc_dpcm_mutex_unlock(fe); return ret; } @@ -2567,7 +2645,7 @@ static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream) int ret; int stream = fe_substream->stream; - mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + snd_soc_dpcm_mutex_lock(fe); fe->dpcm[stream].runtime = fe_substream->runtime; ret = dpcm_path_get(fe, stream, &list); @@ -2584,7 +2662,7 @@ static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream) dpcm_clear_pending_state(fe, stream); dpcm_path_put(&list); open_end: - mutex_unlock(&fe->card->mutex); + snd_soc_dpcm_mutex_unlock(fe); return ret; } @@ -2845,10 +2923,8 @@ static int snd_soc_dpcm_check_state(struct snd_soc_pcm_runtime *fe, struct snd_soc_dpcm *dpcm; int state; int ret = 1; - unsigned long flags; int i; - spin_lock_irqsave(&fe->card->dpcm_lock, flags); for_each_dpcm_fe(be, stream, dpcm) { if (dpcm->fe == fe) @@ -2862,7 +2938,6 @@ static int snd_soc_dpcm_check_state(struct snd_soc_pcm_runtime *fe, } } } - spin_unlock_irqrestore(&fe->card->dpcm_lock, flags); /* it's safe to do this BE DAI */ return ret; -- cgit v1.2.3 From b2ae80663008a7662febe7d13f14ea1b2eb0cd51 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 7 Dec 2021 11:37:43 -0600 Subject: ASoC: soc-pcm: serialize BE triggers When more than one FE is connected to a BE, e.g. in a mixing use case, the BE can be triggered multiple times when the FE are opened/started concurrently. This race condition is problematic in the case of SoundWire BE dailinks, and this is not desirable in a general case. This patch relies on the existing BE PCM lock, which takes atomicity into account. The locking model assumes that all interactions start with the FE, so that there is no deadlock between FE and BE locks. Signed-off-by: Takashi Iwai [test, checkpatch fix and clarification of commit message by plbossart] Signed-off-by: Pierre-Louis Bossart Reviewed-by: Kai Vehmanen Reviewed-by: Bard Liao Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20211207173745.15850-5-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/soc-pcm.c | 46 +++++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 2e282c42bac2..7043857e30b1 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -46,12 +46,18 @@ static inline void snd_soc_dpcm_stream_lock_irq(struct snd_soc_pcm_runtime *rtd, snd_pcm_stream_lock_irq(snd_soc_dpcm_get_substream(rtd, stream)); } +#define snd_soc_dpcm_stream_lock_irqsave(rtd, stream, flags) \ + snd_pcm_stream_lock_irqsave(snd_soc_dpcm_get_substream(rtd, stream), flags) + static inline void snd_soc_dpcm_stream_unlock_irq(struct snd_soc_pcm_runtime *rtd, int stream) { snd_pcm_stream_unlock_irq(snd_soc_dpcm_get_substream(rtd, stream)); } +#define snd_soc_dpcm_stream_unlock_irqrestore(rtd, stream, flags) \ + snd_pcm_stream_unlock_irqrestore(snd_soc_dpcm_get_substream(rtd, stream), flags) + #define DPCM_MAX_BE_USERS 8 static inline const char *soc_cpu_dai_name(struct snd_soc_pcm_runtime *rtd) @@ -2079,6 +2085,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, { struct snd_soc_pcm_runtime *be; struct snd_soc_dpcm *dpcm; + unsigned long flags; int ret = 0; for_each_dpcm_be(fe, stream, dpcm) { @@ -2087,9 +2094,11 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, be = dpcm->be; be_substream = snd_soc_dpcm_get_substream(be, stream); + snd_soc_dpcm_stream_lock_irqsave(be, stream, flags); + /* is this op for this BE ? */ if (!snd_soc_dpcm_be_can_update(fe, be, stream)) - continue; + goto next; dev_dbg(be->dev, "ASoC: trigger BE %s cmd %d\n", be->dai_link->name, cmd); @@ -2099,77 +2108,80 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) - continue; + goto next; ret = soc_pcm_trigger(be_substream, cmd); if (ret) - goto end; + goto next; be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; break; case SNDRV_PCM_TRIGGER_RESUME: if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND)) - continue; + goto next; ret = soc_pcm_trigger(be_substream, cmd); if (ret) - goto end; + goto next; be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) - continue; + goto next; ret = soc_pcm_trigger(be_substream, cmd); if (ret) - goto end; + goto next; be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; break; case SNDRV_PCM_TRIGGER_STOP: if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) - continue; + goto next; if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) - continue; + goto next; ret = soc_pcm_trigger(be_substream, cmd); if (ret) - goto end; + goto next; be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP; break; case SNDRV_PCM_TRIGGER_SUSPEND: if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) - continue; + goto next; if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) - continue; + goto next; ret = soc_pcm_trigger(be_substream, cmd); if (ret) - goto end; + goto next; be->dpcm[stream].state = SND_SOC_DPCM_STATE_SUSPEND; break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) - continue; + goto next; if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) - continue; + goto next; ret = soc_pcm_trigger(be_substream, cmd); if (ret) - goto end; + goto next; be->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED; break; } +next: + snd_soc_dpcm_stream_unlock_irqrestore(be, stream, flags); + if (ret) + break; } -end: if (ret < 0) dev_err(fe->dev, "ASoC: %s() failed at %s (%d)\n", __func__, be->dai_link->name, ret); -- cgit v1.2.3 From 848aedfdc6ba25ad5652797db9266007773e44dd Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 7 Dec 2021 11:37:44 -0600 Subject: ASoC: soc-pcm: test refcount before triggering On start/pause_release/resume, when more than one FE is connected to the same BE, it's possible that the trigger is sent more than once. This is not desirable, we only want to trigger a BE once, which is straightforward to implement with a refcount. For stop/pause/suspend, the problem is more complicated: the check implemented in snd_soc_dpcm_can_be_free_stop() may fail due to a conceptual deadlock when we trigger the BE before the FE. In this case, the FE states have not yet changed, so there are corner cases where the TRIGGER_STOP is never sent - the dual case of start where multiple triggers might be sent. This patch suggests an unconditional trigger in all cases, without checking the FE states, using a refcount protected by the BE PCM stream lock. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Kai Vehmanen Reviewed-by: Bard Liao Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20211207173745.15850-6-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- include/sound/soc-dpcm.h | 2 ++ sound/soc/soc-pcm.c | 53 ++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h index bc7af90099a8..75b92d883976 100644 --- a/include/sound/soc-dpcm.h +++ b/include/sound/soc-dpcm.h @@ -101,6 +101,8 @@ struct snd_soc_dpcm_runtime { enum snd_soc_dpcm_state state; int trigger_pending; /* trigger cmd + 1 if pending, 0 if not */ + + int be_start; /* refcount protected by BE stream pcm lock */ }; #define for_each_dpcm_fe(be, stream, _dpcm) \ diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 7043857e30b1..05a0f52eb11b 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1619,7 +1619,7 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; goto unwind; } - + be->dpcm[stream].be_start = 0; be->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN; count++; } @@ -2105,14 +2105,21 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, switch (cmd) { case SNDRV_PCM_TRIGGER_START: - if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) && + if (!be->dpcm[stream].be_start && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) goto next; + be->dpcm[stream].be_start++; + if (be->dpcm[stream].be_start != 1) + goto next; + ret = soc_pcm_trigger(be_substream, cmd); - if (ret) + if (ret) { + be->dpcm[stream].be_start--; goto next; + } be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; break; @@ -2120,9 +2127,15 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND)) goto next; + be->dpcm[stream].be_start++; + if (be->dpcm[stream].be_start != 1) + goto next; + ret = soc_pcm_trigger(be_substream, cmd); - if (ret) + if (ret) { + be->dpcm[stream].be_start--; goto next; + } be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; break; @@ -2130,9 +2143,15 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) goto next; + be->dpcm[stream].be_start++; + if (be->dpcm[stream].be_start != 1) + goto next; + ret = soc_pcm_trigger(be_substream, cmd); - if (ret) + if (ret) { + be->dpcm[stream].be_start--; goto next; + } be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; break; @@ -2141,12 +2160,18 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) goto next; - if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) + if (be->dpcm[stream].state == SND_SOC_DPCM_STATE_START) + be->dpcm[stream].be_start--; + + if (be->dpcm[stream].be_start != 0) goto next; ret = soc_pcm_trigger(be_substream, cmd); - if (ret) + if (ret) { + if (be->dpcm[stream].state == SND_SOC_DPCM_STATE_START) + be->dpcm[stream].be_start++; goto next; + } be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP; break; @@ -2154,12 +2179,15 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) goto next; - if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) + be->dpcm[stream].be_start--; + if (be->dpcm[stream].be_start != 0) goto next; ret = soc_pcm_trigger(be_substream, cmd); - if (ret) + if (ret) { + be->dpcm[stream].be_start++; goto next; + } be->dpcm[stream].state = SND_SOC_DPCM_STATE_SUSPEND; break; @@ -2167,12 +2195,15 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) goto next; - if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) + be->dpcm[stream].be_start--; + if (be->dpcm[stream].be_start != 0) goto next; ret = soc_pcm_trigger(be_substream, cmd); - if (ret) + if (ret) { + be->dpcm[stream].be_start++; goto next; + } be->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED; break; -- cgit v1.2.3 From 3aa1e96a2b95e2ece198f8dd01e96818971b84df Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 7 Dec 2021 11:37:45 -0600 Subject: ASoC: soc-pcm: fix BE handling of PAUSE_RELEASE A BE connected to more than one FE, e.g. in a mixer case, can go through the following transitions. play FE1 -> BE state is START pause FE1 -> BE state is PAUSED play FE2 -> BE state is START stop FE2 -> BE state is STOP (see note [1] below) release FE1 -> BE state is START stop FE1 -> BE state is STOP play FE1 -> BE state is START pause FE1 -> BE state is PAUSED play FE2 -> BE state is START release FE1 -> BE state is START stop FE2 -> BE state is START stop FE1 -> BE state is STOP play FE1 -> BE state is START play FE2 -> BE state is START (no change) pause FE1 -> BE state is START (no change) pause FE2 -> BE state is PAUSED release FE1 -> BE state is START release FE2 -> BE state is START (no change) stop FE1 -> BE state is START (no change) stop FE2 -> BE state is STOP The existing code for PAUSE_RELEASE only allows for the case where the BE is paused, which clearly would not work in the sequences above. Extend the allowed states to restart the BE when PAUSE_RELEASE is received, and increase the refcount if the BE is already in START. [1] the existing logic does not move the BE state back to PAUSED when the FE2 is stopped. This patch does not change the logic; it would be painful to keep a history of changes on the FE side, the state machine is already rather complicated with transitions based on the last BE state and the trigger type. Reported-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Reviewed-by: Kai Vehmanen Reviewed-by: Bard Liao Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20211207173745.15850-7-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/soc-pcm.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 05a0f52eb11b..7abfc48b26ca 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -2140,7 +2140,10 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) + if (!be->dpcm[stream].be_start && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) goto next; be->dpcm[stream].be_start++; -- cgit v1.2.3 From 833a94aac572d7f0fe3f51329e0eb9f2884cf665 Mon Sep 17 00:00:00 2001 From: Judy Hsiao Date: Tue, 14 Dec 2021 10:25:09 +0800 Subject: ASoC: qcom: Distinguish headset codec by codec_dai->name Distinguish which headset codec is on the board by codec_dai->name instead of card->name. It fixes the crash of being unable to handle kernel paging requests at virtual address ADDR by initializing the correct audio codec on the board. Call stack of the crash: ``` Unable to handle kernel paging request at virtual address ... ... Call trace: rt5682_set_component_pll+0xcc/0xb78 [snd_soc_rt5682] snd_soc_component_set_pll+0x90/0x154 snd_soc_dai_set_pll+0xf4/0x1ac sc7180_snd_startup+0x268/0x3c0 [snd_soc_sc7180] snd_soc_link_startup+0xa4/0x180 soc_pcm_open+0x35c/0x15c8 snd_pcm_open_substream+0xa90/0x13b0 snd_pcm_open+0x1a4/0x55c snd_pcm_capture_open+0x7c/0xe8 snd_open+0x2b8/0x2e4 chrdev_open+0x364/0x3d4 do_dentry_open+0x66c/0xc58 vfs_open+0x7c/0x8c path_openat+0x108c/0x2bbc do_filp_open+0x15c/0x258 do_sys_open+0x278/0x62c __arm64_compat_sys_openat+0x9c/0xb0 ... ``` Fixes: 425c5fce8a03 ("ASoC: qcom: Add support for ALC5682I-VS codec") Signed-off-by: Judy Hsiao Link: https://lore.kernel.org/r/20211214022509.1288245-1-judyhsiao@chromium.org Signed-off-by: Mark Brown --- sound/soc/qcom/sc7180.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/qcom/sc7180.c b/sound/soc/qcom/sc7180.c index 2fff764a00a7..37225ef2563a 100644 --- a/sound/soc/qcom/sc7180.c +++ b/sound/soc/qcom/sc7180.c @@ -131,13 +131,13 @@ static int sc7180_snd_startup(struct snd_pcm_substream *substream) struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int pll_id, pll_source, pll_in, pll_out, clk_id, ret; - if (!(strcmp(card->name, "sc7180-rt5682-max98357a-1mic"))) { + if (!strcmp(codec_dai->name, "rt5682-aif1")) { pll_source = RT5682_PLL1_S_MCLK; pll_id = 0; clk_id = RT5682_SCLK_S_PLL1; pll_out = RT5682_PLL1_FREQ; pll_in = DEFAULT_MCLK_RATE; - } else if (!(strcmp(card->name, "sc7180-rt5682s-max98357a-1mic"))) { + } else if (!strcmp(codec_dai->name, "rt5682s-aif1")) { pll_source = RT5682S_PLL_S_MCLK; pll_id = RT5682S_PLL2; clk_id = RT5682S_SCLK_S_PLL2; -- cgit v1.2.3 From 59716aa3f9764144cdd558c64f04cb83001b71ac Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Sat, 11 Dec 2021 06:58:29 +0000 Subject: ASoC: qdsp6: Fix an IS_ERR() vs NULL bug The function gpr_alloc_port return ERR_PTR on errors, it doesn't return null. Signed-off-by: Miaoqian Lin Link: https://lore.kernel.org/r/20211211065840.1221-1-linmq006@gmail.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6apm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 13598ef5bacb..3e007d609a9b 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -630,9 +630,9 @@ struct q6apm_graph *q6apm_graph_open(struct device *dev, q6apm_cb cb, init_waitqueue_head(&graph->cmd_wait); graph->port = gpr_alloc_port(apm->gdev, dev, graph_callback, graph); - if (!graph->port) { + if (IS_ERR(graph->port)) { kfree(graph); - ret = -ENOMEM; + ret = PTR_ERR(graph->port); goto err; } -- cgit v1.2.3 From 28084f4a0e031a87b624ea121bd8fd782b90ff2a Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Wed, 15 Dec 2021 10:57:03 +0200 Subject: ASoC: SOF: OF: Avoid reverse module dependency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similar with commit 8a49cd11e68ed0 ("ASoC: SOF: ACPI: avoid reverse module dependency") we will be having hardware specific drivers that link against a common "helper" framework. sof-of-dev.c becomes a library with the interface defined in the newly created file sof-of-dev.h. This is the final step started with Kconfig simplification in commit 7548a391c53ca ("ASoC: SOF: i.MX: simplify Kconfig") Signed-off-by: Daniel Baluta Reviewed-by: Paul Olaru Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20211215085703.137414-1-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/imx/imx-ops.h | 10 ----- sound/soc/sof/imx/imx8.c | 43 ++++++++++++++++--- sound/soc/sof/imx/imx8m.c | 31 ++++++++++++-- sound/soc/sof/mediatek/mediatek-ops.h | 8 ---- sound/soc/sof/mediatek/mt8195/mt8195.c | 31 ++++++++++++-- sound/soc/sof/sof-of-dev.c | 77 ++++------------------------------ sound/soc/sof/sof-of-dev.h | 17 ++++++++ 7 files changed, 118 insertions(+), 99 deletions(-) delete mode 100644 sound/soc/sof/imx/imx-ops.h delete mode 100644 sound/soc/sof/mediatek/mediatek-ops.h create mode 100644 sound/soc/sof/sof-of-dev.h diff --git a/sound/soc/sof/imx/imx-ops.h b/sound/soc/sof/imx/imx-ops.h deleted file mode 100644 index 24235ef8c8fa..000000000000 --- a/sound/soc/sof/imx/imx-ops.h +++ /dev/null @@ -1,10 +0,0 @@ -/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ - -#ifndef __IMX_OPS_H__ -#define __IMX_OPS_H__ - -extern struct snd_sof_dsp_ops sof_imx8_ops; -extern struct snd_sof_dsp_ops sof_imx8x_ops; -extern struct snd_sof_dsp_ops sof_imx8m_ops; - -#endif diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c index 00b2bb5fd6ae..099b4356122c 100644 --- a/sound/soc/sof/imx/imx8.c +++ b/sound/soc/sof/imx/imx8.c @@ -21,8 +21,8 @@ #include #include #include "../ops.h" +#include "../sof-of-dev.h" #include "imx-common.h" -#include "imx-ops.h" /* DSP memories */ #define IRAM_OFFSET 0x10000 @@ -487,7 +487,7 @@ static int imx8_dsp_set_power_state(struct snd_sof_dev *sdev, } /* i.MX8 ops */ -struct snd_sof_dsp_ops sof_imx8_ops = { +static const struct snd_sof_dsp_ops sof_imx8_ops = { /* probe and remove */ .probe = imx8_probe, .remove = imx8_remove, @@ -548,10 +548,9 @@ struct snd_sof_dsp_ops sof_imx8_ops = { .set_power_state = imx8_dsp_set_power_state, }; -EXPORT_SYMBOL(sof_imx8_ops); /* i.MX8X ops */ -struct snd_sof_dsp_ops sof_imx8x_ops = { +static const struct snd_sof_dsp_ops sof_imx8x_ops = { /* probe and remove */ .probe = imx8_probe, .remove = imx8_remove, @@ -612,7 +611,41 @@ struct snd_sof_dsp_ops sof_imx8x_ops = { SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP }; -EXPORT_SYMBOL(sof_imx8x_ops); + +static struct sof_dev_desc sof_of_imx8qxp_desc = { + .default_fw_path = "imx/sof", + .default_tplg_path = "imx/sof-tplg", + .default_fw_filename = "sof-imx8x.ri", + .nocodec_tplg_filename = "sof-imx8-nocodec.tplg", + .ops = &sof_imx8x_ops, +}; + +static struct sof_dev_desc sof_of_imx8qm_desc = { + .default_fw_path = "imx/sof", + .default_tplg_path = "imx/sof-tplg", + .default_fw_filename = "sof-imx8.ri", + .nocodec_tplg_filename = "sof-imx8-nocodec.tplg", + .ops = &sof_imx8_ops, +}; + +static const struct of_device_id sof_of_imx8_ids[] = { + { .compatible = "fsl,imx8qxp-dsp", .data = &sof_of_imx8qxp_desc}, + { .compatible = "fsl,imx8qm-dsp", .data = &sof_of_imx8qm_desc}, + { } +}; +MODULE_DEVICE_TABLE(of, sof_of_imx8_ids); + +/* DT driver definition */ +static struct platform_driver snd_sof_of_imx8_driver = { + .probe = sof_of_probe, + .remove = sof_of_remove, + .driver = { + .name = "sof-audio-of-imx8", + .pm = &sof_of_pm, + .of_match_table = sof_of_imx8_ids, + }, +}; +module_platform_driver(snd_sof_of_imx8_driver); MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c index b7df655c49c9..c026caea4c8b 100644 --- a/sound/soc/sof/imx/imx8m.c +++ b/sound/soc/sof/imx/imx8m.c @@ -20,8 +20,8 @@ #include #include "../ops.h" +#include "../sof-of-dev.h" #include "imx-common.h" -#include "imx-ops.h" #define MBOX_OFFSET 0x800000 #define MBOX_SIZE 0x1000 @@ -411,7 +411,7 @@ static int imx8m_dsp_suspend(struct snd_sof_dev *sdev, unsigned int target_state } /* i.MX8 ops */ -struct snd_sof_dsp_ops sof_imx8m_ops = { +static const struct snd_sof_dsp_ops sof_imx8m_ops = { /* probe and remove */ .probe = imx8m_probe, .remove = imx8m_remove, @@ -470,7 +470,32 @@ struct snd_sof_dsp_ops sof_imx8m_ops = { SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, }; -EXPORT_SYMBOL(sof_imx8m_ops); + +static struct sof_dev_desc sof_of_imx8mp_desc = { + .default_fw_path = "imx/sof", + .default_tplg_path = "imx/sof-tplg", + .default_fw_filename = "sof-imx8m.ri", + .nocodec_tplg_filename = "sof-imx8-nocodec.tplg", + .ops = &sof_imx8m_ops, +}; + +static const struct of_device_id sof_of_imx8m_ids[] = { + { .compatible = "fsl,imx8mp-dsp", .data = &sof_of_imx8mp_desc}, + { } +}; +MODULE_DEVICE_TABLE(of, sof_of_imx8m_ids); + +/* DT driver definition */ +static struct platform_driver snd_sof_of_imx8m_driver = { + .probe = sof_of_probe, + .remove = sof_of_remove, + .driver = { + .name = "sof-audio-of-imx8m", + .pm = &sof_of_pm, + .of_match_table = sof_of_imx8m_ids, + }, +}; +module_platform_driver(snd_sof_of_imx8m_driver); MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/mediatek/mediatek-ops.h b/sound/soc/sof/mediatek/mediatek-ops.h deleted file mode 100644 index e0ffa69ecb0c..000000000000 --- a/sound/soc/sof/mediatek/mediatek-ops.h +++ /dev/null @@ -1,8 +0,0 @@ -/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ - -#ifndef __MEDIATEK_OPS_H__ -#define __MEDIATEK_OPS_H__ - -extern const struct snd_sof_dsp_ops sof_mt8195_ops; - -#endif diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.c b/sound/soc/sof/mediatek/mt8195/mt8195.c index c719ba470620..3ab12f352935 100644 --- a/sound/soc/sof/mediatek/mt8195/mt8195.c +++ b/sound/soc/sof/mediatek/mt8195/mt8195.c @@ -21,9 +21,9 @@ #include #include #include "../../ops.h" +#include "../../sof-of-dev.h" #include "../../sof-audio.h" #include "../adsp_helper.h" -#include "../mediatek-ops.h" #include "mt8195.h" #include "mt8195-clk.h" @@ -388,7 +388,7 @@ static struct snd_soc_dai_driver mt8195_dai[] = { }; /* mt8195 ops */ -const struct snd_sof_dsp_ops sof_mt8195_ops = { +static const struct snd_sof_dsp_ops sof_mt8195_ops = { /* probe and remove */ .probe = mt8195_dsp_probe, .remove = mt8195_dsp_remove, @@ -432,7 +432,32 @@ const struct snd_sof_dsp_ops sof_mt8195_ops = { SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, }; -EXPORT_SYMBOL(sof_mt8195_ops); + +static const struct sof_dev_desc sof_of_mt8195_desc = { + .default_fw_path = "mediatek/sof", + .default_tplg_path = "mediatek/sof-tplg", + .default_fw_filename = "sof-mt8195.ri", + .nocodec_tplg_filename = "sof-mt8195-nocodec.tplg", + .ops = &sof_mt8195_ops, +}; + +static const struct of_device_id sof_of_mt8195_ids[] = { + { .compatible = "mediatek,mt8195-dsp", .data = &sof_of_mt8195_desc}, + { } +}; +MODULE_DEVICE_TABLE(of, sof_of_mt8195_ids); + +/* DT driver definition */ +static struct platform_driver snd_sof_of_mt8195_driver = { + .probe = sof_of_probe, + .remove = sof_of_remove, + .driver = { + .name = "sof-audio-of-mt8195", + .pm = &sof_of_pm, + .of_match_table = sof_of_mt8195_ids, + }, +}; +module_platform_driver(snd_sof_of_mt8195_driver); MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/sof-of-dev.c b/sound/soc/sof/sof-of-dev.c index b0089698eecb..e3718638f9ce 100644 --- a/sound/soc/sof/sof-of-dev.c +++ b/sound/soc/sof/sof-of-dev.c @@ -11,9 +11,8 @@ #include #include +#include "sof-of-dev.h" #include "ops.h" -#include "imx/imx-ops.h" -#include "mediatek/mediatek-ops.h" static char *fw_path; module_param(fw_path, charp, 0444); @@ -23,51 +22,14 @@ static char *tplg_path; module_param(tplg_path, charp, 0444); MODULE_PARM_DESC(tplg_path, "alternate path for SOF topology."); -/* platform specific devices */ -#if IS_ENABLED(CONFIG_SND_SOC_SOF_IMX8) -static struct sof_dev_desc sof_of_imx8qxp_desc = { - .default_fw_path = "imx/sof", - .default_tplg_path = "imx/sof-tplg", - .default_fw_filename = "sof-imx8x.ri", - .nocodec_tplg_filename = "sof-imx8-nocodec.tplg", - .ops = &sof_imx8x_ops, -}; - -static struct sof_dev_desc sof_of_imx8qm_desc = { - .default_fw_path = "imx/sof", - .default_tplg_path = "imx/sof-tplg", - .default_fw_filename = "sof-imx8.ri", - .nocodec_tplg_filename = "sof-imx8-nocodec.tplg", - .ops = &sof_imx8_ops, -}; -#endif - -#if IS_ENABLED(CONFIG_SND_SOC_SOF_IMX8M) -static struct sof_dev_desc sof_of_imx8mp_desc = { - .default_fw_path = "imx/sof", - .default_tplg_path = "imx/sof-tplg", - .default_fw_filename = "sof-imx8m.ri", - .nocodec_tplg_filename = "sof-imx8-nocodec.tplg", - .ops = &sof_imx8m_ops, -}; -#endif -#if IS_ENABLED(CONFIG_SND_SOC_SOF_MT8195) -static const struct sof_dev_desc sof_of_mt8195_desc = { - .default_fw_path = "mediatek/sof", - .default_tplg_path = "mediatek/sof-tplg", - .default_fw_filename = "sof-mt8195.ri", - .nocodec_tplg_filename = "sof-mt8195-nocodec.tplg", - .ops = &sof_mt8195_ops, -}; -#endif - -static const struct dev_pm_ops sof_of_pm = { +const struct dev_pm_ops sof_of_pm = { .prepare = snd_sof_prepare, .complete = snd_sof_complete, SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume) SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume, NULL) }; +EXPORT_SYMBOL(sof_of_pm); static void sof_of_probe_complete(struct device *dev) { @@ -79,7 +41,7 @@ static void sof_of_probe_complete(struct device *dev) pm_runtime_enable(dev); } -static int sof_of_probe(struct platform_device *pdev) +int sof_of_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; const struct sof_dev_desc *desc; @@ -120,8 +82,9 @@ static int sof_of_probe(struct platform_device *pdev) /* call sof helper for DSP hardware probe */ return snd_sof_device_probe(dev, sof_pdata); } +EXPORT_SYMBOL(sof_of_probe); -static int sof_of_remove(struct platform_device *pdev) +int sof_of_remove(struct platform_device *pdev) { pm_runtime_disable(&pdev->dev); @@ -130,32 +93,6 @@ static int sof_of_remove(struct platform_device *pdev) return 0; } - -static const struct of_device_id sof_of_ids[] = { -#if IS_ENABLED(CONFIG_SND_SOC_SOF_IMX8) - { .compatible = "fsl,imx8qxp-dsp", .data = &sof_of_imx8qxp_desc}, - { .compatible = "fsl,imx8qm-dsp", .data = &sof_of_imx8qm_desc}, -#endif -#if IS_ENABLED(CONFIG_SND_SOC_SOF_IMX8M) - { .compatible = "fsl,imx8mp-dsp", .data = &sof_of_imx8mp_desc}, -#endif -#if IS_ENABLED(CONFIG_SND_SOC_SOF_MT8195) - { .compatible = "mediatek,mt8195-dsp", .data = &sof_of_mt8195_desc}, -#endif - { } -}; -MODULE_DEVICE_TABLE(of, sof_of_ids); - -/* DT driver definition */ -static struct platform_driver snd_sof_of_driver = { - .probe = sof_of_probe, - .remove = sof_of_remove, - .driver = { - .name = "sof-audio-of", - .pm = &sof_of_pm, - .of_match_table = sof_of_ids, - }, -}; -module_platform_driver(snd_sof_of_driver); +EXPORT_SYMBOL(sof_of_remove); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/sof-of-dev.h b/sound/soc/sof/sof-of-dev.h new file mode 100644 index 000000000000..4e0f6588dad9 --- /dev/null +++ b/sound/soc/sof/sof-of-dev.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright 2021 NXP + */ + +#ifndef __SOUND_SOC_SOF_OF_H +#define __SOUND_SOC_SOF_OF_H + +extern const struct dev_pm_ops sof_of_pm; + +int sof_of_probe(struct platform_device *pdev); +int sof_of_remove(struct platform_device *pdev); + +#endif -- cgit v1.2.3 From 2167c0b205960607fb136b4bb3c556a62be1569a Mon Sep 17 00:00:00 2001 From: Jiasheng Jiang Date: Wed, 15 Dec 2021 11:15:50 +0800 Subject: ASoC: rt5663: Handle device_property_read_u32_array error codes The return value of device_property_read_u32_array() is not always 0. To catch the exception in case that devm_kzalloc failed and the rt5663->imp_table was NULL, which caused the failure of device_property_read_u32_array. Fixes: 450f0f6a8fb4 ("ASoC: rt5663: Add the manual offset field to compensate the DC offset") Signed-off-by: Jiasheng Jiang Link: https://lore.kernel.org/r/20211215031550.70702-1-jiasheng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/rt5663.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c index 0389b2bb360e..2138f62e6af5 100644 --- a/sound/soc/codecs/rt5663.c +++ b/sound/soc/codecs/rt5663.c @@ -3461,6 +3461,7 @@ static void rt5663_calibrate(struct rt5663_priv *rt5663) static int rt5663_parse_dp(struct rt5663_priv *rt5663, struct device *dev) { int table_size; + int ret; device_property_read_u32(dev, "realtek,dc_offset_l_manual", &rt5663->pdata.dc_offset_l_manual); @@ -3477,9 +3478,11 @@ static int rt5663_parse_dp(struct rt5663_priv *rt5663, struct device *dev) table_size = sizeof(struct impedance_mapping_table) * rt5663->pdata.impedance_sensing_num; rt5663->imp_table = devm_kzalloc(dev, table_size, GFP_KERNEL); - device_property_read_u32_array(dev, + ret = device_property_read_u32_array(dev, "realtek,impedance_sensing_table", (u32 *)rt5663->imp_table, table_size); + if (ret) + return ret; } return 0; @@ -3504,8 +3507,11 @@ static int rt5663_i2c_probe(struct i2c_client *i2c, if (pdata) rt5663->pdata = *pdata; - else - rt5663_parse_dp(rt5663, &i2c->dev); + else { + ret = rt5663_parse_dp(rt5663, &i2c->dev); + if (ret) + return ret; + } for (i = 0; i < ARRAY_SIZE(rt5663->supplies); i++) rt5663->supplies[i].supply = rt5663_supply_names[i]; -- cgit v1.2.3 From 9d562fdcd52b1bb1a13cd5078ffc06dd3eff3aef Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 15 Dec 2021 10:03:57 -0800 Subject: ASoC: SOF: ipc: Rename send parameter in snd_sof_ipc_set_get_comp_data() Rename the send parameter to set in snd_sof_ipc_set_get_comp_data() and sof_set_get_large_ctrl_data() to be more aligned with the function name. No functional change. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20211215180404.53254-2-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc.c | 17 ++++++++--------- sound/soc/sof/sof-audio.h | 3 +-- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 6771b444065d..670d780241a3 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -745,7 +745,7 @@ static int sof_get_ctrl_copy_params(enum sof_ipc_ctrl_type ctrl_type, static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev, struct sof_ipc_ctrl_data *cdata, struct sof_ipc_ctrl_data_params *sparams, - bool send) + bool set) { struct sof_ipc_ctrl_data *partdata; size_t send_bytes; @@ -760,7 +760,7 @@ static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev, if (!partdata) return -ENOMEM; - if (send) + if (set) err = sof_get_ctrl_copy_params(cdata->type, cdata, partdata, sparams); else @@ -789,7 +789,7 @@ static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev, msg_bytes -= send_bytes; partdata->elems_remaining = msg_bytes; - if (send) + if (set) memcpy(sparams->dst, sparams->src + offset, send_bytes); err = sof_ipc_tx_message_unlocked(sdev->ipc, @@ -801,7 +801,7 @@ static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev, if (err < 0) break; - if (!send) + if (!set) memcpy(sparams->dst + offset, sparams->src, send_bytes); offset += pl_size; @@ -819,8 +819,7 @@ static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev, int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, u32 ipc_cmd, enum sof_ipc_ctrl_type ctrl_type, - enum sof_ipc_ctrl_cmd ctrl_cmd, - bool send) + enum sof_ipc_ctrl_cmd ctrl_cmd, bool set) { struct snd_soc_component *scomp = scontrol->scomp; struct sof_ipc_ctrl_data *cdata = scontrol->control_data; @@ -858,7 +857,7 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, /* write/read value header via mmaped region */ send_bytes = sizeof(struct sof_ipc_ctrl_value_chan) * cdata->num_elems; - if (send) + if (set) err = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_IRAM, scontrol->readback_offset, cdata->chanv, send_bytes); @@ -870,7 +869,7 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, if (err) dev_err_once(sdev->dev, "error: %s TYPE_IRAM failed\n", - send ? "write to" : "read from"); + set ? "write to" : "read from"); return err; } @@ -934,7 +933,7 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, return -EINVAL; } - err = sof_set_get_large_ctrl_data(sdev, cdata, &sparams, send); + err = sof_set_get_large_ctrl_data(sdev, cdata, &sparams, set); if (err < 0) dev_err(sdev->dev, "error: set/get large ctrl ipc comp %d\n", diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index e419e7082c28..1c1d68e220d5 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -242,8 +242,7 @@ static inline void snd_sof_compr_init_elapsed_work(struct work_struct *work) { } int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, u32 ipc_cmd, enum sof_ipc_ctrl_type ctrl_type, - enum sof_ipc_ctrl_cmd ctrl_cmd, - bool send); + enum sof_ipc_ctrl_cmd ctrl_cmd, bool set); /* DAI link fixup */ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params); -- cgit v1.2.3 From d4a06c4334aed1fe76ae2b7aaae6ee8b72f30a8e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 15 Dec 2021 10:03:58 -0800 Subject: ASoC: SOF: Drop ipc_cmd parameter for snd_sof_ipc_set_get_comp_data() The correct ipc_cmd can be selected based on the `ctrl_cmd` and the `set` parameters: if the ctrl_cmd is SOF_CTRL_CMD_BINARY then SOF_IPC_COMP_*_DATA otherwise SOF_IPC_COMP_*_VALUE. The SET or GET direction can be selected with the use of `set` parameter. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20211215180404.53254-3-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/control.c | 15 ++------------- sound/soc/sof/ipc.c | 8 +++++++- sound/soc/sof/sof-audio.c | 6 ++---- sound/soc/sof/sof-audio.h | 1 - 4 files changed, 11 insertions(+), 19 deletions(-) diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index bb1dfe4f6d40..299ee466625e 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -69,7 +69,6 @@ static void snd_sof_refresh_control(struct snd_sof_control *scontrol) { struct sof_ipc_ctrl_data *cdata = scontrol->control_data; struct snd_soc_component *scomp = scontrol->scomp; - u32 ipc_cmd; int ret; if (!scontrol->comp_data_dirty) @@ -78,18 +77,13 @@ static void snd_sof_refresh_control(struct snd_sof_control *scontrol) if (!pm_runtime_active(scomp->dev)) return; - if (scontrol->cmd == SOF_CTRL_CMD_BINARY) - ipc_cmd = SOF_IPC_COMP_GET_DATA; - else - ipc_cmd = SOF_IPC_COMP_GET_VALUE; - /* set the ABI header values */ cdata->data->magic = SOF_ABI_MAGIC; cdata->data->abi = SOF_ABI_VERSION; /* refresh the component data from DSP */ scontrol->comp_data_dirty = false; - ret = snd_sof_ipc_set_get_comp_data(scontrol, ipc_cmd, + ret = snd_sof_ipc_set_get_comp_data(scontrol, SOF_CTRL_TYPE_VALUE_CHAN_GET, scontrol->cmd, false); if (ret < 0) { @@ -143,7 +137,6 @@ int snd_sof_volume_put(struct snd_kcontrol *kcontrol, /* notify DSP of mixer updates */ if (pm_runtime_active(scomp->dev)) snd_sof_ipc_set_get_comp_data(scontrol, - SOF_IPC_COMP_SET_VALUE, SOF_CTRL_TYPE_VALUE_CHAN_SET, SOF_CTRL_CMD_VOLUME, true); @@ -216,7 +209,6 @@ int snd_sof_switch_put(struct snd_kcontrol *kcontrol, /* notify DSP of mixer updates */ if (pm_runtime_active(scomp->dev)) snd_sof_ipc_set_get_comp_data(scontrol, - SOF_IPC_COMP_SET_VALUE, SOF_CTRL_TYPE_VALUE_CHAN_SET, SOF_CTRL_CMD_SWITCH, true); @@ -265,7 +257,6 @@ int snd_sof_enum_put(struct snd_kcontrol *kcontrol, /* notify DSP of enum updates */ if (pm_runtime_active(scomp->dev)) snd_sof_ipc_set_get_comp_data(scontrol, - SOF_IPC_COMP_SET_VALUE, SOF_CTRL_TYPE_VALUE_CHAN_SET, SOF_CTRL_CMD_ENUM, true); @@ -343,7 +334,6 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, /* notify DSP of byte control updates */ if (pm_runtime_active(scomp->dev)) snd_sof_ipc_set_get_comp_data(scontrol, - SOF_IPC_COMP_SET_DATA, SOF_CTRL_TYPE_DATA_SET, scontrol->cmd, true); @@ -423,7 +413,6 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, /* notify DSP of byte control updates */ if (pm_runtime_active(scomp->dev)) snd_sof_ipc_set_get_comp_data(scontrol, - SOF_IPC_COMP_SET_DATA, SOF_CTRL_TYPE_DATA_SET, scontrol->cmd, true); @@ -463,7 +452,7 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _ cdata->data->magic = SOF_ABI_MAGIC; cdata->data->abi = SOF_ABI_VERSION; /* get all the component data from DSP */ - ret = snd_sof_ipc_set_get_comp_data(scontrol, SOF_IPC_COMP_GET_DATA, SOF_CTRL_TYPE_DATA_GET, + ret = snd_sof_ipc_set_get_comp_data(scontrol, SOF_CTRL_TYPE_DATA_GET, scontrol->cmd, false); if (ret < 0) goto out; diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 670d780241a3..bcfe7edee05e 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -817,7 +817,6 @@ static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev, * IPC get()/set() for kcontrols. */ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, - u32 ipc_cmd, enum sof_ipc_ctrl_type ctrl_type, enum sof_ipc_ctrl_cmd ctrl_cmd, bool set) { @@ -830,6 +829,7 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, struct snd_sof_widget *swidget; bool widget_found = false; size_t send_bytes; + u32 ipc_cmd; int err; list_for_each_entry(swidget, &sdev->widget_list, list) { @@ -873,6 +873,12 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, return err; } + /* Select the IPC cmd based on the ctrl_cmd and the direction */ + if (ctrl_cmd == SOF_CTRL_CMD_BINARY) + ipc_cmd = set ? SOF_IPC_COMP_SET_DATA : SOF_IPC_COMP_GET_DATA; + else + ipc_cmd = set ? SOF_IPC_COMP_SET_VALUE : SOF_IPC_COMP_GET_VALUE; + cdata->rhdr.hdr.cmd = SOF_IPC_GLB_COMP_MSG | ipc_cmd; cdata->cmd = ctrl_cmd; cdata->type = ctrl_type; diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 58a62bfb16ab..dacc0122c3b4 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -14,7 +14,7 @@ static int sof_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) { - int ipc_cmd, ctrl_type; + enum sof_ipc_ctrl_type ctrl_type; int ret; /* reset readback offset for scontrol */ @@ -25,18 +25,16 @@ static int sof_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_control * case SOF_CTRL_CMD_VOLUME: case SOF_CTRL_CMD_ENUM: case SOF_CTRL_CMD_SWITCH: - ipc_cmd = SOF_IPC_COMP_SET_VALUE; ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_SET; break; case SOF_CTRL_CMD_BINARY: - ipc_cmd = SOF_IPC_COMP_SET_DATA; ctrl_type = SOF_CTRL_TYPE_DATA_SET; break; default: return 0; } - ret = snd_sof_ipc_set_get_comp_data(scontrol, ipc_cmd, ctrl_type, scontrol->cmd, true); + ret = snd_sof_ipc_set_get_comp_data(scontrol, ctrl_type, scontrol->cmd, true); if (ret < 0) dev_err(sdev->dev, "error: failed kcontrol value set for widget: %d\n", scontrol->comp_id); diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 1c1d68e220d5..f4316cd742a7 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -240,7 +240,6 @@ static inline void snd_sof_compr_init_elapsed_work(struct work_struct *work) { } * Mixer IPC */ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, - u32 ipc_cmd, enum sof_ipc_ctrl_type ctrl_type, enum sof_ipc_ctrl_cmd ctrl_cmd, bool set); -- cgit v1.2.3 From 8af783723f41d3b3d4f7f8452f190405e7059472 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 15 Dec 2021 10:03:59 -0800 Subject: ASoC: SOF: topology: Set control_data->cmd alongside scontrol->cmd Set the scontrol->control_data->cmd early to the same as scontrol->cmd. This is a preparatory patch to remove the ctrl_cmd parameter for the snd_sof_ipc_set_get_comp_data() function. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20211215180404.53254-4-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index b3ad3a604918..c440e1c53ca5 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1091,10 +1091,12 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, /* set cmd for mixer control */ if (le32_to_cpu(mc->max) == 1) { scontrol->cmd = SOF_CTRL_CMD_SWITCH; + scontrol->control_data->cmd = scontrol->cmd; goto skip; } scontrol->cmd = SOF_CTRL_CMD_VOLUME; + scontrol->control_data->cmd = scontrol->cmd; /* extract tlv data */ if (!kc->tlv.p || get_tlv_data(kc->tlv.p, tlv) < 0) { @@ -1166,6 +1168,7 @@ static int sof_control_load_enum(struct snd_soc_component *scomp, scontrol->num_channels = le32_to_cpu(ec->num_channels); scontrol->control_data->index = kc->index; scontrol->cmd = SOF_CTRL_CMD_ENUM; + scontrol->control_data->cmd = scontrol->cmd; dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d comp_id %d\n", scontrol->comp_id, scontrol->num_channels, scontrol->comp_id); @@ -1212,6 +1215,7 @@ static int sof_control_load_bytes(struct snd_soc_component *scomp, scontrol->comp_id = sdev->next_comp_id; scontrol->cmd = SOF_CTRL_CMD_BINARY; + scontrol->control_data->cmd = scontrol->cmd; scontrol->control_data->index = kc->index; dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d\n", -- cgit v1.2.3 From 9182f3c40b52ebd91d4796d96186ba10b720b4ba Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 15 Dec 2021 10:04:00 -0800 Subject: ASoC: SOF: Drop ctrl_cmd parameter for snd_sof_ipc_set_get_comp_data() The scontrol->control_data->cmd has been configured during initialization to the correct sof_ipc_ctrl_cmd. No need to pass duplicated information, let's use the already available one via scontrol. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20211215180404.53254-5-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/control.c | 26 +++++++------------------- sound/soc/sof/ipc.c | 6 ++---- sound/soc/sof/sof-audio.c | 2 +- sound/soc/sof/sof-audio.h | 3 +-- 4 files changed, 11 insertions(+), 26 deletions(-) diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 299ee466625e..23a916ea93f8 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -84,8 +84,7 @@ static void snd_sof_refresh_control(struct snd_sof_control *scontrol) /* refresh the component data from DSP */ scontrol->comp_data_dirty = false; ret = snd_sof_ipc_set_get_comp_data(scontrol, - SOF_CTRL_TYPE_VALUE_CHAN_GET, - scontrol->cmd, false); + SOF_CTRL_TYPE_VALUE_CHAN_GET, false); if (ret < 0) { dev_err(scomp->dev, "error: failed to get control data: %d\n", ret); /* Set the flag to re-try next time to get the data */ @@ -137,9 +136,7 @@ int snd_sof_volume_put(struct snd_kcontrol *kcontrol, /* notify DSP of mixer updates */ if (pm_runtime_active(scomp->dev)) snd_sof_ipc_set_get_comp_data(scontrol, - SOF_CTRL_TYPE_VALUE_CHAN_SET, - SOF_CTRL_CMD_VOLUME, - true); + SOF_CTRL_TYPE_VALUE_CHAN_SET, true); return change; } @@ -209,9 +206,7 @@ int snd_sof_switch_put(struct snd_kcontrol *kcontrol, /* notify DSP of mixer updates */ if (pm_runtime_active(scomp->dev)) snd_sof_ipc_set_get_comp_data(scontrol, - SOF_CTRL_TYPE_VALUE_CHAN_SET, - SOF_CTRL_CMD_SWITCH, - true); + SOF_CTRL_TYPE_VALUE_CHAN_SET, true); return change; } @@ -257,9 +252,7 @@ int snd_sof_enum_put(struct snd_kcontrol *kcontrol, /* notify DSP of enum updates */ if (pm_runtime_active(scomp->dev)) snd_sof_ipc_set_get_comp_data(scontrol, - SOF_CTRL_TYPE_VALUE_CHAN_SET, - SOF_CTRL_CMD_ENUM, - true); + SOF_CTRL_TYPE_VALUE_CHAN_SET, true); return change; } @@ -334,9 +327,7 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, /* notify DSP of byte control updates */ if (pm_runtime_active(scomp->dev)) snd_sof_ipc_set_get_comp_data(scontrol, - SOF_CTRL_TYPE_DATA_SET, - scontrol->cmd, - true); + SOF_CTRL_TYPE_DATA_SET, true); return 0; } @@ -413,9 +404,7 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, /* notify DSP of byte control updates */ if (pm_runtime_active(scomp->dev)) snd_sof_ipc_set_get_comp_data(scontrol, - SOF_CTRL_TYPE_DATA_SET, - scontrol->cmd, - true); + SOF_CTRL_TYPE_DATA_SET, true); return 0; } @@ -452,8 +441,7 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _ cdata->data->magic = SOF_ABI_MAGIC; cdata->data->abi = SOF_ABI_VERSION; /* get all the component data from DSP */ - ret = snd_sof_ipc_set_get_comp_data(scontrol, SOF_CTRL_TYPE_DATA_GET, - scontrol->cmd, false); + ret = snd_sof_ipc_set_get_comp_data(scontrol, SOF_CTRL_TYPE_DATA_GET, false); if (ret < 0) goto out; diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index bcfe7edee05e..69c8a9964960 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -817,8 +817,7 @@ static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev, * IPC get()/set() for kcontrols. */ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, - enum sof_ipc_ctrl_type ctrl_type, - enum sof_ipc_ctrl_cmd ctrl_cmd, bool set) + enum sof_ipc_ctrl_type ctrl_type, bool set) { struct snd_soc_component *scomp = scontrol->scomp; struct sof_ipc_ctrl_data *cdata = scontrol->control_data; @@ -874,13 +873,12 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, } /* Select the IPC cmd based on the ctrl_cmd and the direction */ - if (ctrl_cmd == SOF_CTRL_CMD_BINARY) + if (cdata->cmd == SOF_CTRL_CMD_BINARY) ipc_cmd = set ? SOF_IPC_COMP_SET_DATA : SOF_IPC_COMP_GET_DATA; else ipc_cmd = set ? SOF_IPC_COMP_SET_VALUE : SOF_IPC_COMP_GET_VALUE; cdata->rhdr.hdr.cmd = SOF_IPC_GLB_COMP_MSG | ipc_cmd; - cdata->cmd = ctrl_cmd; cdata->type = ctrl_type; cdata->comp_id = scontrol->comp_id; cdata->msg_index = 0; diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index dacc0122c3b4..269eca26eab9 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -34,7 +34,7 @@ static int sof_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_control * return 0; } - ret = snd_sof_ipc_set_get_comp_data(scontrol, ctrl_type, scontrol->cmd, true); + ret = snd_sof_ipc_set_get_comp_data(scontrol, ctrl_type, true); if (ret < 0) dev_err(sdev->dev, "error: failed kcontrol value set for widget: %d\n", scontrol->comp_id); diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index f4316cd742a7..5bcc842e4792 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -240,8 +240,7 @@ static inline void snd_sof_compr_init_elapsed_work(struct work_struct *work) { } * Mixer IPC */ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, - enum sof_ipc_ctrl_type ctrl_type, - enum sof_ipc_ctrl_cmd ctrl_cmd, bool set); + enum sof_ipc_ctrl_type ctrl_type, bool set); /* DAI link fixup */ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params); -- cgit v1.2.3 From dd2fef982ff75fbae618cc274fda09bd40582acd Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 15 Dec 2021 10:04:01 -0800 Subject: ASoC: SOF: sof-audio: Drop the `cmd` member from struct snd_sof_control There is no need to use two variables to store and check the same information, the scontrol->cmd is the same as scontrol->control_data->cmd. Drop the former one and when it is needed, access the cmd from the control_data. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20211215180404.53254-6-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/control.c | 6 +++--- sound/soc/sof/sof-audio.c | 2 +- sound/soc/sof/sof-audio.h | 1 - sound/soc/sof/topology.c | 14 +++++--------- 4 files changed, 9 insertions(+), 14 deletions(-) diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 23a916ea93f8..9297b29d65cd 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -372,7 +372,7 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, } /* Check that header id matches the command */ - if (header.numid != scontrol->cmd) { + if (header.numid != cdata->cmd) { dev_err_ratelimited(scomp->dev, "error: incorrect numid %d\n", header.numid); @@ -462,7 +462,7 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _ goto out; } - header.numid = scontrol->cmd; + header.numid = cdata->cmd; header.length = data_size; if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv))) { ret = -EFAULT; @@ -522,7 +522,7 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, if (data_size > size) return -ENOSPC; - header.numid = scontrol->cmd; + header.numid = cdata->cmd; header.length = data_size; if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv))) return -EFAULT; diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 269eca26eab9..4530c6ed34e0 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -21,7 +21,7 @@ static int sof_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_control * scontrol->readback_offset = 0; /* notify DSP of kcontrol values */ - switch (scontrol->cmd) { + switch (scontrol->control_data->cmd) { case SOF_CTRL_CMD_VOLUME: case SOF_CTRL_CMD_ENUM: case SOF_CTRL_CMD_SWITCH: diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 5bcc842e4792..84a8ebe3b1c3 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -74,7 +74,6 @@ struct snd_sof_control { u32 readback_offset; /* offset to mmapped data if used */ struct sof_ipc_ctrl_data *control_data; u32 size; /* cdata size */ - enum sof_ipc_ctrl_cmd cmd; u32 *volume_table; /* volume table computed from tlv data*/ struct list_head list; /* list in sdev control list */ diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index c440e1c53ca5..ec59baf32699 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1090,13 +1090,11 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, /* set cmd for mixer control */ if (le32_to_cpu(mc->max) == 1) { - scontrol->cmd = SOF_CTRL_CMD_SWITCH; - scontrol->control_data->cmd = scontrol->cmd; + scontrol->control_data->cmd = SOF_CTRL_CMD_SWITCH; goto skip; } - scontrol->cmd = SOF_CTRL_CMD_VOLUME; - scontrol->control_data->cmd = scontrol->cmd; + scontrol->control_data->cmd = SOF_CTRL_CMD_VOLUME; /* extract tlv data */ if (!kc->tlv.p || get_tlv_data(kc->tlv.p, tlv) < 0) { @@ -1167,8 +1165,7 @@ static int sof_control_load_enum(struct snd_soc_component *scomp, scontrol->comp_id = sdev->next_comp_id; scontrol->num_channels = le32_to_cpu(ec->num_channels); scontrol->control_data->index = kc->index; - scontrol->cmd = SOF_CTRL_CMD_ENUM; - scontrol->control_data->cmd = scontrol->cmd; + scontrol->control_data->cmd = SOF_CTRL_CMD_ENUM; dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d comp_id %d\n", scontrol->comp_id, scontrol->num_channels, scontrol->comp_id); @@ -1214,8 +1211,7 @@ static int sof_control_load_bytes(struct snd_soc_component *scomp, } scontrol->comp_id = sdev->next_comp_id; - scontrol->cmd = SOF_CTRL_CMD_BINARY; - scontrol->control_data->cmd = scontrol->cmd; + scontrol->control_data->cmd = SOF_CTRL_CMD_BINARY; scontrol->control_data->index = kc->index; dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d\n", @@ -2080,7 +2076,7 @@ static int sof_get_control_data(struct snd_soc_component *scomp, *size += wdata[i].pdata->size; /* get data type */ - switch (wdata[i].control->cmd) { + switch (wdata[i].control->control_data->cmd) { case SOF_CTRL_CMD_VOLUME: case SOF_CTRL_CMD_ENUM: case SOF_CTRL_CMD_SWITCH: -- cgit v1.2.3 From 68be4f0ed40cce833cb313871c52878025e40596 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 15 Dec 2021 10:04:02 -0800 Subject: ASoC: SOF: control: Do not handle control notification with component type The component type is not used in firmware nor in the kernel currently and it is not even clear how it should be handled. Do not even try to handle it to avoid errors. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20211215180404.53254-7-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/control.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 9297b29d65cd..dac0b630b6a0 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -577,6 +577,13 @@ void snd_sof_control_notify(struct snd_sof_dev *sdev, bool found = false; int i, type; + if (cdata->type == SOF_CTRL_TYPE_VALUE_COMP_GET || + cdata->type == SOF_CTRL_TYPE_VALUE_COMP_SET) { + dev_err(sdev->dev, + "Component data is not supported in control notification\n"); + return; + } + /* Find the swidget first */ list_for_each_entry(swidget, &sdev->widget_list, list) { if (swidget->comp_id == cdata->comp_id) { @@ -643,11 +650,6 @@ void snd_sof_control_notify(struct snd_sof_dev *sdev, expected_size += cdata->num_elems * sizeof(struct sof_ipc_ctrl_value_chan); break; - case SOF_CTRL_TYPE_VALUE_COMP_GET: - case SOF_CTRL_TYPE_VALUE_COMP_SET: - expected_size += cdata->num_elems * - sizeof(struct sof_ipc_ctrl_value_comp); - break; case SOF_CTRL_TYPE_DATA_GET: case SOF_CTRL_TYPE_DATA_SET: expected_size += cdata->num_elems + sizeof(struct sof_abi_hdr); -- cgit v1.2.3 From 47d7328f8cda15e60422c8ca36d067c4deb19b7e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 15 Dec 2021 10:04:03 -0800 Subject: ASoC: SOF: Drop ctrl_type parameter for snd_sof_ipc_set_get_comp_data() The SOF_CTRL_TYPE_VALUE_COMP_* type is not used by the firmware nor in the kernel side. It is also not clear what action should be taken for such type. With this in mind: The correct ipc_cmd can be selected based on the `ctrl_cmd` and the `set` parameters: if the ctrl_cmd is SOF_CTRL_CMD_BINARY then SOF_CTRL_TYPE_DATA_* otherwise SOF_CTRL_TYPE_VALUE_CHAN_*. The SET or GET direction can be selected with the use of `set` parameter. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20211215180404.53254-8-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/control.c | 20 +++++++------------- sound/soc/sof/ipc.c | 30 +++++++++++++----------------- sound/soc/sof/sof-audio.c | 17 +---------------- sound/soc/sof/sof-audio.h | 3 +-- 4 files changed, 22 insertions(+), 48 deletions(-) diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index dac0b630b6a0..ef61936dad59 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -83,8 +83,7 @@ static void snd_sof_refresh_control(struct snd_sof_control *scontrol) /* refresh the component data from DSP */ scontrol->comp_data_dirty = false; - ret = snd_sof_ipc_set_get_comp_data(scontrol, - SOF_CTRL_TYPE_VALUE_CHAN_GET, false); + ret = snd_sof_ipc_set_get_comp_data(scontrol, false); if (ret < 0) { dev_err(scomp->dev, "error: failed to get control data: %d\n", ret); /* Set the flag to re-try next time to get the data */ @@ -135,8 +134,7 @@ int snd_sof_volume_put(struct snd_kcontrol *kcontrol, /* notify DSP of mixer updates */ if (pm_runtime_active(scomp->dev)) - snd_sof_ipc_set_get_comp_data(scontrol, - SOF_CTRL_TYPE_VALUE_CHAN_SET, true); + snd_sof_ipc_set_get_comp_data(scontrol, true); return change; } @@ -205,8 +203,7 @@ int snd_sof_switch_put(struct snd_kcontrol *kcontrol, /* notify DSP of mixer updates */ if (pm_runtime_active(scomp->dev)) - snd_sof_ipc_set_get_comp_data(scontrol, - SOF_CTRL_TYPE_VALUE_CHAN_SET, true); + snd_sof_ipc_set_get_comp_data(scontrol, true); return change; } @@ -251,8 +248,7 @@ int snd_sof_enum_put(struct snd_kcontrol *kcontrol, /* notify DSP of enum updates */ if (pm_runtime_active(scomp->dev)) - snd_sof_ipc_set_get_comp_data(scontrol, - SOF_CTRL_TYPE_VALUE_CHAN_SET, true); + snd_sof_ipc_set_get_comp_data(scontrol, true); return change; } @@ -326,8 +322,7 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, /* notify DSP of byte control updates */ if (pm_runtime_active(scomp->dev)) - snd_sof_ipc_set_get_comp_data(scontrol, - SOF_CTRL_TYPE_DATA_SET, true); + snd_sof_ipc_set_get_comp_data(scontrol, true); return 0; } @@ -403,8 +398,7 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, /* notify DSP of byte control updates */ if (pm_runtime_active(scomp->dev)) - snd_sof_ipc_set_get_comp_data(scontrol, - SOF_CTRL_TYPE_DATA_SET, true); + snd_sof_ipc_set_get_comp_data(scontrol, true); return 0; } @@ -441,7 +435,7 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _ cdata->data->magic = SOF_ABI_MAGIC; cdata->data->abi = SOF_ABI_VERSION; /* get all the component data from DSP */ - ret = snd_sof_ipc_set_get_comp_data(scontrol, SOF_CTRL_TYPE_DATA_GET, false); + ret = snd_sof_ipc_set_get_comp_data(scontrol, false); if (ret < 0) goto out; diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 69c8a9964960..8a1eacc7ec5f 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -721,11 +721,6 @@ static int sof_get_ctrl_copy_params(enum sof_ipc_ctrl_type ctrl_type, sparams->src = (u8 *)src->chanv; sparams->dst = (u8 *)dst->chanv; break; - case SOF_CTRL_TYPE_VALUE_COMP_GET: - case SOF_CTRL_TYPE_VALUE_COMP_SET: - sparams->src = (u8 *)src->compv; - sparams->dst = (u8 *)dst->compv; - break; case SOF_CTRL_TYPE_DATA_GET: case SOF_CTRL_TYPE_DATA_SET: sparams->src = (u8 *)src->data->data; @@ -816,8 +811,7 @@ static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev, /* * IPC get()/set() for kcontrols. */ -int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, - enum sof_ipc_ctrl_type ctrl_type, bool set) +int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, bool set) { struct snd_soc_component *scomp = scontrol->scomp; struct sof_ipc_ctrl_data *cdata = scontrol->control_data; @@ -825,6 +819,7 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, struct sof_ipc_fw_ready *ready = &sdev->fw_ready; struct sof_ipc_fw_version *v = &ready->version; struct sof_ipc_ctrl_data_params sparams; + enum sof_ipc_ctrl_type ctrl_type; struct snd_sof_widget *swidget; bool widget_found = false; size_t send_bytes; @@ -872,11 +867,19 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, return err; } - /* Select the IPC cmd based on the ctrl_cmd and the direction */ - if (cdata->cmd == SOF_CTRL_CMD_BINARY) + /* + * Select the IPC cmd and the ctrl_type based on the ctrl_cmd and the + * direction + * Note: SOF_CTRL_TYPE_VALUE_COMP_* is not used and supported currently + * for ctrl_type + */ + if (cdata->cmd == SOF_CTRL_CMD_BINARY) { ipc_cmd = set ? SOF_IPC_COMP_SET_DATA : SOF_IPC_COMP_GET_DATA; - else + ctrl_type = set ? SOF_CTRL_TYPE_DATA_SET : SOF_CTRL_TYPE_DATA_GET; + } else { ipc_cmd = set ? SOF_IPC_COMP_SET_VALUE : SOF_IPC_COMP_GET_VALUE; + ctrl_type = set ? SOF_CTRL_TYPE_VALUE_CHAN_SET : SOF_CTRL_TYPE_VALUE_CHAN_GET; + } cdata->rhdr.hdr.cmd = SOF_IPC_GLB_COMP_MSG | ipc_cmd; cdata->type = ctrl_type; @@ -892,13 +895,6 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, sparams.hdr_bytes = sizeof(struct sof_ipc_ctrl_data); sparams.elems = scontrol->num_channels; break; - case SOF_CTRL_TYPE_VALUE_COMP_GET: - case SOF_CTRL_TYPE_VALUE_COMP_SET: - sparams.msg_bytes = scontrol->num_channels * - sizeof(struct sof_ipc_ctrl_value_comp); - sparams.hdr_bytes = sizeof(struct sof_ipc_ctrl_data); - sparams.elems = scontrol->num_channels; - break; case SOF_CTRL_TYPE_DATA_GET: case SOF_CTRL_TYPE_DATA_SET: sparams.msg_bytes = cdata->data->size; diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 4530c6ed34e0..735fbc5fe1bd 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -14,27 +14,12 @@ static int sof_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) { - enum sof_ipc_ctrl_type ctrl_type; int ret; /* reset readback offset for scontrol */ scontrol->readback_offset = 0; - /* notify DSP of kcontrol values */ - switch (scontrol->control_data->cmd) { - case SOF_CTRL_CMD_VOLUME: - case SOF_CTRL_CMD_ENUM: - case SOF_CTRL_CMD_SWITCH: - ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_SET; - break; - case SOF_CTRL_CMD_BINARY: - ctrl_type = SOF_CTRL_TYPE_DATA_SET; - break; - default: - return 0; - } - - ret = snd_sof_ipc_set_get_comp_data(scontrol, ctrl_type, true); + ret = snd_sof_ipc_set_get_comp_data(scontrol, true); if (ret < 0) dev_err(sdev->dev, "error: failed kcontrol value set for widget: %d\n", scontrol->comp_id); diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 84a8ebe3b1c3..f3009e6b91a1 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -238,8 +238,7 @@ static inline void snd_sof_compr_init_elapsed_work(struct work_struct *work) { } /* * Mixer IPC */ -int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, - enum sof_ipc_ctrl_type ctrl_type, bool set); +int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, bool set); /* DAI link fixup */ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params); -- cgit v1.2.3 From fc5adc2bb13a6988df7ce377320f381add236002 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 15 Dec 2021 10:04:04 -0800 Subject: ASoC: SOF: topology: read back control data from DSP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Read back the control data from the DSP to initialize the control data size to match that of the data in the DSP. This is particularly useful for volatile read-only kcontrols in static pipelines. Signed-off-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Reviewed-by: Péter Ujfalusi Link: https://lore.kernel.org/r/20211215180404.53254-9-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-audio.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 735fbc5fe1bd..91e3fa5a7350 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -59,12 +59,26 @@ static int sof_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_wi /* set up all controls for the widget */ list_for_each_entry(scontrol, &sdev->kcontrol_list, list) if (scontrol->comp_id == swidget->comp_id) { + /* set kcontrol data in DSP */ ret = sof_kcontrol_setup(sdev, scontrol); if (ret < 0) { dev_err(sdev->dev, "error: fail to set up kcontrols for widget %s\n", swidget->widget->name); return ret; } + + /* + * Read back the data from the DSP for static widgets. This is particularly + * useful for binary kcontrols associated with static pipeline widgets to + * initialize the data size to match that in the DSP. + */ + if (swidget->dynamic_pipeline_widget) + continue; + + ret = snd_sof_ipc_set_get_comp_data(scontrol, false); + if (ret < 0) + dev_warn(sdev->dev, "Failed kcontrol get for control in widget %s\n", + swidget->widget->name); } return 0; -- cgit v1.2.3 From 88dffe43cbc625eb52a57daa0d1c0fb7037b63d2 Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Sat, 11 Dec 2021 23:49:44 +0100 Subject: ASoC: nvidia,tegra-audio: Convert multiple txt bindings to yaml Convert Tegra audio complex with the * ALC5632 * MAX98090 * RT5640 * RT5677 * SGTL5000 * TrimSlice * WM8753 * WM8903 * WM9712 codec to the YAML format. Additional changes: - added missing HPOUTL to the WM9712 codec. - extended rt5677 codec with multiple pins Reviewed-by: Dmitry Osipenko Signed-off-by: David Heidelberg Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20211211224946.79875-1-david@ixit.cz Signed-off-by: Mark Brown --- .../bindings/sound/nvidia,tegra-audio-alc5632.txt | 48 ---------- .../bindings/sound/nvidia,tegra-audio-alc5632.yaml | 74 +++++++++++++++ .../bindings/sound/nvidia,tegra-audio-common.yaml | 83 +++++++++++++++++ .../bindings/sound/nvidia,tegra-audio-max98090.txt | 53 ----------- .../sound/nvidia,tegra-audio-max98090.yaml | 97 +++++++++++++++++++ .../bindings/sound/nvidia,tegra-audio-rt5640.txt | 52 ----------- .../bindings/sound/nvidia,tegra-audio-rt5640.yaml | 85 +++++++++++++++++ .../bindings/sound/nvidia,tegra-audio-rt5677.txt | 67 -------------- .../bindings/sound/nvidia,tegra-audio-rt5677.yaml | 103 +++++++++++++++++++++ .../bindings/sound/nvidia,tegra-audio-sgtl5000.txt | 42 --------- .../sound/nvidia,tegra-audio-sgtl5000.yaml | 67 ++++++++++++++ .../sound/nvidia,tegra-audio-trimslice.txt | 21 ----- .../sound/nvidia,tegra-audio-trimslice.yaml | 33 +++++++ .../bindings/sound/nvidia,tegra-audio-wm8753.txt | 40 -------- .../bindings/sound/nvidia,tegra-audio-wm8753.yaml | 79 ++++++++++++++++ .../bindings/sound/nvidia,tegra-audio-wm8903.txt | 62 ------------- .../bindings/sound/nvidia,tegra-audio-wm8903.yaml | 93 +++++++++++++++++++ .../bindings/sound/nvidia,tegra-audio-wm9712.txt | 60 ------------ .../bindings/sound/nvidia,tegra-audio-wm9712.yaml | 76 +++++++++++++++ 19 files changed, 790 insertions(+), 445 deletions(-) delete mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.txt create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.yaml create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra-audio-common.yaml delete mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra-audio-max98090.txt create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra-audio-max98090.yaml delete mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5640.txt create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5640.yaml delete mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5677.txt create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5677.yaml delete mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra-audio-sgtl5000.txt create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra-audio-sgtl5000.yaml delete mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra-audio-trimslice.txt create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra-audio-trimslice.yaml delete mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8753.txt create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8753.yaml delete mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.txt create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.yaml delete mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.yaml diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.txt deleted file mode 100644 index 57f40f93453e..000000000000 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.txt +++ /dev/null @@ -1,48 +0,0 @@ -NVIDIA Tegra audio complex - -Required properties: -- compatible : "nvidia,tegra-audio-alc5632" -- clocks : Must contain an entry for each entry in clock-names. - See ../clocks/clock-bindings.txt for details. -- clock-names : Must include the following entries: - - pll_a - - pll_a_out0 - - mclk (The Tegra cdev1/extern1 clock, which feeds the CODEC's mclk) -- nvidia,model : The user-visible name of this sound complex. -- nvidia,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. Valid names for sources and - sinks are the ALC5632's pins as documented in the binding for the device - and: - - * Headset Stereophone - * Int Spk - * Headset Mic - * Digital Mic - -- nvidia,i2s-controller : The phandle of the Tegra I2S controller -- nvidia,audio-codec : The phandle of the ALC5632 audio codec - -Example: - -sound { - compatible = "nvidia,tegra-audio-alc5632-paz00", - "nvidia,tegra-audio-alc5632"; - - nvidia,model = "Compal PAZ00"; - - nvidia,audio-routing = - "Int Spk", "SPK_OUTP", - "Int Spk", "SPK_OUTN", - "Headset Mic","MICBIAS1", - "MIC1_N", "Headset Mic", - "MIC1_P", "Headset Mic", - "Headset Stereophone", "HP_OUT_R", - "Headset Stereophone", "HP_OUT_L"; - - nvidia,i2s-controller = <&tegra_i2s1>; - nvidia,audio-codec = <&alc5632>; - - clocks = <&tegra_car 112>, <&tegra_car 113>, <&tegra_car 93>; - clock-names = "pll_a", "pll_a_out0", "mclk"; -}; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.yaml new file mode 100644 index 000000000000..7ef774910e5c --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.yaml @@ -0,0 +1,74 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nvidia,tegra-audio-alc5632.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra audio complex with ALC5632 CODEC + +maintainers: + - Jon Hunter + - Thierry Reding + +allOf: + - $ref: nvidia,tegra-audio-common.yaml# + +properties: + compatible: + items: + - pattern: '^[a-z0-9]+,tegra-audio-alc5632(-[a-z0-9]+)+$' + - const: nvidia,tegra-audio-alc5632 + + nvidia,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. Valid names for sources and + sinks are the pins (documented in the binding document), + and the jacks on the board. + minItems: 2 + items: + enum: + # Board Connectors + - "Headset Stereophone" + - "Int Spk" + - "Headset Mic" + - "Digital Mic" + + # CODEC Pins + - SPKOUT + - SPKOUTN + - MICBIAS1 + - MIC1 + - HPR + - HPL + - DMICDAT + +required: + - nvidia,i2s-controller + +unevaluatedProperties: false + +examples: + - | + sound { + compatible = "nvidia,tegra-audio-alc5632-paz00", + "nvidia,tegra-audio-alc5632"; + + nvidia,model = "Compal PAZ00"; + + nvidia,audio-routing = "Int Spk", "SPKOUT", + "Int Spk", "SPKOUTN", + "Headset Mic", "MICBIAS1", + "MIC1", "Headset Mic", + "Headset Stereophone", "HPR", + "Headset Stereophone", "HPL", + "DMICDAT", "Digital Mic"; + + nvidia,i2s-controller = <&i2s>; + nvidia,audio-codec = <&codec>; + + clocks = <&clk 112>, <&clk 113>, <&clk 93>; + clock-names = "pll_a", "pll_a_out0", "mclk"; + }; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-common.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-common.yaml new file mode 100644 index 000000000000..82801b4f46dd --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-common.yaml @@ -0,0 +1,83 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/sound/nvidia,tegra-audio-common.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Common properties for NVIDIA Tegra audio complexes + +maintainers: + - Jon Hunter + - Thierry Reding + +properties: + clocks: + items: + - description: PLL A clock + - description: PLL A OUT0 clock + - description: The Tegra cdev1/extern1 clock, which feeds the card's mclk + + clock-names: + items: + - const: pll_a + - const: pll_a_out0 + - const: mclk + + nvidia,model: + $ref: /schemas/types.yaml#/definitions/string + description: The user-visible name of this sound complex. + + nvidia,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. Valid names for sources and + sinks are the pins (documented in the binding document), + and the jacks on the board. + + nvidia,ac97-controller: + $ref: /schemas/types.yaml#/definitions/phandle + description: The phandle of the AC97 controller + + nvidia,i2s-controller: + $ref: /schemas/types.yaml#/definitions/phandle + description: The phandle of the Tegra I2S controller + + nvidia,audio-codec: + $ref: /schemas/types.yaml#/definitions/phandle + description: The phandle of audio codec + + nvidia,spkr-en-gpios: + maxItems: 1 + description: The GPIO that enables the speakers + + nvidia,hp-mute-gpios: + maxItems: 1 + description: The GPIO that mutes the headphones + + nvidia,hp-det-gpios: + maxItems: 1 + description: The GPIO that detect headphones are plugged in + + nvidia,mic-det-gpios: + maxItems: 1 + description: The GPIO that detect microphone is plugged in + + nvidia,ear-sel-gpios: + maxItems: 1 + description: The GPIO that switch between the microphones + + nvidia,int-mic-en-gpios: + maxItems: 1 + description: The GPIO that enables the internal microphone + + nvidia,ext-mic-en-gpios: + maxItems: 1 + description: The GPIO that enables the external microphone + + nvidia,headset: + type: boolean + description: The Mic Jack represents state of the headset microphone pin + +additionalProperties: true diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-max98090.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-max98090.txt deleted file mode 100644 index c3495beba358..000000000000 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-max98090.txt +++ /dev/null @@ -1,53 +0,0 @@ -NVIDIA Tegra audio complex, with MAX98090 CODEC - -Required properties: -- compatible : "nvidia,tegra-audio-max98090" -- clocks : Must contain an entry for each entry in clock-names. - See ../clocks/clock-bindings.txt for details. -- clock-names : Must include the following entries: - - pll_a - - pll_a_out0 - - mclk (The Tegra cdev1/extern1 clock, which feeds the CODEC's mclk) -- nvidia,model : The user-visible name of this sound complex. -- nvidia,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. Valid names for sources and - sinks are the MAX98090's pins (as documented in its binding), and the jacks - on the board: - - * Headphones - * Speakers - * Mic Jack - * Int Mic - -- nvidia,i2s-controller : The phandle of the Tegra I2S controller that's - connected to the CODEC. -- nvidia,audio-codec : The phandle of the MAX98090 audio codec. - -Optional properties: -- nvidia,hp-det-gpios : The GPIO that detect headphones are plugged in -- nvidia,mic-det-gpios : The GPIO that detect microphones are plugged in - -Example: - -sound { - compatible = "nvidia,tegra-audio-max98090-venice2", - "nvidia,tegra-audio-max98090"; - nvidia,model = "NVIDIA Tegra Venice2"; - - nvidia,audio-routing = - "Headphones", "HPR", - "Headphones", "HPL", - "Speakers", "SPKR", - "Speakers", "SPKL", - "Mic Jack", "MICBIAS", - "IN34", "Mic Jack"; - - nvidia,i2s-controller = <&tegra_i2s1>; - nvidia,audio-codec = <&acodec>; - - clocks = <&tegra_car TEGRA124_CLK_PLL_A>, - <&tegra_car TEGRA124_CLK_PLL_A_OUT0>, - <&tegra_car TEGRA124_CLK_EXTERN1>; - clock-names = "pll_a", "pll_a_out0", "mclk"; -}; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-max98090.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-max98090.yaml new file mode 100644 index 000000000000..ccc2ee77ca30 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-max98090.yaml @@ -0,0 +1,97 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nvidia,tegra-audio-max98090.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra audio complex with MAX98090 CODEC + +maintainers: + - Jon Hunter + - Thierry Reding + +allOf: + - $ref: nvidia,tegra-audio-common.yaml# + +properties: + compatible: + oneOf: + - items: + - pattern: '^[a-z0-9]+,tegra-audio-max98090(-[a-z0-9]+)+$' + - const: nvidia,tegra-audio-max98090 + - items: + - enum: + - nvidia,tegra-audio-max98090-nyan-big + - nvidia,tegra-audio-max98090-nyan-blaze + - const: nvidia,tegra-audio-max98090-nyan + - const: nvidia,tegra-audio-max98090 + + nvidia,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. Valid names for sources and + sinks are the pins (documented in the binding document), + and the jacks on the board. + minItems: 2 + items: + enum: + # Board Connectors + - "Headphones" + - "Speakers" + - "Mic Jack" + - "Int Mic" + + # CODEC Pins + - MIC1 + - MIC2 + - DMICL + - DMICR + - IN1 + - IN2 + - IN3 + - IN4 + - IN5 + - IN6 + - IN12 + - IN34 + - IN56 + - HPL + - HPR + - SPKL + - SPKR + - RCVL + - RCVR + - MICBIAS + +required: + - nvidia,i2s-controller + +unevaluatedProperties: false + +examples: + - | + #include + + sound { + compatible = "nvidia,tegra-audio-max98090-venice2", + "nvidia,tegra-audio-max98090"; + nvidia,model = "NVIDIA Tegra Venice2"; + + nvidia,audio-routing = + "Headphones", "HPR", + "Headphones", "HPL", + "Speakers", "SPKR", + "Speakers", "SPKL", + "Mic Jack", "MICBIAS", + "IN34", "Mic Jack"; + + nvidia,i2s-controller = <&tegra_i2s1>; + nvidia,audio-codec = <&acodec>; + + clocks = <&tegra_car TEGRA124_CLK_PLL_A>, + <&tegra_car TEGRA124_CLK_PLL_A_OUT0>, + <&tegra_car TEGRA124_CLK_EXTERN1>; + clock-names = "pll_a", "pll_a_out0", "mclk"; + }; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5640.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5640.txt deleted file mode 100644 index 7788808dcd0b..000000000000 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5640.txt +++ /dev/null @@ -1,52 +0,0 @@ -NVIDIA Tegra audio complex, with RT5640 CODEC - -Required properties: -- compatible : "nvidia,tegra-audio-rt5640" -- clocks : Must contain an entry for each entry in clock-names. - See ../clocks/clock-bindings.txt for details. -- clock-names : Must include the following entries: - - pll_a - - pll_a_out0 - - mclk (The Tegra cdev1/extern1 clock, which feeds the CODEC's mclk) -- nvidia,model : The user-visible name of this sound complex. -- nvidia,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. Valid names for sources and - sinks are the RT5640's pins (as documented in its binding), and the jacks - on the board: - - * Headphones - * Speakers - * Mic Jack - -- nvidia,i2s-controller : The phandle of the Tegra I2S controller that's - connected to the CODEC. -- nvidia,audio-codec : The phandle of the RT5640 audio codec. This binding - assumes that AIF1 on the CODEC is connected to Tegra. - -Optional properties: -- nvidia,hp-det-gpios : The GPIO that detects headphones are plugged in - -Example: - -sound { - compatible = "nvidia,tegra-audio-rt5640-dalmore", - "nvidia,tegra-audio-rt5640"; - nvidia,model = "NVIDIA Tegra Dalmore"; - - nvidia,audio-routing = - "Headphones", "HPOR", - "Headphones", "HPOL", - "Speakers", "SPORP", - "Speakers", "SPORN", - "Speakers", "SPOLP", - "Speakers", "SPOLN"; - - nvidia,i2s-controller = <&tegra_i2s1>; - nvidia,audio-codec = <&rt5640>; - - nvidia,hp-det-gpios = <&gpio 143 0>; /* GPIO PR7 */ - - clocks = <&tegra_car 216>, <&tegra_car 217>, <&tegra_car 120>; - clock-names = "pll_a", "pll_a_out0", "mclk"; -}; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5640.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5640.yaml new file mode 100644 index 000000000000..e768fb0e9a59 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5640.yaml @@ -0,0 +1,85 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nvidia,tegra-audio-rt5640.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra audio complex with RT5639 or RT5640 CODEC + +maintainers: + - Jon Hunter + - Thierry Reding + +allOf: + - $ref: nvidia,tegra-audio-common.yaml# + +properties: + compatible: + items: + - pattern: '^[a-z0-9]+,tegra-audio-rt56(39|40)(-[a-z0-9]+)+$' + - const: nvidia,tegra-audio-rt5640 + + nvidia,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. Valid names for sources and + sinks are the pins (documented in the binding document), + and the jacks on the board. + minItems: 2 + items: + enum: + # Board Connectors + - "Headphones" + - "Speakers" + - "Mic Jack" + + # CODEC Pins + - DMIC1 + - DMIC2 + - MICBIAS1 + - IN1P + - IN1R + - IN2P + - IN2R + - HPOL + - HPOR + - LOUTL + - LOUTR + - MONOP + - MONON + - SPOLP + - SPOLN + - SPORP + - SPORN + +required: + - nvidia,i2s-controller + +unevaluatedProperties: false + +examples: + - | + sound { + compatible = "nvidia,tegra-audio-rt5640-dalmore", + "nvidia,tegra-audio-rt5640"; + nvidia,model = "NVIDIA Tegra Dalmore"; + + nvidia,audio-routing = + "Headphones", "HPOR", + "Headphones", "HPOL", + "Speakers", "SPORP", + "Speakers", "SPORN", + "Speakers", "SPOLP", + "Speakers", "SPOLN"; + + nvidia,i2s-controller = <&tegra_i2s1>; + nvidia,audio-codec = <&rt5640>; + + nvidia,hp-det-gpios = <&gpio 143 0>; + + clocks = <&clk 216>, <&clk 217>, <&clk 120>; + clock-names = "pll_a", "pll_a_out0", "mclk"; + }; + diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5677.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5677.txt deleted file mode 100644 index a4589cda214e..000000000000 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5677.txt +++ /dev/null @@ -1,67 +0,0 @@ -NVIDIA Tegra audio complex, with RT5677 CODEC - -Required properties: -- compatible : "nvidia,tegra-audio-rt5677" -- clocks : Must contain an entry for each entry in clock-names. - See ../clocks/clock-bindings.txt for details. -- clock-names : Must include the following entries: - - pll_a - - pll_a_out0 - - mclk (The Tegra cdev1/extern1 clock, which feeds the CODEC's mclk) -- nvidia,model : The user-visible name of this sound complex. -- nvidia,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. Valid names for sources and - sinks are the RT5677's pins (as documented in its binding), and the jacks - on the board: - - * Headphone - * Speaker - * Headset Mic - * Internal Mic 1 - * Internal Mic 2 - -- nvidia,i2s-controller : The phandle of the Tegra I2S controller that's - connected to the CODEC. -- nvidia,audio-codec : The phandle of the RT5677 audio codec. This binding - assumes that AIF1 on the CODEC is connected to Tegra. - -Optional properties: -- nvidia,hp-det-gpios : The GPIO that detects headphones are plugged in -- nvidia,hp-en-gpios : The GPIO that enables headphone amplifier -- nvidia,mic-present-gpios: The GPIO that mic jack is plugged in -- nvidia,dmic-clk-en-gpios : The GPIO that gates DMIC clock signal - -Example: - -sound { - compatible = "nvidia,tegra-audio-rt5677-ryu", - "nvidia,tegra-audio-rt5677"; - nvidia,model = "NVIDIA Tegra Ryu"; - - nvidia,audio-routing = - "Headphone", "LOUT2", - "Headphone", "LOUT1", - "Headset Mic", "MICBIAS1", - "IN1P", "Headset Mic", - "IN1N", "Headset Mic", - "DMIC L1", "Internal Mic 1", - "DMIC R1", "Internal Mic 1", - "DMIC L2", "Internal Mic 2", - "DMIC R2", "Internal Mic 2", - "Speaker", "PDM1L", - "Speaker", "PDM1R"; - - nvidia,i2s-controller = <&tegra_i2s1>; - nvidia,audio-codec = <&rt5677>; - - nvidia,hp-det-gpios = <&gpio TEGRA_GPIO(R, 7) GPIO_ACTIVE_HIGH>; - nvidia,mic-present-gpios = <&gpio TEGRA_GPIO(O, 5) GPIO_ACTIVE_LOW>; - nvidia,hp-en-gpios = <&rt5677 1 GPIO_ACTIVE_HIGH>; - nvidia,dmic-clk-en-gpios = <&rt5677 2 GPIO_ACTIVE_HIGH>; - - clocks = <&tegra_car TEGRA124_CLK_PLL_A>, - <&tegra_car TEGRA124_CLK_PLL_A_OUT0>, - <&tegra_car TEGRA124_CLK_EXTERN1>; - clock-names = "pll_a", "pll_a_out0", "mclk"; -}; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5677.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5677.yaml new file mode 100644 index 000000000000..03ff691c26c8 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5677.yaml @@ -0,0 +1,103 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nvidia,tegra-audio-rt5677.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra audio complex with RT5677 CODEC + +maintainers: + - Jon Hunter + - Thierry Reding + +allOf: + - $ref: nvidia,tegra-audio-common.yaml# + +properties: + compatible: + items: + - pattern: '^[a-z0-9]+,tegra-audio-rt5677(-[a-z0-9]+)+$' + - const: nvidia,tegra-audio-rt5677 + + nvidia,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. Valid names for sources and + sinks are the pins (documented in the binding document), + and the jacks on the board. + minItems: 2 + items: + enum: + # Board Connectors + - "Headphone" + - "Speaker" + - "Headset Mic" + - "Internal Mic 1" + - "Internal Mic 2" + + # CODEC Pins + - IN1P + - IN1N + - IN2P + - IN2N + - MICBIAS1 + - DMIC1 + - DMIC2 + - DMIC3 + - DMIC4 + - "DMIC L1" + - "DMIC L2" + - "DMIC L3" + - "DMIC L4" + - "DMIC R1" + - "DMIC R2" + - "DMIC R3" + - "DMIC R4" + - LOUT1 + - LOUT2 + - LOUT3 + - PDM1L + - PDM1R + - PDM2L + - PDM2R + +required: + - nvidia,i2s-controller + +unevaluatedProperties: false + +examples: + - | + sound { + compatible = "nvidia,tegra-audio-rt5677-ryu", + "nvidia,tegra-audio-rt5677"; + nvidia,model = "NVIDIA Tegra Ryu"; + + nvidia,audio-routing = + "Headphone", "LOUT2", + "Headphone", "LOUT1", + "Headset Mic", "MICBIAS1", + "IN1P", "Headset Mic", + "IN1N", "Headset Mic", + "DMIC L1", "Internal Mic 1", + "DMIC R1", "Internal Mic 1", + "DMIC L2", "Internal Mic 2", + "DMIC R2", "Internal Mic 2", + "Speaker", "PDM1L", + "Speaker", "PDM1R"; + + nvidia,i2s-controller = <&tegra_i2s1>; + nvidia,audio-codec = <&rt5677>; + + nvidia,hp-det-gpios = <&gpio 143 0>; + nvidia,mic-present-gpios = <&gpio 132 1>; + nvidia,hp-en-gpios = <&rt5677 1 0>; + nvidia,dmic-clk-en-gpios = <&rt5677 2 1>; + + clocks = <&clk 216>, + <&clk 217>, + <&clk 121>; + clock-names = "pll_a", "pll_a_out0", "mclk"; + }; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-sgtl5000.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-sgtl5000.txt deleted file mode 100644 index 5da7da4ea07a..000000000000 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-sgtl5000.txt +++ /dev/null @@ -1,42 +0,0 @@ -NVIDIA Tegra audio complex, with SGTL5000 CODEC - -Required properties: -- compatible : "nvidia,tegra-audio-sgtl5000" -- clocks : Must contain an entry for each entry in clock-names. - See ../clocks/clock-bindings.txt for details. -- clock-names : Must include the following entries: - - pll_a - - pll_a_out0 - - mclk (The Tegra cdev1/extern1 clock, which feeds the CODEC's mclk) -- nvidia,model : The user-visible name of this sound complex. -- nvidia,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. Valid names for sources and - sinks are the SGTL5000's pins (as documented in its binding), and the jacks - on the board: - - * Headphone Jack - * Line In Jack - * Mic Jack - -- nvidia,i2s-controller : The phandle of the Tegra I2S controller that's - connected to the CODEC. -- nvidia,audio-codec : The phandle of the SGTL5000 audio codec. - -Example: - -sound { - compatible = "toradex,tegra-audio-sgtl5000-apalis_t30", - "nvidia,tegra-audio-sgtl5000"; - nvidia,model = "Toradex Apalis T30"; - nvidia,audio-routing = - "Headphone Jack", "HP_OUT", - "LINE_IN", "Line In Jack", - "MIC_IN", "Mic Jack"; - nvidia,i2s-controller = <&tegra_i2s2>; - nvidia,audio-codec = <&sgtl5000>; - clocks = <&tegra_car TEGRA30_CLK_PLL_A>, - <&tegra_car TEGRA30_CLK_PLL_A_OUT0>, - <&tegra_car TEGRA30_CLK_EXTERN1>; - clock-names = "pll_a", "pll_a_out0", "mclk"; -}; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-sgtl5000.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-sgtl5000.yaml new file mode 100644 index 000000000000..943e7c01741c --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-sgtl5000.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nvidia,tegra-audio-sgtl5000.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra audio complex with SGTL5000 CODEC + +maintainers: + - Jon Hunter + - Thierry Reding + +allOf: + - $ref: nvidia,tegra-audio-common.yaml# + +properties: + compatible: + items: + - pattern: '^[a-z0-9]+,tegra-audio-sgtl5000([-_][a-z0-9]+)+$' + - const: nvidia,tegra-audio-sgtl5000 + + nvidia,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. Valid names for sources and + sinks are the pins (documented in the binding document), + and the jacks on the board. + minItems: 2 + items: + enum: + # Board Connectors + - "Headphone Jack" + - "Line In Jack" + - "Mic Jack" + + # CODEC Pins + - HP_OUT + - LINE_OUT + - LINE_IN + - MIC_IN + +required: + - nvidia,i2s-controller + +unevaluatedProperties: false + +examples: + - | + #include + + sound { + compatible = "toradex,tegra-audio-sgtl5000-apalis_t30", + "nvidia,tegra-audio-sgtl5000"; + nvidia,model = "Toradex Apalis T30 SGTL5000"; + nvidia,audio-routing = + "Headphone Jack", "HP_OUT", + "LINE_IN", "Line In Jack", + "MIC_IN", "Mic Jack"; + nvidia,i2s-controller = <&tegra_i2s2>; + nvidia,audio-codec = <&codec>; + clocks = <&tegra_car TEGRA30_CLK_PLL_A>, + <&tegra_car TEGRA30_CLK_PLL_A_OUT0>, + <&tegra_car TEGRA30_CLK_EXTERN1>; + clock-names = "pll_a", "pll_a_out0", "mclk"; + }; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-trimslice.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-trimslice.txt deleted file mode 100644 index ef1fe7358279..000000000000 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-trimslice.txt +++ /dev/null @@ -1,21 +0,0 @@ -NVIDIA Tegra audio complex for TrimSlice - -Required properties: -- compatible : "nvidia,tegra-audio-trimslice" -- clocks : Must contain an entry for each entry in clock-names. -- clock-names : Must include the following entries: - "pll_a" (The Tegra clock of that name), - "pll_a_out0" (The Tegra clock of that name), - "mclk" (The Tegra cdev1/extern1 clock, which feeds the CODEC's mclk) -- nvidia,i2s-controller : The phandle of the Tegra I2S1 controller -- nvidia,audio-codec : The phandle of the WM8903 audio codec - -Example: - -sound { - compatible = "nvidia,tegra-audio-trimslice"; - nvidia,i2s-controller = <&tegra_i2s1>; - nvidia,audio-codec = <&codec>; - clocks = <&tegra_car 112>, <&tegra_car 113>, <&tegra_car 93>; - clock-names = "pll_a", "pll_a_out0", "mclk"; -}; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-trimslice.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-trimslice.yaml new file mode 100644 index 000000000000..8c87cd166238 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-trimslice.yaml @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nvidia,tegra-audio-trimslice.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra audio complex with TrimSlice CODEC + +maintainers: + - Jon Hunter + - Thierry Reding + +allOf: + - $ref: nvidia,tegra-audio-common.yaml# + +properties: + compatible: + const: nvidia,tegra-audio-trimslice + +required: + - nvidia,i2s-controller + +unevaluatedProperties: false + +examples: + - | + sound { + compatible = "nvidia,tegra-audio-trimslice"; + nvidia,i2s-controller = <&tegra_i2s1>; + nvidia,audio-codec = <&codec>; + clocks = <&tegra_car 112>, <&tegra_car 113>, <&tegra_car 93>; + clock-names = "pll_a", "pll_a_out0", "mclk"; + }; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8753.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8753.txt deleted file mode 100644 index 96f6a57dd6b4..000000000000 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8753.txt +++ /dev/null @@ -1,40 +0,0 @@ -NVIDIA Tegra audio complex - -Required properties: -- compatible : "nvidia,tegra-audio-wm8753" -- clocks : Must contain an entry for each entry in clock-names. - See ../clocks/clock-bindings.txt for details. -- clock-names : Must include the following entries: - - pll_a - - pll_a_out0 - - mclk (The Tegra cdev1/extern1 clock, which feeds the CODEC's mclk) -- nvidia,model : The user-visible name of this sound complex. -- nvidia,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. Valid names for sources and - sinks are the WM8753's pins as documented in the binding for the WM8753, - and the jacks on the board: - - * Headphone Jack - * Mic Jack - -- nvidia,i2s-controller : The phandle of the Tegra I2S1 controller -- nvidia,audio-codec : The phandle of the WM8753 audio codec -Example: - -sound { - compatible = "nvidia,tegra-audio-wm8753-whistler", - "nvidia,tegra-audio-wm8753" - nvidia,model = "tegra-wm8753-harmony"; - - nvidia,audio-routing = - "Headphone Jack", "LOUT1", - "Headphone Jack", "ROUT1"; - - nvidia,i2s-controller = <&i2s1>; - nvidia,audio-codec = <&wm8753>; - - clocks = <&tegra_car 112>, <&tegra_car 113>, <&tegra_car 93>; - clock-names = "pll_a", "pll_a_out0", "mclk"; -}; - diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8753.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8753.yaml new file mode 100644 index 000000000000..a5b431d7d0c2 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8753.yaml @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nvidia,tegra-audio-wm8753.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra audio complex with WM8753 CODEC + +maintainers: + - Jon Hunter + - Thierry Reding + +allOf: + - $ref: nvidia,tegra-audio-common.yaml# + +properties: + compatible: + items: + - pattern: '^[a-z0-9]+,tegra-audio-wm8753(-[a-z0-9]+)+$' + - const: nvidia,tegra-audio-wm8753 + + nvidia,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. Valid names for sources and + sinks are the pins (documented in the binding document), + and the jacks on the board. + minItems: 2 + items: + enum: + # Board Connectors + - "Headphone Jack" + - "Mic Jack" + + # CODEC Pins + - LOUT1 + - LOUT2 + - ROUT1 + - ROUT2 + - MONO1 + - MONO2 + - OUT3 + - OUT4 + - LINE1 + - LINE2 + - RXP + - RXN + - ACIN + - ACOP + - MIC1N + - MIC1 + - MIC2N + - MIC2 + - "Mic Bias" + +required: + - nvidia,i2s-controller + +unevaluatedProperties: false + +examples: + - | + sound { + compatible = "nvidia,tegra-audio-wm8753-whistler", + "nvidia,tegra-audio-wm8753"; + nvidia,model = "tegra-wm8753-harmony"; + + nvidia,audio-routing = + "Headphone Jack", "LOUT1", + "Headphone Jack", "ROUT1"; + + nvidia,i2s-controller = <&i2s1>; + nvidia,audio-codec = <&wm8753>; + + clocks = <&clk 112>, <&clk 113>, <&clk 93>; + clock-names = "pll_a", "pll_a_out0", "mclk"; + }; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.txt deleted file mode 100644 index bbd581a8c5bc..000000000000 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.txt +++ /dev/null @@ -1,62 +0,0 @@ -NVIDIA Tegra audio complex - -Required properties: -- compatible : "nvidia,tegra-audio-wm8903" -- clocks : Must contain an entry for each entry in clock-names. - See ../clocks/clock-bindings.txt for details. -- clock-names : Must include the following entries: - - pll_a - - pll_a_out0 - - mclk (The Tegra cdev1/extern1 clock, which feeds the CODEC's mclk) -- nvidia,model : The user-visible name of this sound complex. -- nvidia,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. Valid names for sources and - sinks are the WM8903's pins (documented in the WM8903 binding document), - and the jacks on the board: - - * Headphone Jack - * Int Spk - * Mic Jack - * Int Mic - -- nvidia,i2s-controller : The phandle of the Tegra I2S1 controller -- nvidia,audio-codec : The phandle of the WM8903 audio codec - -Optional properties: -- nvidia,spkr-en-gpios : The GPIO that enables the speakers -- nvidia,hp-mute-gpios : The GPIO that mutes the headphones -- nvidia,hp-det-gpios : The GPIO that detect headphones are plugged in -- nvidia,int-mic-en-gpios : The GPIO that enables the internal microphone -- nvidia,ext-mic-en-gpios : The GPIO that enables the external microphone -- nvidia,headset : The Mic Jack represents state of the headset microphone pin - -Example: - -sound { - compatible = "nvidia,tegra-audio-wm8903-harmony", - "nvidia,tegra-audio-wm8903" - nvidia,model = "tegra-wm8903-harmony"; - - nvidia,audio-routing = - "Headphone Jack", "HPOUTR", - "Headphone Jack", "HPOUTL", - "Int Spk", "ROP", - "Int Spk", "RON", - "Int Spk", "LOP", - "Int Spk", "LON", - "Mic Jack", "MICBIAS", - "IN1L", "Mic Jack"; - - nvidia,i2s-controller = <&i2s1>; - nvidia,audio-codec = <&wm8903>; - - nvidia,spkr-en-gpios = <&codec 2 0>; - nvidia,hp-det-gpios = <&gpio 178 0>; /* gpio PW2 */ - nvidia,int-mic-en-gpios = <&gpio 184 0>; /*gpio PX0 */ - nvidia,ext-mic-en-gpios = <&gpio 185 0>; /* gpio PX1 */ - - clocks = <&tegra_car 112>, <&tegra_car 113>, <&tegra_car 93>; - clock-names = "pll_a", "pll_a_out0", "mclk"; -}; - diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.yaml new file mode 100644 index 000000000000..1b836acab980 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.yaml @@ -0,0 +1,93 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nvidia,tegra-audio-wm8903.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra audio complex with WM8903 CODEC + +maintainers: + - Jon Hunter + - Thierry Reding + +allOf: + - $ref: nvidia,tegra-audio-common.yaml# + +properties: + compatible: + oneOf: + - items: + - pattern: '^[a-z0-9]+,tegra-audio-wm8903(-[a-z0-9]+)+$' + - const: nvidia,tegra-audio-wm8903 + - items: + - pattern: ad,tegra-audio-plutux + - const: nvidia,tegra-audio-wm8903 + + nvidia,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. Valid names for sources and + sinks are the pins (documented in the binding document), + and the jacks on the board. + minItems: 2 + items: + enum: + # Board Connectors + - "Headphone Jack" + - "Int Spk" + - "Mic Jack" + - "Int Mic" + + # CODEC Pins + - IN1L + - IN1R + - IN2L + - IN2R + - IN3L + - IN3R + - DMICDAT + - HPOUTL + - HPOUTR + - LINEOUTL + - LINEOUTR + - LOP + - LON + - ROP + - RON + - MICBIAS + +required: + - nvidia,i2s-controller + +unevaluatedProperties: false + +examples: + - | + sound { + compatible = "nvidia,tegra-audio-wm8903-harmony", + "nvidia,tegra-audio-wm8903"; + nvidia,model = "tegra-wm8903-harmony"; + + nvidia,audio-routing = + "Headphone Jack", "HPOUTR", + "Headphone Jack", "HPOUTL", + "Int Spk", "ROP", + "Int Spk", "RON", + "Int Spk", "LOP", + "Int Spk", "LON", + "Mic Jack", "MICBIAS", + "IN1L", "Mic Jack"; + + nvidia,i2s-controller = <&i2s1>; + nvidia,audio-codec = <&wm8903>; + + nvidia,spkr-en-gpios = <&codec 2 0>; + nvidia,hp-det-gpios = <&gpio 178 0>; + nvidia,int-mic-en-gpios = <&gpio 184 0>; + nvidia,ext-mic-en-gpios = <&gpio 185 0>; + + clocks = <&clk 112>, <&clk 113>, <&clk 93>; + clock-names = "pll_a", "pll_a_out0", "mclk"; + }; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt deleted file mode 100644 index 436f6cd9d07c..000000000000 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt +++ /dev/null @@ -1,60 +0,0 @@ -NVIDIA Tegra audio complex - -Required properties: -- compatible : "nvidia,tegra-audio-wm9712" -- clocks : Must contain an entry for each entry in clock-names. - See ../clocks/clock-bindings.txt for details. -- clock-names : Must include the following entries: - - pll_a - - pll_a_out0 - - mclk (The Tegra cdev1/extern1 clock, which feeds the CODEC's mclk) -- nvidia,model : The user-visible name of this sound complex. -- nvidia,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. Valid names for sources and - sinks are the WM9712's pins, and the jacks on the board: - - WM9712 pins: - - * MONOOUT - * HPOUTL - * HPOUTR - * LOUT2 - * ROUT2 - * OUT3 - * LINEINL - * LINEINR - * PHONE - * PCBEEP - * MIC1 - * MIC2 - * Mic Bias - - Board connectors: - - * Headphone - * LineIn - * Mic - -- nvidia,ac97-controller : The phandle of the Tegra AC97 controller - - -Example: - -sound { - compatible = "nvidia,tegra-audio-wm9712-colibri_t20", - "nvidia,tegra-audio-wm9712"; - nvidia,model = "Toradex Colibri T20"; - - nvidia,audio-routing = - "Headphone", "HPOUTL", - "Headphone", "HPOUTR", - "LineIn", "LINEINL", - "LineIn", "LINEINR", - "Mic", "MIC1"; - - nvidia,ac97-controller = <&ac97>; - - clocks = <&tegra_car 112>, <&tegra_car 113>, <&tegra_car 93>; - clock-names = "pll_a", "pll_a_out0", "mclk"; -}; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.yaml new file mode 100644 index 000000000000..a1448283344b --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.yaml @@ -0,0 +1,76 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nvidia,tegra-audio-wm9712.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra audio complex with WM9712 CODEC + +maintainers: + - Jon Hunter + - Thierry Reding + +allOf: + - $ref: nvidia,tegra-audio-common.yaml# + +properties: + compatible: + items: + - pattern: '^[a-z0-9]+,tegra-audio-wm9712([-_][a-z0-9]+)+$' + - const: nvidia,tegra-audio-wm9712 + + nvidia,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. Valid names for sources and + sinks are the pins (documented in the binding document), + and the jacks on the board. + minItems: 2 + items: + enum: + # Board Connectors + - "Headphone" + - "LineIn" + - "Mic" + + # CODEC Pins + - MONOOUT + - HPOUTL + - HPOUTR + - LOUT2 + - ROUT2 + - OUT3 + - LINEINL + - LINEINR + - PHONE + - PCBEEP + - MIC1 + - MIC2 + - "Mic Bias" + +required: + - nvidia,ac97-controller + +unevaluatedProperties: false + +examples: + - | + sound { + compatible = "nvidia,tegra-audio-wm9712-colibri_t20", + "nvidia,tegra-audio-wm9712"; + nvidia,model = "Toradex Colibri T20"; + + nvidia,audio-routing = + "Headphone", "HPOUTL", + "Headphone", "HPOUTR", + "LineIn", "LINEINL", + "LineIn", "LINEINR", + "Mic", "MIC1"; + + nvidia,ac97-controller = <&ac97>; + + clocks = <&clk 112>, <&clk 113>, <&clk 93>; + clock-names = "pll_a", "pll_a_out0", "mclk"; + }; -- cgit v1.2.3 From fb6c83cab376c0963341a9521e85c1795acaec9b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 15 Dec 2021 10:35:11 -0600 Subject: ASoC: AMD: fix depend/select mistake on SND_AMD_ACP_CONFIG on i386 or x86_64: when # CONFIG_ACPI is not set, so SND_SOC_ACPI is not set: WARNING: unmet direct dependencies detected for SND_AMD_ACP_CONFIG Depends on [n]: SOUND [=y] && !UML && SND [=y] && SND_SOC [=y] && SND_SOC_ACPI [=n] Selected by [y]: - SND_SOC_AMD_ACP_COMMON [=y] && SOUND [=y] && !UML && SND [=y] && SND_SOC [=y] && X86 [=y] && PCI [=y] This problem is due to the unconditional selection of SND_AMD_ACP_CONFIG in other options. Using 'depends on' solved an initial problem but exposed another, let's use select instead. Reported-by: Randy Dunlap Fixes: d9b994cd7641 ('ASoC: AMD: acp-config: fix missing dependency on SND_SOC_ACPI') Signed-off-by: Pierre-Louis Bossart Acked-by: Randy Dunlap # build-tested Reviewed-by: Daniel Baluta Reviewed-by: Ranjani Sridharan Reviewed-by: Ajit Kumar Pandey Link: https://lore.kernel.org/r/20211215163511.151286-1-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/amd/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig index bcfeb3fc2592..7a9e45094f37 100644 --- a/sound/soc/amd/Kconfig +++ b/sound/soc/amd/Kconfig @@ -98,7 +98,7 @@ config SND_SOC_AMD_YC_MACH config SND_AMD_ACP_CONFIG tristate "AMD ACP configuration selection" - depends on SND_SOC_ACPI + select SND_SOC_ACPI if ACPI help This option adds an auto detection to determine which ACP driver modules to use -- cgit v1.2.3 From 0725ac9ac4492568514a94971f46dd0546684ff8 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 22 Nov 2021 23:21:53 +0100 Subject: ASoC: tegra20-spdif: stop setting slave_id The DMA resource is never set up anywhere, and passing this as slave_id has not been the proper procedure in a long time. As a preparation for removing all slave_id references from the ALSA code, remove this one. According to Dmitry Osipenko, this driver has never been used and the mechanism for configuring DMA would not work as it is implemented, so this part will get rewritten when the driver gets put into use again in the future. Signed-off-by: Arnd Bergmann Reviewed-by: Dmitry Osipenko Acked-by: Mark Brown Link: https://lore.kernel.org/r/20211122222203.4103644-2-arnd@kernel.org Signed-off-by: Vinod Koul --- sound/soc/tegra/tegra20_spdif.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c index 7751575cd6d6..57a6c576b91f 100644 --- a/sound/soc/tegra/tegra20_spdif.c +++ b/sound/soc/tegra/tegra20_spdif.c @@ -290,7 +290,6 @@ static int tegra20_spdif_platform_probe(struct platform_device *pdev) spdif->playback_dma_data.addr = mem->start + TEGRA20_SPDIF_DATA_OUT; spdif->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; spdif->playback_dma_data.maxburst = 4; - spdif->playback_dma_data.slave_id = dmareq->start; pm_runtime_enable(&pdev->dev); -- cgit v1.2.3 From d53939dcc4cfbe6de2c42daec90c199825f6a96f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 22 Nov 2021 23:22:02 +0100 Subject: dmaengine: tegra20-apb: stop checking config->slave_id Nothing sets the slave_id field any more, so stop accessing it to allow the removal of this field. Signed-off-by: Arnd Bergmann Acked-by: Mark Brown Link: https://lore.kernel.org/r/20211122222203.4103644-11-arnd@kernel.org Signed-off-by: Vinod Koul --- drivers/dma/tegra20-apb-dma.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index b7260749e8ee..eaafcbe4ca94 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -343,12 +343,6 @@ static int tegra_dma_slave_config(struct dma_chan *dc, } memcpy(&tdc->dma_sconfig, sconfig, sizeof(*sconfig)); - if (tdc->slave_id == TEGRA_APBDMA_SLAVE_ID_INVALID && - sconfig->device_fc) { - if (sconfig->slave_id > TEGRA_APBDMA_CSR_REQ_SEL_MASK) - return -EINVAL; - tdc->slave_id = sconfig->slave_id; - } tdc->config_init = true; return 0; -- cgit v1.2.3 From bdecfceffeeb9000e78b0f613069f5c06974b347 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 22 Nov 2021 23:21:54 +0100 Subject: ASoC: dai_dma: remove slave_id field This field is no longer set from any driver now, so remove the last references as well. Signed-off-by: Arnd Bergmann Acked-by: Mark Brown Link: https://lore.kernel.org/r/20211122222203.4103644-3-arnd@kernel.org Signed-off-by: Vinod Koul --- include/sound/dmaengine_pcm.h | 2 -- sound/core/pcm_dmaengine.c | 5 ++--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index 96666efddb39..38ea046e653c 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h @@ -60,7 +60,6 @@ struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream) * @maxburst: Maximum number of words(note: words, as in units of the * src_addr_width member, not bytes) that can be send to or received from the * DAI in one burst. - * @slave_id: Slave requester id for the DMA channel. * @filter_data: Custom DMA channel filter data, this will usually be used when * requesting the DMA channel. * @chan_name: Custom channel name to use when requesting DMA channel. @@ -74,7 +73,6 @@ struct snd_dmaengine_dai_dma_data { dma_addr_t addr; enum dma_slave_buswidth addr_width; u32 maxburst; - unsigned int slave_id; void *filter_data; const char *chan_name; unsigned int fifo_size; diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c index 1fc2fa077574..af6f717e1e7e 100644 --- a/sound/core/pcm_dmaengine.c +++ b/sound/core/pcm_dmaengine.c @@ -91,8 +91,8 @@ EXPORT_SYMBOL_GPL(snd_hwparams_to_dma_slave_config); * @dma_data: DAI DMA data * @slave_config: DMA slave configuration * - * Initializes the {dst,src}_addr, {dst,src}_maxburst, {dst,src}_addr_width and - * slave_id fields of the DMA slave config from the same fields of the DAI DMA + * Initializes the {dst,src}_addr, {dst,src}_maxburst, {dst,src}_addr_width + * fields of the DMA slave config from the same fields of the DAI DMA * data struct. The src and dst fields will be initialized depending on the * direction of the substream. If the substream is a playback stream the dst * fields will be initialized, if it is a capture stream the src fields will be @@ -124,7 +124,6 @@ void snd_dmaengine_pcm_set_config_from_dai_data( slave_config->src_addr_width = dma_data->addr_width; } - slave_config->slave_id = dma_data->slave_id; slave_config->peripheral_config = dma_data->peripheral_config; slave_config->peripheral_size = dma_data->peripheral_size; } -- cgit v1.2.3 From feaa4a09acc9a33211dbe3930357922f7ad9750c Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 22 Nov 2021 23:21:55 +0100 Subject: spi: pic32: stop setting dma_config->slave_id Setting slave_id makes no sense with DT based probing, and should eventually get removed entirely. Address this driver by no longer setting the field here. I could not find which DMA driver is used on PIC32, if it's in the tree at all, but none of the obvious ones even care about slave_id any more. Acked-by: Mark Brown Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20211122222203.4103644-4-arnd@kernel.org Signed-off-by: Vinod Koul --- drivers/spi/spi-pic32.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/spi/spi-pic32.c b/drivers/spi/spi-pic32.c index 5eb7b61bbb4d..f86433b29260 100644 --- a/drivers/spi/spi-pic32.c +++ b/drivers/spi/spi-pic32.c @@ -370,7 +370,6 @@ static int pic32_spi_dma_config(struct pic32_spi *pic32s, u32 dma_width) cfg.src_addr_width = dma_width; cfg.dst_addr_width = dma_width; /* tx channel */ - cfg.slave_id = pic32s->tx_irq; cfg.direction = DMA_MEM_TO_DEV; ret = dmaengine_slave_config(master->dma_tx, &cfg); if (ret) { @@ -378,7 +377,6 @@ static int pic32_spi_dma_config(struct pic32_spi *pic32s, u32 dma_width) return ret; } /* rx channel */ - cfg.slave_id = pic32s->rx_irq; cfg.direction = DMA_DEV_TO_MEM; ret = dmaengine_slave_config(master->dma_rx, &cfg); if (ret) -- cgit v1.2.3 From f59f6aaead975f0ec4d8ff2d59c4ffb8cf0127b2 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 22 Nov 2021 23:21:56 +0100 Subject: mmc: bcm2835: stop setting chan_config->slave_id The field is not interpreted by the DMA engine driver, as all the data is passed from devicetree instead. Remove the assignment so the field can eventually be deleted. Reviewed-by: Nicolas Saenz Julienne Signed-off-by: Arnd Bergmann Acked-by: Ulf Hansson Acked-by: Mark Brown Link: https://lore.kernel.org/r/20211122222203.4103644-5-arnd@kernel.org Signed-off-by: Vinod Koul --- drivers/mmc/host/bcm2835.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/mmc/host/bcm2835.c b/drivers/mmc/host/bcm2835.c index 8c2361e66277..463b707d9e99 100644 --- a/drivers/mmc/host/bcm2835.c +++ b/drivers/mmc/host/bcm2835.c @@ -1293,14 +1293,12 @@ static int bcm2835_add_host(struct bcm2835_host *host) host->dma_cfg_tx.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; host->dma_cfg_tx.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - host->dma_cfg_tx.slave_id = 13; /* DREQ channel */ host->dma_cfg_tx.direction = DMA_MEM_TO_DEV; host->dma_cfg_tx.src_addr = 0; host->dma_cfg_tx.dst_addr = host->phys_addr + SDDATA; host->dma_cfg_rx.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; host->dma_cfg_rx.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - host->dma_cfg_rx.slave_id = 13; /* DREQ channel */ host->dma_cfg_rx.direction = DMA_DEV_TO_MEM; host->dma_cfg_rx.src_addr = host->phys_addr + SDDATA; host->dma_cfg_rx.dst_addr = 0; -- cgit v1.2.3 From 37228af82e5f4d7be64f71c63463112b9dd4fc55 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 22 Nov 2021 23:21:57 +0100 Subject: dmaengine: shdma: remove legacy slave_id parsing The slave device is picked through either devicetree or a filter function, and any remaining out-of-tree drivers would have warned about this usage since 2015. Stop interpreting the field finally so it can be removed from the interface. Reviewed-by: Laurent Pinchart Signed-off-by: Arnd Bergmann Acked-by: Mark Brown Link: https://lore.kernel.org/r/20211122222203.4103644-6-arnd@kernel.org Signed-off-by: Vinod Koul --- drivers/dma/sh/shdma-base.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/dma/sh/shdma-base.c b/drivers/dma/sh/shdma-base.c index 7f72b3f4cd1a..41c6bc650fa3 100644 --- a/drivers/dma/sh/shdma-base.c +++ b/drivers/dma/sh/shdma-base.c @@ -786,14 +786,6 @@ static int shdma_config(struct dma_chan *chan, if (!config) return -EINVAL; - /* - * overriding the slave_id through dma_slave_config is deprecated, - * but possibly some out-of-tree drivers still do it. - */ - if (WARN_ON_ONCE(config->slave_id && - config->slave_id != schan->real_slave_id)) - schan->real_slave_id = config->slave_id; - /* * We could lock this, but you shouldn't be configuring the * channel, while using it... -- cgit v1.2.3 From 134c37fa250a87a7e77c80a7c59ae16c462e46e0 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 22 Nov 2021 23:21:58 +0100 Subject: dmaengine: pxa/mmp: stop referencing config->slave_id The last driver referencing the slave_id on Marvell PXA and MMP platforms was the SPI driver, but this stopped doing so a long time ago, so the TODO from the earlier patch can no be removed. Fixes: b729bf34535e ("spi/pxa2xx: Don't use slave_id of dma_slave_config") Fixes: 13b3006b8ebd ("dma: mmp_pdma: add filter function") Signed-off-by: Arnd Bergmann Acked-by: Mark Brown Link: https://lore.kernel.org/r/20211122222203.4103644-7-arnd@kernel.org Signed-off-by: Vinod Koul --- drivers/dma/mmp_pdma.c | 6 ------ drivers/dma/pxa_dma.c | 7 ------- 2 files changed, 13 deletions(-) diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index a23563cd118b..5a53d7fcef01 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -727,12 +727,6 @@ static int mmp_pdma_config_write(struct dma_chan *dchan, chan->dir = direction; chan->dev_addr = addr; - /* FIXME: drivers should be ported over to use the filter - * function. Once that's done, the following two lines can - * be removed. - */ - if (cfg->slave_id) - chan->drcmr = cfg->slave_id; return 0; } diff --git a/drivers/dma/pxa_dma.c b/drivers/dma/pxa_dma.c index 52d04641e361..6078cc81892e 100644 --- a/drivers/dma/pxa_dma.c +++ b/drivers/dma/pxa_dma.c @@ -909,13 +909,6 @@ static void pxad_get_config(struct pxad_chan *chan, *dcmd |= PXA_DCMD_BURST16; else if (maxburst == 32) *dcmd |= PXA_DCMD_BURST32; - - /* FIXME: drivers should be ported over to use the filter - * function. Once that's done, the following two lines can - * be removed. - */ - if (chan->cfg.slave_id) - chan->drcmr = chan->cfg.slave_id; } static struct dma_async_tx_descriptor * -- cgit v1.2.3 From 722d6d2bdcc2dcff5527c704fb8f2bbcb018a232 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 22 Nov 2021 23:21:59 +0100 Subject: dmaengine: sprd: stop referencing config->slave_id It appears that the code that reads the slave_id from the channel config was copied incorrectly from other drivers. Nothing ever sets this field on platforms that use this driver, so remove the reference. Reviewed-by: Baolin Wang Signed-off-by: Arnd Bergmann Acked-by: Mark Brown Link: https://lore.kernel.org/r/20211122222203.4103644-8-arnd@kernel.org Signed-off-by: Vinod Koul --- drivers/dma/sprd-dma.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/dma/sprd-dma.c b/drivers/dma/sprd-dma.c index 4357d2395e6b..7f158ef5672d 100644 --- a/drivers/dma/sprd-dma.c +++ b/drivers/dma/sprd-dma.c @@ -795,9 +795,6 @@ static int sprd_dma_fill_desc(struct dma_chan *chan, return dst_datawidth; } - if (slave_cfg->slave_id) - schan->dev_id = slave_cfg->slave_id; - hw->cfg = SPRD_DMA_DONOT_WAIT_BDONE << SPRD_DMA_WAIT_BDONE_OFFSET; /* -- cgit v1.2.3 From 03de6b273805b3c552ff158f8688555937375926 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 22 Nov 2021 23:22:00 +0100 Subject: dmaengine: qcom-adm: stop abusing slave_id config The slave_id was previously used to pick one DMA slave instead of another, but this is now done through the DMA descriptors in device tree. For the qcom_adm driver, the configuration is documented in the DT binding to contain a tuple of device identifier and a "crci" field, but the implementation ends up using only a single cell for identifying the slave, with the crci getting passed in nonstandard properties of the device, and passed through the dma driver using the old slave_id field. Part of the problem apparently is that the nand driver ends up using only a single DMA request ID, but requires distinct values for "crci" depending on the type of transfer. Change both the dmaengine driver and the two slave drivers to allow the documented binding to work in addition to the ad-hoc passing of crci values. In order to no longer abuse the slave_id field, pass the data using the "peripheral_config" mechanism instead. Signed-off-by: Arnd Bergmann Acked-by: Mark Brown Link: https://lore.kernel.org/r/20211122222203.4103644-9-arnd@kernel.org Signed-off-by: Vinod Koul --- drivers/dma/qcom/qcom_adm.c | 56 ++++++++++++++++++++++++++++++++++----- drivers/mtd/nand/raw/qcom_nandc.c | 14 ++++++++-- drivers/tty/serial/msm_serial.c | 15 +++++++++-- include/linux/dma/qcom_adm.h | 12 +++++++++ 4 files changed, 86 insertions(+), 11 deletions(-) create mode 100644 include/linux/dma/qcom_adm.h diff --git a/drivers/dma/qcom/qcom_adm.c b/drivers/dma/qcom/qcom_adm.c index ee78bed8d60d..facdacf8aede 100644 --- a/drivers/dma/qcom/qcom_adm.c +++ b/drivers/dma/qcom/qcom_adm.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -140,6 +141,8 @@ struct adm_chan { struct adm_async_desc *curr_txd; struct dma_slave_config slave; + u32 crci; + u32 mux; struct list_head node; int error; @@ -379,8 +382,8 @@ static struct dma_async_tx_descriptor *adm_prep_slave_sg(struct dma_chan *chan, return ERR_PTR(-EINVAL); } - crci = achan->slave.slave_id & 0xf; - if (!crci || achan->slave.slave_id > 0x1f) { + crci = achan->crci & 0xf; + if (!crci || achan->crci > 0x1f) { dev_err(adev->dev, "invalid crci value\n"); return ERR_PTR(-EINVAL); } @@ -403,9 +406,7 @@ static struct dma_async_tx_descriptor *adm_prep_slave_sg(struct dma_chan *chan, if (!async_desc) return ERR_PTR(-ENOMEM); - if (crci) - async_desc->mux = achan->slave.slave_id & ADM_CRCI_MUX_SEL ? - ADM_CRCI_CTL_MUX_SEL : 0; + async_desc->mux = achan->mux ? ADM_CRCI_CTL_MUX_SEL : 0; async_desc->crci = crci; async_desc->blk_size = blk_size; async_desc->dma_len = single_count * sizeof(struct adm_desc_hw_single) + @@ -488,10 +489,13 @@ static int adm_terminate_all(struct dma_chan *chan) static int adm_slave_config(struct dma_chan *chan, struct dma_slave_config *cfg) { struct adm_chan *achan = to_adm_chan(chan); + struct qcom_adm_peripheral_config *config = cfg->peripheral_config; unsigned long flag; spin_lock_irqsave(&achan->vc.lock, flag); memcpy(&achan->slave, cfg, sizeof(struct dma_slave_config)); + if (cfg->peripheral_size == sizeof(config)) + achan->crci = config->crci; spin_unlock_irqrestore(&achan->vc.lock, flag); return 0; @@ -694,6 +698,45 @@ static void adm_channel_init(struct adm_device *adev, struct adm_chan *achan, achan->vc.desc_free = adm_dma_free_desc; } +/** + * adm_dma_xlate + * @dma_spec: pointer to DMA specifier as found in the device tree + * @ofdma: pointer to DMA controller data + * + * This can use either 1-cell or 2-cell formats, the first cell + * identifies the slave device, while the optional second cell + * contains the crci value. + * + * Returns pointer to appropriate dma channel on success or NULL on error. + */ +static struct dma_chan *adm_dma_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + struct dma_device *dev = ofdma->of_dma_data; + struct dma_chan *chan, *candidate = NULL; + struct adm_chan *achan; + + if (!dev || dma_spec->args_count > 2) + return NULL; + + list_for_each_entry(chan, &dev->channels, device_node) + if (chan->chan_id == dma_spec->args[0]) { + candidate = chan; + break; + } + + if (!candidate) + return NULL; + + achan = to_adm_chan(candidate); + if (dma_spec->args_count == 2) + achan->crci = dma_spec->args[1]; + else + achan->crci = 0; + + return dma_get_slave_channel(candidate); +} + static int adm_dma_probe(struct platform_device *pdev) { struct adm_device *adev; @@ -838,8 +881,7 @@ static int adm_dma_probe(struct platform_device *pdev) goto err_disable_clks; } - ret = of_dma_controller_register(pdev->dev.of_node, - of_dma_xlate_by_chan_id, + ret = of_dma_controller_register(pdev->dev.of_node, adm_dma_xlate, &adev->common); if (ret) goto err_unregister_dma; diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 04e6f7b26706..7c6efa3b6255 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -952,6 +953,7 @@ static int prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read, struct dma_async_tx_descriptor *dma_desc; struct scatterlist *sgl; struct dma_slave_config slave_conf; + struct qcom_adm_peripheral_config periph_conf = {}; enum dma_transfer_direction dir_eng; int ret; @@ -983,11 +985,19 @@ static int prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read, if (read) { slave_conf.src_maxburst = 16; slave_conf.src_addr = nandc->base_dma + reg_off; - slave_conf.slave_id = nandc->data_crci; + if (nandc->data_crci) { + periph_conf.crci = nandc->data_crci; + slave_conf.peripheral_config = &periph_conf; + slave_conf.peripheral_size = sizeof(periph_conf); + } } else { slave_conf.dst_maxburst = 16; slave_conf.dst_addr = nandc->base_dma + reg_off; - slave_conf.slave_id = nandc->cmd_crci; + if (nandc->cmd_crci) { + periph_conf.crci = nandc->cmd_crci; + slave_conf.peripheral_config = &periph_conf; + slave_conf.peripheral_size = sizeof(periph_conf); + } } ret = dmaengine_slave_config(nandc->chan, &slave_conf); diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index fcef7a961430..c6be09f44dc1 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -290,6 +291,7 @@ static void msm_request_tx_dma(struct msm_port *msm_port, resource_size_t base) { struct device *dev = msm_port->uart.dev; struct dma_slave_config conf; + struct qcom_adm_peripheral_config periph_conf = {}; struct msm_dma *dma; u32 crci = 0; int ret; @@ -308,7 +310,11 @@ static void msm_request_tx_dma(struct msm_port *msm_port, resource_size_t base) conf.device_fc = true; conf.dst_addr = base + UARTDM_TF; conf.dst_maxburst = UARTDM_BURST_SIZE; - conf.slave_id = crci; + if (crci) { + conf.peripheral_config = &periph_conf; + conf.peripheral_size = sizeof(periph_conf); + periph_conf.crci = crci; + } ret = dmaengine_slave_config(dma->chan, &conf); if (ret) @@ -333,6 +339,7 @@ static void msm_request_rx_dma(struct msm_port *msm_port, resource_size_t base) { struct device *dev = msm_port->uart.dev; struct dma_slave_config conf; + struct qcom_adm_peripheral_config periph_conf = {}; struct msm_dma *dma; u32 crci = 0; int ret; @@ -355,7 +362,11 @@ static void msm_request_rx_dma(struct msm_port *msm_port, resource_size_t base) conf.device_fc = true; conf.src_addr = base + UARTDM_RF; conf.src_maxburst = UARTDM_BURST_SIZE; - conf.slave_id = crci; + if (crci) { + conf.peripheral_config = &periph_conf; + conf.peripheral_size = sizeof(periph_conf); + periph_conf.crci = crci; + } ret = dmaengine_slave_config(dma->chan, &conf); if (ret) diff --git a/include/linux/dma/qcom_adm.h b/include/linux/dma/qcom_adm.h new file mode 100644 index 000000000000..af20df674f0c --- /dev/null +++ b/include/linux/dma/qcom_adm.h @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0-only +#ifndef __LINUX_DMA_QCOM_ADM_H +#define __LINUX_DMA_QCOM_ADM_H + +#include + +struct qcom_adm_peripheral_config { + u32 crci; + u32 mux; +}; + +#endif /* __LINUX_DMA_QCOM_ADM_H */ -- cgit v1.2.3 From 93cdb5b0dc56cc7a8b87a61146495f3bdc93d7ba Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 22 Nov 2021 23:22:01 +0100 Subject: dmaengine: xilinx_dpdma: stop using slave_id field The display driver wants to pass a custom flag to the DMA engine driver, which it started doing by using the slave_id field that was traditionally used for a different purpose. As there is no longer a correct use for the slave_id field, it should really be removed, and the remaining users changed over to something different. The new mechanism for passing nonstandard settings is using the .peripheral_config field, so use that to pass a newly defined structure here, making it clear that this will not work in portable drivers. Reviewed-by: Laurent Pinchart Signed-off-by: Arnd Bergmann Acked-by: Mark Brown Link: https://lore.kernel.org/r/20211122222203.4103644-10-arnd@kernel.org Signed-off-by: Vinod Koul --- drivers/dma/xilinx/xilinx_dpdma.c | 17 +++++++++++------ drivers/gpu/drm/xlnx/zynqmp_disp.c | 9 +++++++-- include/linux/dma/xilinx_dpdma.h | 11 +++++++++++ 3 files changed, 29 insertions(+), 8 deletions(-) create mode 100644 include/linux/dma/xilinx_dpdma.h diff --git a/drivers/dma/xilinx/xilinx_dpdma.c b/drivers/dma/xilinx/xilinx_dpdma.c index ce5c66e6897d..b0f4948b00a5 100644 --- a/drivers/dma/xilinx/xilinx_dpdma.c +++ b/drivers/dma/xilinx/xilinx_dpdma.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -1273,6 +1274,7 @@ static int xilinx_dpdma_config(struct dma_chan *dchan, struct dma_slave_config *config) { struct xilinx_dpdma_chan *chan = to_xilinx_chan(dchan); + struct xilinx_dpdma_peripheral_config *pconfig; unsigned long flags; /* @@ -1282,15 +1284,18 @@ static int xilinx_dpdma_config(struct dma_chan *dchan, * fixed both on the DPDMA side and on the DP controller side. */ - spin_lock_irqsave(&chan->lock, flags); - /* - * Abuse the slave_id to indicate that the channel is part of a video - * group. + * Use the peripheral_config to indicate that the channel is part + * of a video group. This requires matching use of the custom + * structure in each driver. */ - if (chan->id <= ZYNQMP_DPDMA_VIDEO2) - chan->video_group = config->slave_id != 0; + pconfig = config->peripheral_config; + if (WARN_ON(pconfig && config->peripheral_size != sizeof(*pconfig))) + return -EINVAL; + spin_lock_irqsave(&chan->lock, flags); + if (chan->id <= ZYNQMP_DPDMA_VIDEO2 && pconfig) + chan->video_group = pconfig->video_group; spin_unlock_irqrestore(&chan->lock, flags); return 0; diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c index ff2b308d8651..11c409cbc88e 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -1058,14 +1059,18 @@ static void zynqmp_disp_layer_set_format(struct zynqmp_disp_layer *layer, zynqmp_disp_avbuf_set_format(layer->disp, layer, layer->disp_fmt); /* - * Set slave_id for each DMA channel to indicate they're part of a + * Set pconfig for each DMA channel to indicate they're part of a * video group. */ for (i = 0; i < info->num_planes; i++) { struct zynqmp_disp_layer_dma *dma = &layer->dmas[i]; + struct xilinx_dpdma_peripheral_config pconfig = { + .video_group = true, + }; struct dma_slave_config config = { .direction = DMA_MEM_TO_DEV, - .slave_id = 1, + .peripheral_config = &pconfig, + .peripheral_size = sizeof(pconfig), }; dmaengine_slave_config(dma->chan, &config); diff --git a/include/linux/dma/xilinx_dpdma.h b/include/linux/dma/xilinx_dpdma.h new file mode 100644 index 000000000000..83a1377f03f8 --- /dev/null +++ b/include/linux/dma/xilinx_dpdma.h @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef __LINUX_DMA_XILINX_DPDMA_H +#define __LINUX_DMA_XILINX_DPDMA_H + +#include + +struct xilinx_dpdma_peripheral_config { + bool video_group; +}; + +#endif /* __LINUX_DMA_XILINX_DPDMA_H */ -- cgit v1.2.3 From 3c219644075795a99271d345efdfa8b256e55161 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 22 Nov 2021 23:22:03 +0100 Subject: dmaengine: remove slave_id config field All references to the slave_id field have been removed, so remove the field as well to prevent new references from creeping in again. Originally this allowed slave DMA drivers to configure which device is accessed with the dmaengine_slave_config() call, but this was inconsistent, as the same information is also passed while requesting a channel, and never changes in practice. In modern kernels, the device is always selected when requesting the channel, so the .slave_id field is no longer useful. Reviewed-by: Laurent Pinchart Signed-off-by: Arnd Bergmann Acked-by: Mark Brown Link: https://lore.kernel.org/r/20211122222203.4103644-12-arnd@kernel.org Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 9000f3ffce8b..0349b35235e6 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -418,9 +418,6 @@ enum dma_slave_buswidth { * @device_fc: Flow Controller Settings. Only valid for slave channels. Fill * with 'true' if peripheral should be flow controller. Direction will be * selected at Runtime. - * @slave_id: Slave requester id. Only valid for slave channels. The dma - * slave peripheral will have unique id as dma requester which need to be - * pass as slave config. * @peripheral_config: peripheral configuration for programming peripheral * for dmaengine transfer * @peripheral_size: peripheral configuration buffer size @@ -448,7 +445,6 @@ struct dma_slave_config { u32 src_port_window_size; u32 dst_port_window_size; bool device_fc; - unsigned int slave_id; void *peripheral_config; size_t peripheral_size; }; -- cgit v1.2.3 From eed5391f6747ffa7e3972b53aa721bded8feeff7 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 16 Dec 2021 17:16:26 -0600 Subject: ASoC: SOF: pcm: remove support for RESUME trigger The SOF driver removed the support for INFO_RESUME in the commit "ASoC: SOF: pcm: do not add SNDRV_PCM_INFO_RESUME to runtime hw info". And resuming is handled by the ALSA core with the .prepare and .trigger_start stages. So, remove handling of RESUME trigger in the component driver trigger op. Reviewed-by: Kai Vehmanen Reviewed-by: Rander Wang Reviewed-by: Bard Liao Signed-off-by: Ranjani Sridharan Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20211216231628.344687-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/pcm.c | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index e4446defe51e..37fb8e6cd493 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -395,26 +395,6 @@ static int sof_pcm_trigger(struct snd_soc_component *component, case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_RELEASE; break; - case SNDRV_PCM_TRIGGER_RESUME: - if (spcm->stream[substream->stream].suspend_ignored) { - /* - * this case will be triggered when INFO_RESUME is - * supported, no need to resume streams that remained - * enabled in D0ix. - */ - spcm->stream[substream->stream].suspend_ignored = false; - return 0; - } - - /* set up hw_params */ - ret = sof_pcm_prepare(component, substream); - if (ret < 0) { - dev_err(component->dev, - "error: failed to set up hw_params upon resume\n"); - return ret; - } - - fallthrough; case SNDRV_PCM_TRIGGER_START: if (spcm->stream[substream->stream].suspend_ignored) { /* -- cgit v1.2.3 From 9b465060d144dd8196729cc8d77e328f1328dcbf Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 16 Dec 2021 17:16:27 -0600 Subject: ASoC: SOF: Intel: hda: remove support for RESUME trigger The SOF driver removed the support for INFO_RESUME in the commit "ASoC: SOF: pcm: do not add SNDRV_PCM_INFO_RESUME to runtime hw info". And resuming is handled by the ALSA core with the .prepare and .trigger_start stages. So, remove handling of RESUME trigger in the HDA DAI BE trigger op. Reviewed-by: Kai Vehmanen Reviewed-by: Rander Wang Reviewed-by: Bard Liao Signed-off-by: Ranjani Sridharan Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20211216231628.344687-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-dai.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 35ffb71116c6..6381f2b227f0 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -342,16 +342,6 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, w = snd_soc_dai_get_widget(dai, substream->stream); switch (cmd) { - case SNDRV_PCM_TRIGGER_RESUME: - /* set up hw_params */ - ret = hda_link_pcm_prepare(substream, dai); - if (ret < 0) { - dev_err(dai->dev, - "error: setting up hw_params during resume\n"); - return ret; - } - - fallthrough; case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: snd_hdac_ext_link_stream_start(link_dev); -- cgit v1.2.3 From 35218cf61869ca4b11c8c0b49a95f75f379e403a Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 16 Dec 2021 17:16:28 -0600 Subject: ASoC: SOF: Intel: hda: remove support for RESUME in platform trigger The SOF driver removed the support for INFO_RESUME in the commit "ASoC: SOF: pcm: do not add SNDRV_PCM_INFO_RESUME to runtime hw info". And resuming is handled by the ALSA core with the .prepare and .trigger_start stages. So, remove handling of RESUME trigger in the component driver trigger op. So, remove handling the RESUME trigger in the platform trigger op for HDA platforms. Reviewed-by: Kai Vehmanen Reviewed-by: Rander Wang Reviewed-by: Bard Liao Signed-off-by: Ranjani Sridharan Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20211216231628.344687-4-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-stream.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index e910f68706d9..ba60807fbd8f 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -329,7 +329,6 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, /* cmd must be for audio stream */ switch (cmd) { - case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_START: snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, -- cgit v1.2.3 From cb515f105cab124c5740e70dd0e8c78186ae81b7 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 16 Dec 2021 17:24:20 -0600 Subject: ASoC: SOF: avoid casting "const" attribute away Casting "const" attribute away is dangerous, obtain a writable pointer instead to avoid that. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20211216232422.345164-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/renoir.c | 7 +++--- sound/soc/sof/intel/atom.c | 13 +++++----- sound/soc/sof/intel/atom.h | 4 ++-- sound/soc/sof/intel/bdw.c | 11 +++++---- sound/soc/sof/intel/hda.c | 60 ++++++++++++++++++++++------------------------ sound/soc/sof/intel/hda.h | 4 ++-- sound/soc/sof/ops.h | 8 ++++--- sound/soc/sof/sof-audio.c | 9 +++---- sound/soc/sof/sof-priv.h | 4 ++-- 9 files changed, 62 insertions(+), 58 deletions(-) diff --git a/sound/soc/sof/amd/renoir.c b/sound/soc/sof/amd/renoir.c index 43037109e130..c3ecb9e9d5ba 100644 --- a/sound/soc/sof/amd/renoir.c +++ b/sound/soc/sof/amd/renoir.c @@ -104,7 +104,7 @@ static struct snd_soc_dai_driver renoir_sof_dai[] = { }, }; -static void amd_sof_machine_select(struct snd_sof_dev *sdev) +static struct snd_soc_acpi_mach *amd_sof_machine_select(struct snd_sof_dev *sdev) { struct snd_sof_pdata *sof_pdata = sdev->pdata; const struct sof_dev_desc *desc = sof_pdata->desc; @@ -113,12 +113,13 @@ static void amd_sof_machine_select(struct snd_sof_dev *sdev) mach = snd_soc_acpi_find_machine(desc->machines); if (!mach) { dev_warn(sdev->dev, "No matching ASoC machine driver found\n"); - return; + return NULL; } sof_pdata->tplg_filename = mach->sof_tplg_filename; sof_pdata->fw_filename = mach->fw_filename; - sof_pdata->machine = mach; + + return mach; } /* AMD Renoir DSP ops */ diff --git a/sound/soc/sof/intel/atom.c b/sound/soc/sof/intel/atom.c index cdc96a7df493..5aa064b28fca 100644 --- a/sound/soc/sof/intel/atom.c +++ b/sound/soc/sof/intel/atom.c @@ -293,7 +293,7 @@ static const char *fixup_tplg_name(struct snd_sof_dev *sdev, return tplg_filename; } -void atom_machine_select(struct snd_sof_dev *sdev) +struct snd_soc_acpi_mach *atom_machine_select(struct snd_sof_dev *sdev) { struct snd_sof_pdata *sof_pdata = sdev->pdata; const struct sof_dev_desc *desc = sof_pdata->desc; @@ -304,7 +304,7 @@ void atom_machine_select(struct snd_sof_dev *sdev) mach = snd_soc_acpi_find_machine(desc->machines); if (!mach) { dev_warn(sdev->dev, "warning: No matching ASoC machine driver found\n"); - return; + return NULL; } pdev = to_platform_device(sdev->dev); @@ -322,12 +322,13 @@ void atom_machine_select(struct snd_sof_dev *sdev) if (!tplg_filename) { dev_dbg(sdev->dev, "error: no topology filename\n"); - return; + return NULL; } sof_pdata->tplg_filename = tplg_filename; mach->mach_params.acpi_ipc_irq_index = desc->irqindex_host_ipc; - sof_pdata->machine = mach; + + return mach; } EXPORT_SYMBOL_NS(atom_machine_select, SND_SOC_SOF_INTEL_ATOM_HIFI_EP); @@ -402,14 +403,14 @@ struct snd_soc_dai_driver atom_dai[] = { }; EXPORT_SYMBOL_NS(atom_dai, SND_SOC_SOF_INTEL_ATOM_HIFI_EP); -void atom_set_mach_params(const struct snd_soc_acpi_mach *mach, +void atom_set_mach_params(struct snd_soc_acpi_mach *mach, struct snd_sof_dev *sdev) { struct snd_sof_pdata *pdata = sdev->pdata; const struct sof_dev_desc *desc = pdata->desc; struct snd_soc_acpi_mach_params *mach_params; - mach_params = (struct snd_soc_acpi_mach_params *)&mach->mach_params; + mach_params = &mach->mach_params; mach_params->platform = dev_name(sdev->dev); mach_params->num_dai_drivers = desc->ops->num_drv; mach_params->dai_drivers = desc->ops->drv; diff --git a/sound/soc/sof/intel/atom.h b/sound/soc/sof/intel/atom.h index 96a462c7a2e5..b965e5e080a6 100644 --- a/sound/soc/sof/intel/atom.h +++ b/sound/soc/sof/intel/atom.h @@ -65,8 +65,8 @@ int atom_run(struct snd_sof_dev *sdev); int atom_reset(struct snd_sof_dev *sdev); void atom_dump(struct snd_sof_dev *sdev, u32 flags); -void atom_machine_select(struct snd_sof_dev *sdev); -void atom_set_mach_params(const struct snd_soc_acpi_mach *mach, +struct snd_soc_acpi_mach *atom_machine_select(struct snd_sof_dev *sdev); +void atom_set_mach_params(struct snd_soc_acpi_mach *mach, struct snd_sof_dev *sdev); extern struct snd_soc_dai_driver atom_dai[]; diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c index 1a8a39a878fd..1121711e9029 100644 --- a/sound/soc/sof/intel/bdw.c +++ b/sound/soc/sof/intel/bdw.c @@ -509,7 +509,7 @@ static int bdw_probe(struct snd_sof_dev *sdev) return ret; } -static void bdw_machine_select(struct snd_sof_dev *sdev) +static struct snd_soc_acpi_mach *bdw_machine_select(struct snd_sof_dev *sdev) { struct snd_sof_pdata *sof_pdata = sdev->pdata; const struct sof_dev_desc *desc = sof_pdata->desc; @@ -518,22 +518,23 @@ static void bdw_machine_select(struct snd_sof_dev *sdev) mach = snd_soc_acpi_find_machine(desc->machines); if (!mach) { dev_warn(sdev->dev, "warning: No matching ASoC machine driver found\n"); - return; + return NULL; } sof_pdata->tplg_filename = mach->sof_tplg_filename; mach->mach_params.acpi_ipc_irq_index = desc->irqindex_host_ipc; - sof_pdata->machine = mach; + + return mach; } -static void bdw_set_mach_params(const struct snd_soc_acpi_mach *mach, +static void bdw_set_mach_params(struct snd_soc_acpi_mach *mach, struct snd_sof_dev *sdev) { struct snd_sof_pdata *pdata = sdev->pdata; const struct sof_dev_desc *desc = pdata->desc; struct snd_soc_acpi_mach_params *mach_params; - mach_params = (struct snd_soc_acpi_mach_params *)&mach->mach_params; + mach_params = &mach->mach_params; mach_params->platform = dev_name(sdev->dev); mach_params->num_dai_drivers = desc->ops->num_drv; mach_params->dai_drivers = desc->ops->drv; diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index dabbd5d908f6..21100d2e6644 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -1105,7 +1105,8 @@ int hda_dsp_remove(struct snd_sof_dev *sdev) } #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) -static int hda_generic_machine_select(struct snd_sof_dev *sdev) +static void hda_generic_machine_select(struct snd_sof_dev *sdev, + struct snd_soc_acpi_mach **mach) { struct hdac_bus *bus = sof_to_bus(sdev); struct snd_soc_acpi_mach_params *mach_params; @@ -1137,7 +1138,7 @@ static int hda_generic_machine_select(struct snd_sof_dev *sdev) * - one HDMI codec, and/or * - one external HDAudio codec */ - if (!pdata->machine && codec_num <= 2) { + if (!*mach && codec_num <= 2) { hda_mach = snd_soc_acpi_intel_hda_machines; dev_info(bus->dev, "using HDA machine driver %s now\n", @@ -1152,10 +1153,9 @@ static int hda_generic_machine_select(struct snd_sof_dev *sdev) tplg_filename = hda_mach->sof_tplg_filename; ret = dmic_topology_fixup(sdev, &tplg_filename, idisp_str, &dmic_num); if (ret < 0) - return ret; + return; hda_mach->mach_params.dmic_num = dmic_num; - pdata->machine = hda_mach; pdata->tplg_filename = tplg_filename; if (codec_num == 2) { @@ -1165,23 +1165,22 @@ static int hda_generic_machine_select(struct snd_sof_dev *sdev) */ hda_mach->mach_params.link_mask = 0; } + + *mach = hda_mach; } } /* used by hda machine driver to create dai links */ - if (pdata->machine) { - mach_params = (struct snd_soc_acpi_mach_params *) - &pdata->machine->mach_params; + if (*mach) { + mach_params = &(*mach)->mach_params; mach_params->codec_mask = bus->codec_mask; mach_params->common_hdmi_codec_drv = hda_codec_use_common_hdmi; } - - return 0; } #else -static int hda_generic_machine_select(struct snd_sof_dev *sdev) +static void hda_generic_machine_select(struct snd_sof_dev *sdev, + struct snd_soc_acpi_mach **mach) { - return 0; } #endif @@ -1264,7 +1263,7 @@ static bool link_slaves_found(struct snd_sof_dev *sdev, return true; } -static int hda_sdw_machine_select(struct snd_sof_dev *sdev) +static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev) { struct snd_sof_pdata *pdata = sdev->pdata; const struct snd_soc_acpi_link_adr *link; @@ -1282,7 +1281,7 @@ static int hda_sdw_machine_select(struct snd_sof_dev *sdev) * machines, for mixed cases with I2C/I2S the detection relies * on the HID list. */ - if (link_mask && !pdata->machine) { + if (link_mask) { for (mach = pdata->desc->alt_machines; mach && mach->link_mask; mach++) { /* @@ -1317,7 +1316,6 @@ static int hda_sdw_machine_select(struct snd_sof_dev *sdev) if (mach && mach->link_mask) { int dmic_num = 0; - pdata->machine = mach; mach->mach_params.links = mach->links; mach->mach_params.link_mask = mach->link_mask; mach->mach_params.platform = dev_name(sdev->dev); @@ -1339,9 +1337,8 @@ static int hda_sdw_machine_select(struct snd_sof_dev *sdev) int ret; ret = dmic_topology_fixup(sdev, &tplg_filename, "", &dmic_num); - if (ret < 0) - return ret; + return NULL; pdata->tplg_filename = tplg_filename; } @@ -1351,35 +1348,36 @@ static int hda_sdw_machine_select(struct snd_sof_dev *sdev) "SoundWire machine driver %s topology %s\n", mach->drv_name, pdata->tplg_filename); - } else { - dev_info(sdev->dev, - "No SoundWire machine driver found\n"); + + return mach; } + + dev_info(sdev->dev, "No SoundWire machine driver found\n"); } - return 0; + return NULL; } #else -static int hda_sdw_machine_select(struct snd_sof_dev *sdev) +static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev) { - return 0; + return NULL; } #endif -void hda_set_mach_params(const struct snd_soc_acpi_mach *mach, +void hda_set_mach_params(struct snd_soc_acpi_mach *mach, struct snd_sof_dev *sdev) { struct snd_sof_pdata *pdata = sdev->pdata; const struct sof_dev_desc *desc = pdata->desc; struct snd_soc_acpi_mach_params *mach_params; - mach_params = (struct snd_soc_acpi_mach_params *)&mach->mach_params; + mach_params = &mach->mach_params; mach_params->platform = dev_name(sdev->dev); mach_params->num_dai_drivers = desc->ops->num_drv; mach_params->dai_drivers = desc->ops->drv; } -void hda_machine_select(struct snd_sof_dev *sdev) +struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev) { struct snd_sof_pdata *sof_pdata = sdev->pdata; const struct sof_dev_desc *desc = sof_pdata->desc; @@ -1394,8 +1392,6 @@ void hda_machine_select(struct snd_sof_dev *sdev) if (!sof_pdata->tplg_filename) sof_pdata->tplg_filename = mach->sof_tplg_filename; - sof_pdata->machine = mach; - if (mach->link_mask) { mach->mach_params.links = mach->links; mach->mach_params.link_mask = mach->link_mask; @@ -1405,16 +1401,18 @@ void hda_machine_select(struct snd_sof_dev *sdev) /* * If I2S fails, try SoundWire */ - hda_sdw_machine_select(sdev); + if (!mach) + mach = hda_sdw_machine_select(sdev); /* * Choose HDA generic machine driver if mach is NULL. * Otherwise, set certain mach params. */ - hda_generic_machine_select(sdev); - - if (!sof_pdata->machine) + hda_generic_machine_select(sdev, &mach); + if (!mach) dev_warn(sdev->dev, "warning: No matching ASoC machine driver found\n"); + + return mach; } int hda_pci_intel_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index cb71d9d5cf6c..5b4d59647a1d 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -728,8 +728,8 @@ extern const struct sof_intel_dsp_desc jsl_chip_info; extern const struct sof_intel_dsp_desc adls_chip_info; /* machine driver select */ -void hda_machine_select(struct snd_sof_dev *sdev); -void hda_set_mach_params(const struct snd_soc_acpi_mach *mach, +struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev); +void hda_set_mach_params(struct snd_soc_acpi_mach *mach, struct snd_sof_dev *sdev); /* PCI driver selection and probe */ diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 0226a53148c9..b0ffb2a93bcc 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -557,15 +557,17 @@ snd_sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata) sof_ops(sdev)->machine_unregister(sdev, pdata); } -static inline void +static inline struct snd_soc_acpi_mach * snd_sof_machine_select(struct snd_sof_dev *sdev) { if (sof_ops(sdev) && sof_ops(sdev)->machine_select) - sof_ops(sdev)->machine_select(sdev); + return sof_ops(sdev)->machine_select(sdev); + + return NULL; } static inline void -snd_sof_set_mach_params(const struct snd_soc_acpi_mach *mach, +snd_sof_set_mach_params(struct snd_soc_acpi_mach *mach, struct snd_sof_dev *sdev) { if (sof_ops(sdev) && sof_ops(sdev)->set_mach_params) diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 91e3fa5a7350..9e76b796502f 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -1027,9 +1027,10 @@ int sof_machine_check(struct snd_sof_dev *sdev) if (!IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE)) { /* find machine */ - snd_sof_machine_select(sdev); - if (sof_pdata->machine) { - snd_sof_set_mach_params(sof_pdata->machine, sdev); + mach = snd_sof_machine_select(sdev); + if (mach) { + sof_pdata->machine = mach; + snd_sof_set_mach_params(mach, sdev); return 0; } @@ -1051,7 +1052,7 @@ int sof_machine_check(struct snd_sof_dev *sdev) sof_pdata->tplg_filename = desc->nocodec_tplg_filename; sof_pdata->machine = mach; - snd_sof_set_mach_params(sof_pdata->machine, sdev); + snd_sof_set_mach_params(mach, sdev); return 0; } diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 16caf5c74035..114882e4370f 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -293,8 +293,8 @@ struct snd_sof_dsp_ops { void *pdata); /* optional */ void (*machine_unregister)(struct snd_sof_dev *sdev, void *pdata); /* optional */ - void (*machine_select)(struct snd_sof_dev *sdev); /* optional */ - void (*set_mach_params)(const struct snd_soc_acpi_mach *mach, + struct snd_soc_acpi_mach * (*machine_select)(struct snd_sof_dev *sdev); /* optional */ + void (*set_mach_params)(struct snd_soc_acpi_mach *mach, struct snd_sof_dev *sdev); /* optional */ /* DAI ops */ -- cgit v1.2.3 From 182b682b9ab1348e07ea1bf9d8f2505cc67f9237 Mon Sep 17 00:00:00 2001 From: Ajit Kumar Pandey Date: Thu, 16 Dec 2021 17:24:21 -0600 Subject: ASoC: SOF: ipc: Add null pointer check for substream->runtime When pcm stream is stopped "substream->runtime" pointer will be set to NULL by ALSA core. In case host received an ipc msg from firmware of type IPC_STREAM_POSITION after pcm stream is stopped, there will be kernel NULL pointer exception in ipc_period_elapsed(). This patch fixes it by adding NULL pointer check for "substream->runtime". Reviewed-by: Ranjani Sridharan Signed-off-by: Ajit Kumar Pandey Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20211216232422.345164-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 8a1eacc7ec5f..12860da1d373 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -623,7 +623,8 @@ static void ipc_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id) if (spcm->pcm.compress) snd_sof_compr_fragment_elapsed(stream->cstream); - else if (!stream->substream->runtime->no_period_wakeup) + else if (stream->substream->runtime && + !stream->substream->runtime->no_period_wakeup) /* only inform ALSA for period_wakeup mode */ snd_sof_pcm_period_elapsed(stream->substream); } -- cgit v1.2.3 From 60ded273e4c047aec364f70195aced71a4082f90 Mon Sep 17 00:00:00 2001 From: Karol Trzcinski Date: Thu, 16 Dec 2021 17:24:22 -0600 Subject: ipc: debug: Add shared memory heap to memory scan Newly added shared heap zones should be taken into account during memory usage scanning. Reviewed-by: Kai Vehmanen Reviewed-by: Liam Girdwood Signed-off-by: Karol Trzcinski Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20211216232422.345164-4-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- include/sound/sof/debug.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/sound/sof/debug.h b/include/sound/sof/debug.h index 3ecb5793789d..38693e3fb514 100644 --- a/include/sound/sof/debug.h +++ b/include/sound/sof/debug.h @@ -19,6 +19,8 @@ enum sof_ipc_dbg_mem_zone { SOF_IPC_MEM_ZONE_SYS_RUNTIME = 1, /**< System-runtime zone */ SOF_IPC_MEM_ZONE_RUNTIME = 2, /**< Runtime zone */ SOF_IPC_MEM_ZONE_BUFFER = 3, /**< Buffer zone */ + SOF_IPC_MEM_ZONE_RUNTIME_SHARED = 4, /**< System runtime zone */ + SOF_IPC_MEM_ZONE_SYS_SHARED = 5, /**< System shared zone */ }; /** ABI3.18 */ -- cgit v1.2.3 From 9b3c847b5fa0364a00227f13ab8ac0cbaf69be83 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 16 Dec 2021 09:00:18 +0900 Subject: ASoC: dt-bindings: audio-graph-port: enable both flag/phandle for bitclock/frame-master snd_soc_daifmt_parse_clock_provider_raw() is handling both bitclock/frame-master, and is supporting both flag/phandle. Current DT is assuming it is flag style. This patch allows both case. Signed-off-by: Kuninori Morimoto Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20211216000018.2641925-1-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/audio-graph-port.yaml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/audio-graph-port.yaml b/Documentation/devicetree/bindings/sound/audio-graph-port.yaml index 43e7f86e3b23..476dcb49ece6 100644 --- a/Documentation/devicetree/bindings/sound/audio-graph-port.yaml +++ b/Documentation/devicetree/bindings/sound/audio-graph-port.yaml @@ -42,10 +42,15 @@ patternProperties: $ref: /schemas/types.yaml#/definitions/flag frame-master: description: Indicates dai-link frame master. - $ref: /schemas/types.yaml#/definitions/phandle + oneOf: + - $ref: /schemas/types.yaml#/definitions/flag + - $ref: /schemas/types.yaml#/definitions/phandle bitclock-master: description: Indicates dai-link bit clock master - $ref: /schemas/types.yaml#/definitions/phandle + oneOf: + - $ref: /schemas/types.yaml#/definitions/flag + - $ref: /schemas/types.yaml#/definitions/phandle + dai-format: description: audio format. items: -- cgit v1.2.3 From 4941cd7cc845ae0a5317b1462e1b11bab4c023c0 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 16 Dec 2021 17:03:50 -0600 Subject: ASoC: SOF: Kconfig: Make the SOF_DEVELOPER_SUPPORT depend on SND_SOC_SOF SND_SOC_SOF_DEVELOPER_SUPPORT contains options affecting how the built SOF driver stack will behave, enables debug options and other features. These options have no meaning if the SND_SOC_SOF is not even enabled. If we have SOF client options under developer_support and debug they can be selected to be built even without the core, but they do need symbols from the core (the sof-client API) which can result build failure. In Kconfig we can have SND_SOC_SOF_TOPLEVEL=y SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST=y for example, which will make the flood client to be built, but the SOF core is not as SND_SOC_SOF is not selected. Reviewed-by: Paul Olaru Reviewed-by: Daniel Baluta Reviewed-by: Ranjani Sridharan Signed-off-by: Peter Ujfalusi Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20211216230350.343857-1-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index ac34c330cf0c..1a7d6cefd3b7 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -63,7 +63,7 @@ config SND_SOC_SOF_DEBUG_PROBES config SND_SOC_SOF_DEVELOPER_SUPPORT bool "SOF developer options support" - depends on EXPERT + depends on EXPERT && SND_SOC_SOF help This option unlocks SOF developer options for debug/performance/ code hardening. -- cgit v1.2.3 From 46f016119e2ac38d9efd32e4957bc888dc71fffe Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sat, 4 Dec 2021 17:37:04 +0300 Subject: ASoC: dt-bindings: Add binding for Tegra20 S/PDIF Add device-tree binding for Tegra20 S/PDIF controller. Reviewed-by: Rob Herring Signed-off-by: Dmitry Osipenko Acked-by: Thierry Reding Link: https://lore.kernel.org/r/20211204143725.31646-2-digetx@gmail.com Signed-off-by: Mark Brown --- .../bindings/sound/nvidia,tegra20-spdif.yaml | 85 ++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra20-spdif.yaml diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra20-spdif.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra20-spdif.yaml new file mode 100644 index 000000000000..60a368a132b8 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra20-spdif.yaml @@ -0,0 +1,85 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nvidia,tegra20-spdif.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra20 S/PDIF Controller + +description: | + The S/PDIF controller supports both input and output in serial audio + digital interface format. The input controller can digitally recover + a clock from the received stream. The S/PDIF controller is also used + to generate the embedded audio for HDMI output channel. + +maintainers: + - Thierry Reding + - Jon Hunter + +properties: + compatible: + const: nvidia,tegra20-spdif + + reg: + maxItems: 1 + + resets: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + minItems: 2 + + clock-names: + items: + - const: out + - const: in + + dmas: + minItems: 2 + + dma-names: + items: + - const: rx + - const: tx + + "#sound-dai-cells": + const: 0 + + nvidia,fixed-parent-rate: + description: | + Specifies whether board prefers parent clock to stay at a fixed rate. + This allows multiple Tegra20 audio components work simultaneously by + limiting number of supportable audio rates. + type: boolean + +required: + - compatible + - reg + - resets + - interrupts + - clocks + - clock-names + - dmas + - dma-names + - "#sound-dai-cells" + +additionalProperties: false + +examples: + - | + spdif@70002400 { + compatible = "nvidia,tegra20-spdif"; + reg = <0x70002400 0x200>; + interrupts = <77>; + clocks = <&clk 99>, <&clk 98>; + clock-names = "out", "in"; + resets = <&rst 10>; + dmas = <&apbdma 3>, <&apbdma 3>; + dma-names = "rx", "tx"; + #sound-dai-cells = <0>; + }; + +... -- cgit v1.2.3 From 80c3d0a97abfd2a678b6077236a77ccb8c4747fa Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sat, 4 Dec 2021 17:37:05 +0300 Subject: ASoC: dt-bindings: tegra20-i2s: Convert to schema Convert NVIDIA Tegra20 I2S binding to schema. Reviewed-by: Rob Herring Signed-off-by: Dmitry Osipenko Acked-by: Thierry Reding Link: https://lore.kernel.org/r/20211204143725.31646-3-digetx@gmail.com Signed-off-by: Mark Brown --- .../bindings/sound/nvidia,tegra20-i2s.txt | 30 ---------- .../bindings/sound/nvidia,tegra20-i2s.yaml | 70 ++++++++++++++++++++++ 2 files changed, 70 insertions(+), 30 deletions(-) delete mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.txt create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.yaml diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.txt deleted file mode 100644 index dc30c6bfbe95..000000000000 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.txt +++ /dev/null @@ -1,30 +0,0 @@ -NVIDIA Tegra 20 I2S controller - -Required properties: -- compatible : "nvidia,tegra20-i2s" -- reg : Should contain I2S registers location and length -- interrupts : Should contain I2S interrupt -- resets : Must contain an entry for each entry in reset-names. - See ../reset/reset.txt for details. -- reset-names : Must include the following entries: - - i2s -- dmas : Must contain an entry for each entry in clock-names. - See ../dma/dma.txt for details. -- dma-names : Must include the following entries: - - rx - - tx -- clocks : Must contain one entry, for the module clock. - See ../clocks/clock-bindings.txt for details. - -Example: - -i2s@70002800 { - compatible = "nvidia,tegra20-i2s"; - reg = <0x70002800 0x200>; - interrupts = < 45 >; - clocks = <&tegra_car 11>; - resets = <&tegra_car 11>; - reset-names = "i2s"; - dmas = <&apbdma 21>, <&apbdma 21>; - dma-names = "rx", "tx"; -}; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.yaml new file mode 100644 index 000000000000..ad43b237d9af --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.yaml @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nvidia,tegra20-i2s.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra20 I2S Controller + +description: | + The I2S Controller streams synchronous serial audio data between system + memory and an external audio device. The controller supports the I2S Left + Justified Mode, Right Justified Mode, and DSP mode formats. + +maintainers: + - Thierry Reding + - Jon Hunter + +properties: + compatible: + const: nvidia,tegra20-i2s + + reg: + maxItems: 1 + + resets: + maxItems: 1 + + reset-names: + const: i2s + + interrupts: + maxItems: 1 + + clocks: + minItems: 1 + + dmas: + minItems: 2 + + dma-names: + items: + - const: rx + - const: tx + +required: + - compatible + - reg + - resets + - reset-names + - interrupts + - clocks + - dmas + - dma-names + +additionalProperties: false + +examples: + - | + i2s@70002800 { + compatible = "nvidia,tegra20-i2s"; + reg = <0x70002800 0x200>; + interrupts = <45>; + clocks = <&tegra_car 11>; + resets = <&tegra_car 11>; + reset-names = "i2s"; + dmas = <&apbdma 21>, <&apbdma 21>; + dma-names = "rx", "tx"; + }; + +... -- cgit v1.2.3 From 549818e5c85a6d806cdef146d0203df2689d4e2f Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sat, 4 Dec 2021 17:37:06 +0300 Subject: ASoC: dt-bindings: tegra20-i2s: Document new nvidia,fixed-parent-rate property Document new nvidia,fixed-parent-rate property which instructs that this board wants parent clock to stay at a fixed rate. It allows to prevent conflicts between audio components that share same parent PLL. For instance, this property allows to have HDMI audio, speaker and headphones in the system playing audio simultaneously, which is a common pattern for consumer devices. Reviewed-by: Rob Herring Signed-off-by: Dmitry Osipenko Link: https://lore.kernel.org/r/20211204143725.31646-4-digetx@gmail.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.yaml index ad43b237d9af..68ae124eaf80 100644 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.yaml +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra20-i2s.yaml @@ -42,6 +42,13 @@ properties: - const: rx - const: tx + nvidia,fixed-parent-rate: + description: | + Specifies whether board prefers parent clock to stay at a fixed rate. + This allows multiple Tegra20 audio components work simultaneously by + limiting number of supportable audio rates. + type: boolean + required: - compatible - reg -- cgit v1.2.3 From 16736a0221db6d6f3fe130750c6dc5bbf5417da4 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sat, 4 Dec 2021 17:37:08 +0300 Subject: ASoC: tegra20: spdif: Set FIFO trigger level FIFO trigger level must be not less than the size of DMA burst, otherwise audio will be played x4 faster that it should be because part of the DMA data will be dropped on FIFO input buffer overflow. Signed-off-by: Dmitry Osipenko Acked-by: Thierry Reding Link: https://lore.kernel.org/r/20211204143725.31646-6-digetx@gmail.com Signed-off-by: Mark Brown --- sound/soc/tegra/tegra20_spdif.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c index 57a6c576b91f..e45e371edc42 100644 --- a/sound/soc/tegra/tegra20_spdif.c +++ b/sound/soc/tegra/tegra20_spdif.c @@ -69,6 +69,14 @@ static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream, regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL, mask, val); + /* + * FIFO trigger level must be bigger than DMA burst or equal to it, + * otherwise data is discarded on overflow. + */ + regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_DATA_FIFO_CSR, + TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_MASK, + TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU4_WORD_FULL); + switch (params_rate(params)) { case 32000: spdifclock = 4096000; -- cgit v1.2.3 From c0000fc618cdbe190274cf37040033dfa23c159d Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sat, 4 Dec 2021 17:37:10 +0300 Subject: ASoC: tegra20: spdif: Support device-tree Tegra20 S/PDIF driver was added in a pre-DT era and was never used since that time. Revive driver by adding device-tree support. Signed-off-by: Dmitry Osipenko Acked-by: Thierry Reding Link: https://lore.kernel.org/r/20211204143725.31646-8-digetx@gmail.com Signed-off-by: Mark Brown --- sound/soc/tegra/tegra20_spdif.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c index e45e371edc42..801784915004 100644 --- a/sound/soc/tegra/tegra20_spdif.c +++ b/sound/soc/tegra/tegra20_spdif.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -270,7 +271,7 @@ static int tegra20_spdif_platform_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, spdif); - spdif->clk_spdif_out = devm_clk_get(&pdev->dev, "spdif_out"); + spdif->clk_spdif_out = devm_clk_get(&pdev->dev, "out"); if (IS_ERR(spdif->clk_spdif_out)) { pr_err("Can't retrieve spdif clock\n"); ret = PTR_ERR(spdif->clk_spdif_out); @@ -340,10 +341,17 @@ static const struct dev_pm_ops tegra20_spdif_pm_ops = { tegra20_spdif_runtime_resume, NULL) }; +static const struct of_device_id tegra20_spdif_of_match[] = { + { .compatible = "nvidia,tegra20-spdif", }, + {}, +}; +MODULE_DEVICE_TABLE(of, tegra20_spdif_of_match); + static struct platform_driver tegra20_spdif_driver = { .driver = { .name = DRV_NAME, .pm = &tegra20_spdif_pm_ops, + .of_match_table = tegra20_spdif_of_match, }, .probe = tegra20_spdif_platform_probe, .remove = tegra20_spdif_platform_remove, @@ -354,4 +362,3 @@ module_platform_driver(tegra20_spdif_driver); MODULE_AUTHOR("Stephen Warren "); MODULE_DESCRIPTION("Tegra20 SPDIF ASoC driver"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" DRV_NAME); -- cgit v1.2.3 From 117aeed43974e500dcbd106e51218a83ae2c9977 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sat, 4 Dec 2021 17:37:11 +0300 Subject: ASoC: tegra20: spdif: Improve driver's code - Clean up whitespaces, defines and variables. - Remove obsolete code. - Adhere to upstream coding style. - Don't override returned error code. - Replace pr_err with dev_err. No functional changes are made by this patch. This is a minor code's refactoring that will ease further maintenance of the driver. Signed-off-by: Dmitry Osipenko Acked-by: Thierry Reding Link: https://lore.kernel.org/r/20211204143725.31646-9-digetx@gmail.com Signed-off-by: Mark Brown --- sound/soc/tegra/tegra20_spdif.c | 49 +++++++++++++++-------------------------- 1 file changed, 18 insertions(+), 31 deletions(-) diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c index 801784915004..d5c618611dbb 100644 --- a/sound/soc/tegra/tegra20_spdif.c +++ b/sound/soc/tegra/tegra20_spdif.c @@ -23,8 +23,6 @@ #include "tegra20_spdif.h" -#define DRV_NAME "tegra20-spdif" - static __maybe_unused int tegra20_spdif_runtime_suspend(struct device *dev) { struct tegra20_spdif *spdif = dev_get_drvdata(dev); @@ -49,11 +47,10 @@ static __maybe_unused int tegra20_spdif_runtime_resume(struct device *dev) } static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) { - struct device *dev = dai->dev; - struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai); + struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev); unsigned int mask = 0, val = 0; int ret, spdifclock; @@ -106,7 +103,7 @@ static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream, ret = clk_set_rate(spdif->clk_spdif_out, spdifclock); if (ret) { - dev_err(dev, "Can't set SPDIF clock rate: %d\n", ret); + dev_err(dai->dev, "Can't set SPDIF clock rate: %d\n", ret); return ret; } @@ -127,9 +124,9 @@ static void tegra20_spdif_stop_playback(struct tegra20_spdif *spdif) } static int tegra20_spdif_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) + struct snd_soc_dai *dai) { - struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai); + struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev); switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -151,7 +148,7 @@ static int tegra20_spdif_trigger(struct snd_pcm_substream *substream, int cmd, static int tegra20_spdif_probe(struct snd_soc_dai *dai) { - struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai); + struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev); dai->capture_dma_data = NULL; dai->playback_dma_data = &spdif->playback_dma_data; @@ -160,26 +157,26 @@ static int tegra20_spdif_probe(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops tegra20_spdif_dai_ops = { - .hw_params = tegra20_spdif_hw_params, - .trigger = tegra20_spdif_trigger, + .hw_params = tegra20_spdif_hw_params, + .trigger = tegra20_spdif_trigger, }; static struct snd_soc_dai_driver tegra20_spdif_dai = { - .name = DRV_NAME, + .name = "tegra20-spdif", .probe = tegra20_spdif_probe, .playback = { .stream_name = "Playback", .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000, + SNDRV_PCM_RATE_48000, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, .ops = &tegra20_spdif_dai_ops, }; static const struct snd_soc_component_driver tegra20_spdif_component = { - .name = DRV_NAME, + .name = "tegra20-spdif", }; static bool tegra20_spdif_wr_rd_reg(struct device *dev, unsigned int reg) @@ -260,7 +257,7 @@ static const struct regmap_config tegra20_spdif_regmap_config = { static int tegra20_spdif_platform_probe(struct platform_device *pdev) { struct tegra20_spdif *spdif; - struct resource *mem, *dmareq; + struct resource *mem; void __iomem *regs; int ret; @@ -273,27 +270,19 @@ static int tegra20_spdif_platform_probe(struct platform_device *pdev) spdif->clk_spdif_out = devm_clk_get(&pdev->dev, "out"); if (IS_ERR(spdif->clk_spdif_out)) { - pr_err("Can't retrieve spdif clock\n"); - ret = PTR_ERR(spdif->clk_spdif_out); - return ret; + dev_err(&pdev->dev, "Could not retrieve spdif clock\n"); + return PTR_ERR(spdif->clk_spdif_out); } regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); if (IS_ERR(regs)) return PTR_ERR(regs); - dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!dmareq) { - dev_err(&pdev->dev, "No DMA resource\n"); - return -ENODEV; - } - spdif->regmap = devm_regmap_init_mmio(&pdev->dev, regs, - &tegra20_spdif_regmap_config); + &tegra20_spdif_regmap_config); if (IS_ERR(spdif->regmap)) { dev_err(&pdev->dev, "regmap init failed\n"); - ret = PTR_ERR(spdif->regmap); - return ret; + return PTR_ERR(spdif->regmap); } spdif->playback_dma_data.addr = mem->start + TEGRA20_SPDIF_DATA_OUT; @@ -306,7 +295,6 @@ static int tegra20_spdif_platform_probe(struct platform_device *pdev) &tegra20_spdif_dai, 1); if (ret) { dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); - ret = -ENOMEM; goto err_pm_disable; } @@ -349,14 +337,13 @@ MODULE_DEVICE_TABLE(of, tegra20_spdif_of_match); static struct platform_driver tegra20_spdif_driver = { .driver = { - .name = DRV_NAME, + .name = "tegra20-spdif", .pm = &tegra20_spdif_pm_ops, .of_match_table = tegra20_spdif_of_match, }, .probe = tegra20_spdif_platform_probe, .remove = tegra20_spdif_platform_remove, }; - module_platform_driver(tegra20_spdif_driver); MODULE_AUTHOR("Stephen Warren "); -- cgit v1.2.3 From 150f4d573fe19a77864f6dec31aa444332f9fc9e Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sat, 4 Dec 2021 17:37:12 +0300 Subject: ASoC: tegra20: spdif: Use more resource-managed helpers Use resource-managed helpers to make code cleaner. Driver's remove callback isn't needed anymore since driver is completely resource-managed now. Signed-off-by: Dmitry Osipenko Acked-by: Thierry Reding Link: https://lore.kernel.org/r/20211204143725.31646-10-digetx@gmail.com Signed-off-by: Mark Brown --- sound/soc/tegra/tegra20_spdif.c | 33 +++++++++------------------------ sound/soc/tegra/tegra_pcm.c | 6 ++++++ sound/soc/tegra/tegra_pcm.h | 1 + 3 files changed, 16 insertions(+), 24 deletions(-) diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c index d5c618611dbb..7dd263721c2c 100644 --- a/sound/soc/tegra/tegra20_spdif.c +++ b/sound/soc/tegra/tegra20_spdif.c @@ -289,38 +289,24 @@ static int tegra20_spdif_platform_probe(struct platform_device *pdev) spdif->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; spdif->playback_dma_data.maxburst = 4; - pm_runtime_enable(&pdev->dev); + ret = devm_pm_runtime_enable(&pdev->dev); + if (ret) + return ret; - ret = snd_soc_register_component(&pdev->dev, &tegra20_spdif_component, - &tegra20_spdif_dai, 1); + ret = devm_snd_soc_register_component(&pdev->dev, + &tegra20_spdif_component, + &tegra20_spdif_dai, 1); if (ret) { dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); - goto err_pm_disable; + return ret; } - ret = tegra_pcm_platform_register(&pdev->dev); + ret = devm_tegra_pcm_platform_register(&pdev->dev); if (ret) { dev_err(&pdev->dev, "Could not register PCM: %d\n", ret); - goto err_unregister_component; + return ret; } - return 0; - -err_unregister_component: - snd_soc_unregister_component(&pdev->dev); -err_pm_disable: - pm_runtime_disable(&pdev->dev); - - return ret; -} - -static int tegra20_spdif_platform_remove(struct platform_device *pdev) -{ - tegra_pcm_platform_unregister(&pdev->dev); - snd_soc_unregister_component(&pdev->dev); - - pm_runtime_disable(&pdev->dev); - return 0; } @@ -342,7 +328,6 @@ static struct platform_driver tegra20_spdif_driver = { .of_match_table = tegra20_spdif_of_match, }, .probe = tegra20_spdif_platform_probe, - .remove = tegra20_spdif_platform_remove, }; module_platform_driver(tegra20_spdif_driver); diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c index ef1e74d95236..468c8e77de21 100644 --- a/sound/soc/tegra/tegra_pcm.c +++ b/sound/soc/tegra/tegra_pcm.c @@ -48,6 +48,12 @@ int tegra_pcm_platform_register(struct device *dev) } EXPORT_SYMBOL_GPL(tegra_pcm_platform_register); +int devm_tegra_pcm_platform_register(struct device *dev) +{ + return devm_snd_dmaengine_pcm_register(dev, &tegra_dmaengine_pcm_config, 0); +} +EXPORT_SYMBOL_GPL(devm_tegra_pcm_platform_register); + int tegra_pcm_platform_register_with_chan_names(struct device *dev, struct snd_dmaengine_pcm_config *config, char *txdmachan, char *rxdmachan) diff --git a/sound/soc/tegra/tegra_pcm.h b/sound/soc/tegra/tegra_pcm.h index d602126c65b7..2a36eea1740d 100644 --- a/sound/soc/tegra/tegra_pcm.h +++ b/sound/soc/tegra/tegra_pcm.h @@ -32,6 +32,7 @@ int tegra_pcm_hw_params(struct snd_soc_component *component, snd_pcm_uframes_t tegra_pcm_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream); int tegra_pcm_platform_register(struct device *dev); +int devm_tegra_pcm_platform_register(struct device *dev); int tegra_pcm_platform_register_with_chan_names(struct device *dev, struct snd_dmaengine_pcm_config *config, char *txdmachan, char *rxdmachan); -- cgit v1.2.3 From ec1b4545d75575118e01a5e95699cff5010b4e19 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sat, 4 Dec 2021 17:37:13 +0300 Subject: ASoC: tegra20: spdif: Reset hardware Reset S/PDIF controller on runtime PM suspend/resume to ensure that we always have a consistent hardware state. Signed-off-by: Dmitry Osipenko Acked-by: Thierry Reding Link: https://lore.kernel.org/r/20211204143725.31646-11-digetx@gmail.com Signed-off-by: Mark Brown --- sound/soc/tegra/tegra20_spdif.c | 32 ++++++++++++++++++++++++++++++++ sound/soc/tegra/tegra20_spdif.h | 1 + 2 files changed, 33 insertions(+) diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c index 7dd263721c2c..bc45a0a8afab 100644 --- a/sound/soc/tegra/tegra20_spdif.c +++ b/sound/soc/tegra/tegra20_spdif.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -14,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +29,8 @@ static __maybe_unused int tegra20_spdif_runtime_suspend(struct device *dev) { struct tegra20_spdif *spdif = dev_get_drvdata(dev); + regcache_cache_only(spdif->regmap, true); + clk_disable_unprepare(spdif->clk_spdif_out); return 0; @@ -37,13 +41,35 @@ static __maybe_unused int tegra20_spdif_runtime_resume(struct device *dev) struct tegra20_spdif *spdif = dev_get_drvdata(dev); int ret; + ret = reset_control_assert(spdif->reset); + if (ret) + return ret; + ret = clk_prepare_enable(spdif->clk_spdif_out); if (ret) { dev_err(dev, "clk_enable failed: %d\n", ret); return ret; } + usleep_range(10, 100); + + ret = reset_control_deassert(spdif->reset); + if (ret) + goto disable_clocks; + + regcache_cache_only(spdif->regmap, false); + regcache_mark_dirty(spdif->regmap); + + ret = regcache_sync(spdif->regmap); + if (ret) + goto disable_clocks; + return 0; + +disable_clocks: + clk_disable_unprepare(spdif->clk_spdif_out); + + return ret; } static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream, @@ -268,6 +294,12 @@ static int tegra20_spdif_platform_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, spdif); + spdif->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL); + if (IS_ERR(spdif->reset)) { + dev_err(&pdev->dev, "Can't retrieve spdif reset\n"); + return PTR_ERR(spdif->reset); + } + spdif->clk_spdif_out = devm_clk_get(&pdev->dev, "out"); if (IS_ERR(spdif->clk_spdif_out)) { dev_err(&pdev->dev, "Could not retrieve spdif clock\n"); diff --git a/sound/soc/tegra/tegra20_spdif.h b/sound/soc/tegra/tegra20_spdif.h index 1973ffc2d5c7..ff4b79e2052f 100644 --- a/sound/soc/tegra/tegra20_spdif.h +++ b/sound/soc/tegra/tegra20_spdif.h @@ -451,6 +451,7 @@ struct tegra20_spdif { struct snd_dmaengine_dai_dma_data capture_dma_data; struct snd_dmaengine_dai_dma_data playback_dma_data; struct regmap *regmap; + struct reset_control *reset; }; #endif -- cgit v1.2.3 From d51693092ecc732fca3f49549cde1c5206331b09 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sat, 4 Dec 2021 17:37:14 +0300 Subject: ASoC: tegra20: spdif: Support system suspend Support system suspend by enforcing runtime PM suspend/resume. Now there is no doubt that h/w is indeed stopped during suspend and that h/w state will be properly restored after resume. Signed-off-by: Dmitry Osipenko Acked-by: Thierry Reding Link: https://lore.kernel.org/r/20211204143725.31646-12-digetx@gmail.com Signed-off-by: Mark Brown --- sound/soc/tegra/tegra20_spdif.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c index bc45a0a8afab..a4aa5614aef4 100644 --- a/sound/soc/tegra/tegra20_spdif.c +++ b/sound/soc/tegra/tegra20_spdif.c @@ -345,6 +345,8 @@ static int tegra20_spdif_platform_probe(struct platform_device *pdev) static const struct dev_pm_ops tegra20_spdif_pm_ops = { SET_RUNTIME_PM_OPS(tegra20_spdif_runtime_suspend, tegra20_spdif_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) }; static const struct of_device_id tegra20_spdif_of_match[] = { -- cgit v1.2.3 From 9d8f51cd1fa993939db02a014d4f4b6e252c2a18 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sat, 4 Dec 2021 17:37:15 +0300 Subject: ASoC: tegra20: spdif: Filter out unsupported rates SPDIF and other SoC components share audio PLL on Tegra, thus only one component may set the desired base clock rate. This creates problem for HDMI audio because it uses SPDIF and audio may not work if SPDIF's clock doesn't exactly match standard audio rate since some receivers may reject audio in that case. Filter out audio rates which SPDIF output can't support, assuming that other components won't change rate at runtime. Signed-off-by: Dmitry Osipenko Acked-by: Thierry Reding Link: https://lore.kernel.org/r/20211204143725.31646-13-digetx@gmail.com Signed-off-by: Mark Brown --- sound/soc/tegra/tegra20_spdif.c | 61 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c index a4aa5614aef4..d09cd7ee6879 100644 --- a/sound/soc/tegra/tegra20_spdif.c +++ b/sound/soc/tegra/tegra20_spdif.c @@ -79,6 +79,7 @@ static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream, struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev); unsigned int mask = 0, val = 0; int ret, spdifclock; + long rate; mask |= TEGRA20_SPDIF_CTRL_PACK | TEGRA20_SPDIF_CTRL_BIT_MODE_MASK; @@ -133,6 +134,12 @@ static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream, return ret; } + rate = clk_get_rate(spdif->clk_spdif_out); + if (rate != spdifclock) + dev_warn_once(dai->dev, + "SPDIF clock rate %d doesn't match requested rate %lu\n", + spdifclock, rate); + return 0; } @@ -172,6 +179,59 @@ static int tegra20_spdif_trigger(struct snd_pcm_substream *substream, int cmd, return 0; } +static int tegra20_spdif_filter_rates(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *r = hw_param_interval(params, rule->var); + struct snd_soc_dai *dai = rule->private; + struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev); + struct clk *parent = clk_get_parent(spdif->clk_spdif_out); + const unsigned int rates[] = { 32000, 44100, 48000 }; + long i, parent_rate, valid_rates = 0; + + parent_rate = clk_get_rate(parent); + if (parent_rate <= 0) { + dev_err(dai->dev, "Can't get parent clock rate: %ld\n", + parent_rate); + return parent_rate ?: -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(rates); i++) { + if (parent_rate % (rates[i] * 128) == 0) + valid_rates |= BIT(i); + } + + /* + * At least one rate must be valid, otherwise the parent clock isn't + * audio PLL. Nothing should be filtered in this case. + */ + if (!valid_rates) + valid_rates = BIT(ARRAY_SIZE(rates)) - 1; + + return snd_interval_list(r, ARRAY_SIZE(rates), rates, valid_rates); +} + +static int tegra20_spdif_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + if (!device_property_read_bool(dai->dev, "nvidia,fixed-parent-rate")) + return 0; + + /* + * SPDIF and I2S share audio PLL. HDMI takes audio packets from SPDIF + * and audio may not work on some TVs if clock rate isn't precise. + * + * PLL rate is controlled by I2S side. Filter out audio rates that + * don't match PLL rate at the start of stream to allow both SPDIF + * and I2S work simultaneously, assuming that PLL rate won't be + * changed later on. + */ + return snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + tegra20_spdif_filter_rates, dai, + SNDRV_PCM_HW_PARAM_RATE, -1); +} + static int tegra20_spdif_probe(struct snd_soc_dai *dai) { struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev); @@ -185,6 +245,7 @@ static int tegra20_spdif_probe(struct snd_soc_dai *dai) static const struct snd_soc_dai_ops tegra20_spdif_dai_ops = { .hw_params = tegra20_spdif_hw_params, .trigger = tegra20_spdif_trigger, + .startup = tegra20_spdif_startup, }; static struct snd_soc_dai_driver tegra20_spdif_dai = { -- cgit v1.2.3 From bfa4671db1effe315cade5bddd6cf025e1c403d0 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sat, 4 Dec 2021 17:37:16 +0300 Subject: ASoC: tegra20: i2s: Filter out unsupported rates Support new nvidia,fixed-parent-rate device-tree property which instructs I2S that board wants parent clock rate to stay at a fixed rate. This allows to play audio over S/PDIF and I2S simultaneously. The root of the problem is that audio components on Tegra share the same audio PLL, and thus, only a subset of rates can be supported if we want to play audio simultaneously. Filter out audio rates that don't match parent clock rate if device-tree has the nvidia,fixed-parent-rate property. Signed-off-by: Dmitry Osipenko Acked-by: Thierry Reding Link: https://lore.kernel.org/r/20211204143725.31646-14-digetx@gmail.com Signed-off-by: Mark Brown --- sound/soc/tegra/tegra20_i2s.c | 49 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/sound/soc/tegra/tegra20_i2s.c b/sound/soc/tegra/tegra20_i2s.c index 266d2cab9f49..27365a877e47 100644 --- a/sound/soc/tegra/tegra20_i2s.c +++ b/sound/soc/tegra/tegra20_i2s.c @@ -262,10 +262,59 @@ static int tegra20_i2s_probe(struct snd_soc_dai *dai) return 0; } +static const unsigned int tegra20_i2s_rates[] = { + 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200, 96000 +}; + +static int tegra20_i2s_filter_rates(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *r = hw_param_interval(params, rule->var); + struct snd_soc_dai *dai = rule->private; + struct tegra20_i2s *i2s = dev_get_drvdata(dai->dev); + struct clk *parent = clk_get_parent(i2s->clk_i2s); + long i, parent_rate, valid_rates = 0; + + parent_rate = clk_get_rate(parent); + if (parent_rate <= 0) { + dev_err(dai->dev, "Can't get parent clock rate: %ld\n", + parent_rate); + return parent_rate ?: -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(tegra20_i2s_rates); i++) { + if (parent_rate % (tegra20_i2s_rates[i] * 128) == 0) + valid_rates |= BIT(i); + } + + /* + * At least one rate must be valid, otherwise the parent clock isn't + * audio PLL. Nothing should be filtered in this case. + */ + if (!valid_rates) + valid_rates = BIT(ARRAY_SIZE(tegra20_i2s_rates)) - 1; + + return snd_interval_list(r, ARRAY_SIZE(tegra20_i2s_rates), + tegra20_i2s_rates, valid_rates); +} + +static int tegra20_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + if (!device_property_read_bool(dai->dev, "nvidia,fixed-parent-rate")) + return 0; + + return snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + tegra20_i2s_filter_rates, dai, + SNDRV_PCM_HW_PARAM_RATE, -1); +} + static const struct snd_soc_dai_ops tegra20_i2s_dai_ops = { .set_fmt = tegra20_i2s_set_fmt, .hw_params = tegra20_i2s_hw_params, .trigger = tegra20_i2s_trigger, + .startup = tegra20_i2s_startup, }; static const struct snd_soc_dai_driver tegra20_i2s_dai_template = { -- cgit v1.2.3 From 2a9a72e290d4a4741e673f86b9fba9bfb319786d Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Thu, 16 Dec 2021 12:57:39 +0100 Subject: ASoC: Intel: catpt: Test dmaengine_submit() result before moving on After calling dmaengine_submit(), the submitted transfer descriptor belongs to the DMA engine. Pointer to that descriptor may no longer be valid after the call and should be tested before awaiting transfer completion. Reported-by: Kevin Tian Suggested-by: Dave Jiang Fixes: 4fac9b31d0b9 ("ASoC: Intel: Add catpt base members") Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20211216115743.2130622-2-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/dsp.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/catpt/dsp.c b/sound/soc/intel/catpt/dsp.c index 9c5fd18f2600..346bec000306 100644 --- a/sound/soc/intel/catpt/dsp.c +++ b/sound/soc/intel/catpt/dsp.c @@ -65,6 +65,7 @@ static int catpt_dma_memcpy(struct catpt_dev *cdev, struct dma_chan *chan, { struct dma_async_tx_descriptor *desc; enum dma_status status; + int ret; desc = dmaengine_prep_dma_memcpy(chan, dst_addr, src_addr, size, DMA_CTRL_ACK); @@ -77,13 +78,22 @@ static int catpt_dma_memcpy(struct catpt_dev *cdev, struct dma_chan *chan, catpt_updatel_shim(cdev, HMDC, CATPT_HMDC_HDDA(CATPT_DMA_DEVID, chan->chan_id), CATPT_HMDC_HDDA(CATPT_DMA_DEVID, chan->chan_id)); - dmaengine_submit(desc); + + ret = dma_submit_error(dmaengine_submit(desc)); + if (ret) { + dev_err(cdev->dev, "submit tx failed: %d\n", ret); + goto clear_hdda; + } + status = dma_wait_for_async_tx(desc); + ret = (status == DMA_COMPLETE) ? 0 : -EPROTO; + +clear_hdda: /* regardless of status, disable access to HOST memory in demand mode */ catpt_updatel_shim(cdev, HMDC, CATPT_HMDC_HDDA(CATPT_DMA_DEVID, chan->chan_id), 0); - return (status == DMA_COMPLETE) ? 0 : -EPROTO; + return ret; } int catpt_dma_memcpy_todsp(struct catpt_dev *cdev, struct dma_chan *chan, -- cgit v1.2.3 From dad492cfd24caf1b62d598555cde279bcca4755e Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Thu, 16 Dec 2021 12:57:40 +0100 Subject: ASoC: Intel: catpt: Reduce size of catpt_component_open() With some improved if-logy, function's size can be reduced slightly. Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20211216115743.2130622-3-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/pcm.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c index ebb27daeb1c7..16146c693c08 100644 --- a/sound/soc/intel/catpt/pcm.c +++ b/sound/soc/intel/catpt/pcm.c @@ -595,9 +595,8 @@ static int catpt_component_open(struct snd_soc_component *component, { struct snd_soc_pcm_runtime *rtm = substream->private_data; - if (rtm->dai_link->no_pcm) - return 0; - snd_soc_set_runtime_hwparams(substream, &catpt_pcm_hardware); + if (!rtm->dai_link->no_pcm) + snd_soc_set_runtime_hwparams(substream, &catpt_pcm_hardware); return 0; } -- cgit v1.2.3 From a62a02986d3990f4b55c2d75610f8fb2074b0870 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Thu, 16 Dec 2021 12:57:41 +0100 Subject: ASoC: Intel: catpt: Streamline locals declaration for PCM-functions Group all the catpt_xxx structs together in PCM related functions so they look more cohesive. Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20211216115743.2130622-4-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/pcm.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c index 16146c693c08..939a9b801dec 100644 --- a/sound/soc/intel/catpt/pcm.c +++ b/sound/soc/intel/catpt/pcm.c @@ -259,9 +259,9 @@ static enum catpt_channel_config catpt_get_channel_config(u32 num_channels) static int catpt_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct catpt_dev *cdev = dev_get_drvdata(dai->dev); struct catpt_stream_template *template; struct catpt_stream_runtime *stream; + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); struct resource *res; int ret; @@ -306,8 +306,8 @@ err_pgtbl: static void catpt_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct catpt_dev *cdev = dev_get_drvdata(dai->dev); struct catpt_stream_runtime *stream; + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); stream = snd_soc_dai_get_dma_data(dai, substream); @@ -329,9 +329,9 @@ static int catpt_set_dspvol(struct catpt_dev *cdev, u8 stream_id, long *ctlvol); static int catpt_dai_apply_usettings(struct snd_soc_dai *dai, struct catpt_stream_runtime *stream) { - struct catpt_dev *cdev = dev_get_drvdata(dai->dev); struct snd_soc_component *component = dai->component; struct snd_kcontrol *pos; + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); const char *name; int ret; u32 id = stream->info.stream_hw_id; @@ -374,12 +374,12 @@ static int catpt_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct catpt_dev *cdev = dev_get_drvdata(dai->dev); + struct snd_pcm_runtime *rtm = substream->runtime; + struct snd_dma_buffer *dmab; struct catpt_stream_runtime *stream; struct catpt_audio_format afmt; struct catpt_ring_info rinfo; - struct snd_pcm_runtime *rtm = substream->runtime; - struct snd_dma_buffer *dmab; + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); int ret; stream = snd_soc_dai_get_dma_data(dai, substream); @@ -427,8 +427,8 @@ static int catpt_dai_hw_params(struct snd_pcm_substream *substream, static int catpt_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct catpt_dev *cdev = dev_get_drvdata(dai->dev); struct catpt_stream_runtime *stream; + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); stream = snd_soc_dai_get_dma_data(dai, substream); if (!stream->allocated) @@ -444,8 +444,8 @@ static int catpt_dai_hw_free(struct snd_pcm_substream *substream, static int catpt_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct catpt_dev *cdev = dev_get_drvdata(dai->dev); struct catpt_stream_runtime *stream; + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); int ret; stream = snd_soc_dai_get_dma_data(dai, substream); @@ -467,9 +467,9 @@ static int catpt_dai_prepare(struct snd_pcm_substream *substream, static int catpt_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { - struct catpt_dev *cdev = dev_get_drvdata(dai->dev); - struct catpt_stream_runtime *stream; struct snd_pcm_runtime *runtime = substream->runtime; + struct catpt_stream_runtime *stream; + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); snd_pcm_uframes_t pos; int ret; @@ -604,10 +604,10 @@ static snd_pcm_uframes_t catpt_component_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct catpt_dev *cdev = dev_get_drvdata(component->dev); - struct catpt_stream_runtime *stream; struct snd_soc_pcm_runtime *rtm = substream->private_data; struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtm, 0); + struct catpt_stream_runtime *stream; + struct catpt_dev *cdev = dev_get_drvdata(component->dev); u32 pos; if (rtm->dai_link->no_pcm) @@ -632,8 +632,8 @@ static int catpt_dai_pcm_new(struct snd_soc_pcm_runtime *rtm, struct snd_soc_dai *dai) { struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtm, 0); - struct catpt_dev *cdev = dev_get_drvdata(dai->dev); struct catpt_ssp_device_format devfmt; + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); int ret; devfmt.iface = dai->driver->id; @@ -893,8 +893,8 @@ static int catpt_stream_volume_get(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct catpt_dev *cdev = dev_get_drvdata(component->dev); struct catpt_stream_runtime *stream; + struct catpt_dev *cdev = dev_get_drvdata(component->dev); long *ctlvol = (long *)kcontrol->private_value; u32 dspvol; int i; @@ -925,8 +925,8 @@ static int catpt_stream_volume_put(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct catpt_dev *cdev = dev_get_drvdata(component->dev); struct catpt_stream_runtime *stream; + struct catpt_dev *cdev = dev_get_drvdata(component->dev); long *ctlvol = (long *)kcontrol->private_value; int ret, i; @@ -1001,8 +1001,8 @@ static int catpt_loopback_switch_put(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct catpt_dev *cdev = dev_get_drvdata(component->dev); struct catpt_stream_runtime *stream; + struct catpt_dev *cdev = dev_get_drvdata(component->dev); bool mute; int ret; -- cgit v1.2.3 From f04b4fb47d83b110a5b007fb2eddea862cfeb151 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 17 Dec 2021 10:22:31 +0100 Subject: ASoC: sh: rz-ssi: Check return value of pm_runtime_resume_and_get() The return value of pm_runtime_resume_and_get() needs to be checked to avoid a usage count imbalance in the error case. This fix is basically the same as 92c959bae2e5 ("reset: renesas: Fix Runtime PM usage"), and the last step before pm_runtime_resume_and_get() can be annotated as __must_check. Signed-off-by: Heiner Kallweit Link: https://lore.kernel.org/r/9fed506d-b780-55cd-45a4-9bd2407c910f@gmail.com Signed-off-by: Mark Brown --- sound/soc/sh/rz-ssi.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/soc/sh/rz-ssi.c b/sound/soc/sh/rz-ssi.c index fa0cc08f70ec..e8d98b362f9d 100644 --- a/sound/soc/sh/rz-ssi.c +++ b/sound/soc/sh/rz-ssi.c @@ -1020,7 +1020,12 @@ static int rz_ssi_probe(struct platform_device *pdev) reset_control_deassert(ssi->rstc); pm_runtime_enable(&pdev->dev); - pm_runtime_resume_and_get(&pdev->dev); + ret = pm_runtime_resume_and_get(&pdev->dev); + if (ret < 0) { + pm_runtime_disable(ssi->dev); + reset_control_assert(ssi->rstc); + return dev_err_probe(ssi->dev, ret, "pm_runtime_resume_and_get failed\n"); + } spin_lock_init(&ssi->lock); dev_set_drvdata(&pdev->dev, ssi); -- cgit v1.2.3 From 30e693ee82d20361f2caacca3b68c79e1a7cb16c Mon Sep 17 00:00:00 2001 From: Trevor Wu Date: Thu, 16 Dec 2021 10:24:24 +0800 Subject: ASoC: mediatek: mt8195: correct default value mt8195_cg_patch is used to reset the default value of audio cg, so the register value could be consistent with CCF reference count. Nevertheless, AUDIO_TOP_CON1[1:0] is used to control an internal mux, and it's expected to keep the default value 0. This patch corrects the default value in case an unexpected behavior happens in the future. Fixes: 6746cc8582599 ("ASoC: mediatek: mt8195: add platform driver") Signed-off-by: Trevor Wu Link: https://lore.kernel.org/r/20211216022424.28470-1-trevor.wu@mediatek.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8195/mt8195-afe-pcm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c index 2bb05a828e8d..15b4cae2524c 100644 --- a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c +++ b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c @@ -3028,7 +3028,7 @@ static const struct reg_sequence mt8195_afe_reg_defaults[] = { static const struct reg_sequence mt8195_cg_patch[] = { { AUDIO_TOP_CON0, 0xfffffffb }, - { AUDIO_TOP_CON1, 0xfffffffa }, + { AUDIO_TOP_CON1, 0xfffffff8 }, }; static int mt8195_afe_init_registers(struct mtk_base_afe *afe) -- cgit v1.2.3 From 5ea4e76b73cd6f2cf29b02a57040c1a11fa8c3f0 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 14 Dec 2021 11:08:22 +0900 Subject: ASoC: codecs: ak4118: Use dev_err_probe() helper Use the dev_err_probe() helper, instead of open-coding the same operation. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/20211214020843.2225831-2-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/codecs/ak4118.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/sound/soc/codecs/ak4118.c b/sound/soc/codecs/ak4118.c index e0a6451851e8..2e6bafd2a821 100644 --- a/sound/soc/codecs/ak4118.c +++ b/sound/soc/codecs/ak4118.c @@ -374,20 +374,14 @@ static int ak4118_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, ak4118); ak4118->reset = devm_gpiod_get(&i2c->dev, "reset", GPIOD_OUT_HIGH); - if (IS_ERR(ak4118->reset)) { - ret = PTR_ERR(ak4118->reset); - if (ret != -EPROBE_DEFER) - dev_err(&i2c->dev, "Failed to get reset: %d\n", ret); - return ret; - } + if (IS_ERR(ak4118->reset)) + return dev_err_probe(&i2c->dev, PTR_ERR(ak4118->reset), + "Failed to get reset\n"); ak4118->irq = devm_gpiod_get(&i2c->dev, "irq", GPIOD_IN); - if (IS_ERR(ak4118->irq)) { - ret = PTR_ERR(ak4118->irq); - if (ret != -EPROBE_DEFER) - dev_err(&i2c->dev, "Failed to get IRQ: %d\n", ret); - return ret; - } + if (IS_ERR(ak4118->irq)) + return dev_err_probe(&i2c->dev, PTR_ERR(ak4118->irq), + "Failed to get IRQ\n"); ret = devm_request_threaded_irq(&i2c->dev, gpiod_to_irq(ak4118->irq), NULL, ak4118_irq_handler, -- cgit v1.2.3 From 900b4b911aca2270ae3f966df5f31081a086c3cf Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 14 Dec 2021 11:08:23 +0900 Subject: ASoC: codecs: es7241: Use dev_err_probe() helper Use the dev_err_probe() helper, instead of open-coding the same operation. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/20211214020843.2225831-3-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/codecs/es7241.c | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/sound/soc/codecs/es7241.c b/sound/soc/codecs/es7241.c index 2344a0b03518..9f20bfb855b3 100644 --- a/sound/soc/codecs/es7241.c +++ b/sound/soc/codecs/es7241.c @@ -255,7 +255,6 @@ static int es7241_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct es7241_data *priv; - int err; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -271,28 +270,19 @@ static int es7241_probe(struct platform_device *pdev) es7241_parse_fmt(dev, priv); priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(priv->reset)) { - err = PTR_ERR(priv->reset); - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get 'reset' gpio: %d", err); - return err; - } + if (IS_ERR(priv->reset)) + return dev_err_probe(dev, PTR_ERR(priv->reset), + "Failed to get 'reset' gpio"); priv->m0 = devm_gpiod_get_optional(dev, "m0", GPIOD_OUT_LOW); - if (IS_ERR(priv->m0)) { - err = PTR_ERR(priv->m0); - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get 'm0' gpio: %d", err); - return err; - } + if (IS_ERR(priv->m0)) + return dev_err_probe(dev, PTR_ERR(priv->m0), + "Failed to get 'm0' gpio"); priv->m1 = devm_gpiod_get_optional(dev, "m1", GPIOD_OUT_LOW); - if (IS_ERR(priv->m1)) { - err = PTR_ERR(priv->m1); - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get 'm1' gpio: %d", err); - return err; - } + if (IS_ERR(priv->m1)) + return dev_err_probe(dev, PTR_ERR(priv->m1), + "Failed to get 'm1' gpio"); return devm_snd_soc_register_component(&pdev->dev, &es7241_component_driver, -- cgit v1.2.3 From 6df96c8f5b50574c196607f036a09b5626124a24 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 14 Dec 2021 11:08:24 +0900 Subject: ASoC: codecs: max9759: Use dev_err_probe() helper Use the dev_err_probe() helper, instead of open-coding the same operation. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/20211214020843.2225831-4-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/codecs/max9759.c | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/sound/soc/codecs/max9759.c b/sound/soc/codecs/max9759.c index 00e9d4fd1651..d75fd61b9032 100644 --- a/sound/soc/codecs/max9759.c +++ b/sound/soc/codecs/max9759.c @@ -140,7 +140,6 @@ static int max9759_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct max9759 *priv; - int err; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -149,29 +148,20 @@ static int max9759_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); priv->gpiod_shutdown = devm_gpiod_get(dev, "shutdown", GPIOD_OUT_HIGH); - if (IS_ERR(priv->gpiod_shutdown)) { - err = PTR_ERR(priv->gpiod_shutdown); - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get 'shutdown' gpio: %d", err); - return err; - } + if (IS_ERR(priv->gpiod_shutdown)) + return dev_err_probe(dev, PTR_ERR(priv->gpiod_shutdown), + "Failed to get 'shutdown' gpio"); priv->gpiod_mute = devm_gpiod_get(dev, "mute", GPIOD_OUT_HIGH); - if (IS_ERR(priv->gpiod_mute)) { - err = PTR_ERR(priv->gpiod_mute); - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get 'mute' gpio: %d", err); - return err; - } + if (IS_ERR(priv->gpiod_mute)) + return dev_err_probe(dev, PTR_ERR(priv->gpiod_mute), + "Failed to get 'mute' gpio"); priv->is_mute = true; priv->gpiod_gain = devm_gpiod_get_array(dev, "gain", GPIOD_OUT_HIGH); - if (IS_ERR(priv->gpiod_gain)) { - err = PTR_ERR(priv->gpiod_gain); - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get 'gain' gpios: %d", err); - return err; - } + if (IS_ERR(priv->gpiod_gain)) + return dev_err_probe(dev, PTR_ERR(priv->gpiod_gain), + "Failed to get 'gain' gpios"); priv->gain = 0; if (priv->gpiod_gain->ndescs != 2) { -- cgit v1.2.3 From edfe9f451a8c6174fad43689fb5af5c096940e13 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 14 Dec 2021 11:08:25 +0900 Subject: ASoC: codecs: max9860: Use dev_err_probe() helper Use the dev_err_probe() helper, instead of open-coding the same operation. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/20211214020843.2225831-5-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/codecs/max9860.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/sound/soc/codecs/max9860.c b/sound/soc/codecs/max9860.c index dd29b183ecd6..7c9686be59d9 100644 --- a/sound/soc/codecs/max9860.c +++ b/sound/soc/codecs/max9860.c @@ -606,12 +606,9 @@ static int max9860_probe(struct i2c_client *i2c) return -ENOMEM; max9860->dvddio = devm_regulator_get(dev, "DVDDIO"); - if (IS_ERR(max9860->dvddio)) { - ret = PTR_ERR(max9860->dvddio); - if (ret != -EPROBE_DEFER) - dev_err(dev, "Failed to get DVDDIO supply: %d\n", ret); - return ret; - } + if (IS_ERR(max9860->dvddio)) + return dev_err_probe(dev, PTR_ERR(max9860->dvddio), + "Failed to get DVDDIO supply\n"); max9860->dvddio_nb.notifier_call = max9860_dvddio_event; @@ -643,8 +640,7 @@ static int max9860_probe(struct i2c_client *i2c) if (IS_ERR(mclk)) { ret = PTR_ERR(mclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "Failed to get MCLK: %d\n", ret); + dev_err_probe(dev, ret, "Failed to get MCLK\n"); goto err_regulator; } -- cgit v1.2.3 From 526f6ca95a9d0c85ccb0a83ed48936394e4185e6 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 14 Dec 2021 11:08:26 +0900 Subject: ASoC: codecs: pcm3168a: Use dev_err_probe() helper Use the dev_err_probe() helper, instead of open-coding the same operation. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/20211214020843.2225831-6-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/codecs/pcm3168a.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c index b6fd412441a1..fdf92c8b28e1 100644 --- a/sound/soc/codecs/pcm3168a.c +++ b/sound/soc/codecs/pcm3168a.c @@ -751,21 +751,14 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap) pcm3168a->gpio_rst = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE); - if (IS_ERR(pcm3168a->gpio_rst)) { - ret = PTR_ERR(pcm3168a->gpio_rst); - if (ret != -EPROBE_DEFER ) - dev_err(dev, "failed to acquire RST gpio: %d\n", ret); - - return ret; - } + if (IS_ERR(pcm3168a->gpio_rst)) + return dev_err_probe(dev, PTR_ERR(pcm3168a->gpio_rst), + "failed to acquire RST gpio\n"); pcm3168a->scki = devm_clk_get(dev, "scki"); - if (IS_ERR(pcm3168a->scki)) { - ret = PTR_ERR(pcm3168a->scki); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to acquire clock 'scki': %d\n", ret); - return ret; - } + if (IS_ERR(pcm3168a->scki)) + return dev_err_probe(dev, PTR_ERR(pcm3168a->scki), + "failed to acquire clock 'scki'\n"); ret = clk_prepare_enable(pcm3168a->scki); if (ret) { @@ -781,8 +774,7 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap) ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(pcm3168a->supplies), pcm3168a->supplies); if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to request supplies: %d\n", ret); + dev_err_probe(dev, ret, "failed to request supplies\n"); goto err_clk; } -- cgit v1.2.3 From ec1e0e72a8d4180c65aee01e9563ddfb47f87709 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 14 Dec 2021 11:08:27 +0900 Subject: ASoC: codecs: sgtl5000: Use dev_err_probe() helper Use the dev_err_probe() helper, instead of open-coding the same operation. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/20211214020843.2225831-7-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/codecs/sgtl5000.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 97bf1f222805..8eebf27d0ea2 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -1612,9 +1612,8 @@ static int sgtl5000_i2c_probe(struct i2c_client *client, if (ret == -ENOENT) ret = -EPROBE_DEFER; - if (ret != -EPROBE_DEFER) - dev_err(&client->dev, "Failed to get mclock: %d\n", - ret); + dev_err_probe(&client->dev, ret, "Failed to get mclock\n"); + goto disable_regs; } -- cgit v1.2.3 From 2c16636a8bbd85573376363420c8e9f6006d3753 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 14 Dec 2021 11:08:28 +0900 Subject: ASoC: codecs: simple-amplifier: Use dev_err_probe() helper Use the dev_err_probe() helper, instead of open-coding the same operation. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/20211214020843.2225831-8-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/codecs/simple-amplifier.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/simple-amplifier.c b/sound/soc/codecs/simple-amplifier.c index b30fc1f894e1..d306c585b52b 100644 --- a/sound/soc/codecs/simple-amplifier.c +++ b/sound/soc/codecs/simple-amplifier.c @@ -69,7 +69,6 @@ static int simple_amp_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct simple_amp *priv; - int err; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (priv == NULL) @@ -78,12 +77,9 @@ static int simple_amp_probe(struct platform_device *pdev) priv->gpiod_enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW); - if (IS_ERR(priv->gpiod_enable)) { - err = PTR_ERR(priv->gpiod_enable); - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get 'enable' gpio: %d", err); - return err; - } + if (IS_ERR(priv->gpiod_enable)) + return dev_err_probe(dev, PTR_ERR(priv->gpiod_enable), + "Failed to get 'enable' gpio"); return devm_snd_soc_register_component(dev, &simple_amp_component_driver, -- cgit v1.2.3 From 17d7044715c5b1e0321f8e56060260e39bba54b7 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 14 Dec 2021 11:08:29 +0900 Subject: ASoC: codecs: simple-mux: Use dev_err_probe() helper Use the dev_err_probe() helper, instead of open-coding the same operation. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/20211214020843.2225831-9-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/codecs/simple-mux.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/simple-mux.c b/sound/soc/codecs/simple-mux.c index e0a09dadfa7c..d30c0d24d90a 100644 --- a/sound/soc/codecs/simple-mux.c +++ b/sound/soc/codecs/simple-mux.c @@ -82,7 +82,6 @@ static int simple_mux_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct simple_mux *priv; - int err; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -91,12 +90,9 @@ static int simple_mux_probe(struct platform_device *pdev) dev_set_drvdata(dev, priv); priv->gpiod_mux = devm_gpiod_get(dev, "mux", GPIOD_OUT_LOW); - if (IS_ERR(priv->gpiod_mux)) { - err = PTR_ERR(priv->gpiod_mux); - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get 'mux' gpio: %d", err); - return err; - } + if (IS_ERR(priv->gpiod_mux)) + return dev_err_probe(dev, PTR_ERR(priv->gpiod_mux), + "Failed to get 'mux' gpio"); return devm_snd_soc_register_component(dev, &simple_mux_component_driver, NULL, 0); } -- cgit v1.2.3 From 382ae995597fbe214596f794ee5a38b4b64195be Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 14 Dec 2021 11:08:30 +0900 Subject: ASoC: codecs: ssm2305: Use dev_err_probe() helper Use the dev_err_probe() helper, instead of open-coding the same operation. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/20211214020843.2225831-10-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/codecs/ssm2305.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/sound/soc/codecs/ssm2305.c b/sound/soc/codecs/ssm2305.c index 2968959c4b75..1d022643c307 100644 --- a/sound/soc/codecs/ssm2305.c +++ b/sound/soc/codecs/ssm2305.c @@ -57,7 +57,6 @@ static int ssm2305_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct ssm2305 *priv; - int err; /* Allocate the private data */ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -69,13 +68,9 @@ static int ssm2305_probe(struct platform_device *pdev) /* Get shutdown gpio */ priv->gpiod_shutdown = devm_gpiod_get(dev, "shutdown", GPIOD_OUT_LOW); - if (IS_ERR(priv->gpiod_shutdown)) { - err = PTR_ERR(priv->gpiod_shutdown); - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get 'shutdown' gpio: %d\n", - err); - return err; - } + if (IS_ERR(priv->gpiod_shutdown)) + return dev_err_probe(dev, PTR_ERR(priv->gpiod_shutdown), + "Failed to get 'shutdown' gpio\n"); return devm_snd_soc_register_component(dev, &ssm2305_component_driver, NULL, 0); -- cgit v1.2.3 From 7ff27faec8cccbbf499d0b4cd8ef951f1d5f5d05 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 14 Dec 2021 11:08:31 +0900 Subject: ASoC: codecs: tlv320aic31xx: Use dev_err_probe() helper Use the dev_err_probe() helper, instead of open-coding the same operation. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/20211214020843.2225831-11-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic31xx.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index 4224b4b3cae6..e77342aff46d 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -1680,11 +1680,9 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c, aic31xx->gpio_reset = devm_gpiod_get_optional(aic31xx->dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(aic31xx->gpio_reset)) { - if (PTR_ERR(aic31xx->gpio_reset) != -EPROBE_DEFER) - dev_err(aic31xx->dev, "not able to acquire gpio\n"); - return PTR_ERR(aic31xx->gpio_reset); - } + if (IS_ERR(aic31xx->gpio_reset)) + return dev_err_probe(aic31xx->dev, PTR_ERR(aic31xx->gpio_reset), + "not able to acquire gpio\n"); for (i = 0; i < ARRAY_SIZE(aic31xx->supplies); i++) aic31xx->supplies[i].supply = aic31xx_supply_names[i]; @@ -1692,12 +1690,8 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c, ret = devm_regulator_bulk_get(aic31xx->dev, ARRAY_SIZE(aic31xx->supplies), aic31xx->supplies); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(aic31xx->dev, - "Failed to request supplies: %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(aic31xx->dev, ret, "Failed to request supplies\n"); aic31xx_configure_ocmv(aic31xx); -- cgit v1.2.3 From 0624dafa6a85be94e98822075c08006b5b528e2d Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 14 Dec 2021 11:08:32 +0900 Subject: ASoC: ateml: Use dev_err_probe() helper Use the dev_err_probe() helper, instead of open-coding the same operation. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/20211214020843.2225831-12-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/atmel/mikroe-proto.c | 6 +++--- sound/soc/atmel/tse850-pcm5142.c | 32 ++++++++++++-------------------- 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/sound/soc/atmel/mikroe-proto.c b/sound/soc/atmel/mikroe-proto.c index f9331f7e80fe..627564c18c27 100644 --- a/sound/soc/atmel/mikroe-proto.c +++ b/sound/soc/atmel/mikroe-proto.c @@ -144,9 +144,9 @@ static int snd_proto_probe(struct platform_device *pdev) of_node_put(cpu_np); ret = snd_soc_register_card(&snd_proto); - if (ret && ret != -EPROBE_DEFER) - dev_err(&pdev->dev, - "snd_soc_register_card() failed: %d\n", ret); + if (ret) + dev_err_probe(&pdev->dev, ret, + "snd_soc_register_card() failed\n"); return ret; } diff --git a/sound/soc/atmel/tse850-pcm5142.c b/sound/soc/atmel/tse850-pcm5142.c index 1b3a31296c9b..ef537de7719c 100644 --- a/sound/soc/atmel/tse850-pcm5142.c +++ b/sound/soc/atmel/tse850-pcm5142.c @@ -371,35 +371,27 @@ static int tse850_probe(struct platform_device *pdev) } tse850->add = devm_gpiod_get(dev, "axentia,add", GPIOD_OUT_HIGH); - if (IS_ERR(tse850->add)) { - if (PTR_ERR(tse850->add) != -EPROBE_DEFER) - dev_err(dev, "failed to get 'add' gpio\n"); - return PTR_ERR(tse850->add); - } + if (IS_ERR(tse850->add)) + return dev_err_probe(dev, PTR_ERR(tse850->add), + "failed to get 'add' gpio\n"); tse850->add_cache = 1; tse850->loop1 = devm_gpiod_get(dev, "axentia,loop1", GPIOD_OUT_HIGH); - if (IS_ERR(tse850->loop1)) { - if (PTR_ERR(tse850->loop1) != -EPROBE_DEFER) - dev_err(dev, "failed to get 'loop1' gpio\n"); - return PTR_ERR(tse850->loop1); - } + if (IS_ERR(tse850->loop1)) + return dev_err_probe(dev, PTR_ERR(tse850->loop1), + "failed to get 'loop1' gpio\n"); tse850->loop1_cache = 1; tse850->loop2 = devm_gpiod_get(dev, "axentia,loop2", GPIOD_OUT_HIGH); - if (IS_ERR(tse850->loop2)) { - if (PTR_ERR(tse850->loop2) != -EPROBE_DEFER) - dev_err(dev, "failed to get 'loop2' gpio\n"); - return PTR_ERR(tse850->loop2); - } + if (IS_ERR(tse850->loop2)) + return dev_err_probe(dev, PTR_ERR(tse850->loop2), + "failed to get 'loop2' gpio\n"); tse850->loop2_cache = 1; tse850->ana = devm_regulator_get(dev, "axentia,ana"); - if (IS_ERR(tse850->ana)) { - if (PTR_ERR(tse850->ana) != -EPROBE_DEFER) - dev_err(dev, "failed to get 'ana' regulator\n"); - return PTR_ERR(tse850->ana); - } + if (IS_ERR(tse850->ana)) + return dev_err_probe(dev, PTR_ERR(tse850->ana), + "failed to get 'ana' regulator\n"); ret = regulator_enable(tse850->ana); if (ret < 0) { -- cgit v1.2.3 From 88fb6da3f4313feb885f432cfc3051b33fdb2df7 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 14 Dec 2021 11:08:33 +0900 Subject: ASoC: ti: Use dev_err_probe() helper Use the dev_err_probe() helper, instead of open-coding the same operation. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/20211214020843.2225831-13-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/ti/davinci-mcasp.c | 10 +++------- sound/soc/ti/j721e-evm.c | 10 +++------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index 3e105caac95e..2c146b91fca3 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -2024,13 +2024,9 @@ static int davinci_mcasp_get_dma_type(struct davinci_mcasp *mcasp) tmp = mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data; chan = dma_request_chan(mcasp->dev, tmp); - if (IS_ERR(chan)) { - if (PTR_ERR(chan) != -EPROBE_DEFER) - dev_err(mcasp->dev, - "Can't verify DMA configuration (%ld)\n", - PTR_ERR(chan)); - return PTR_ERR(chan); - } + if (IS_ERR(chan)) + return dev_err_probe(mcasp->dev, PTR_ERR(chan), + "Can't verify DMA configuration\n"); if (WARN_ON(!chan->device || !chan->device->dev)) { dma_release_channel(chan); return -EINVAL; diff --git a/sound/soc/ti/j721e-evm.c b/sound/soc/ti/j721e-evm.c index 9347f982c3e1..4077e15ec48b 100644 --- a/sound/soc/ti/j721e-evm.c +++ b/sound/soc/ti/j721e-evm.c @@ -464,13 +464,9 @@ static int j721e_get_clocks(struct device *dev, int ret; clocks->target = devm_clk_get(dev, prefix); - if (IS_ERR(clocks->target)) { - ret = PTR_ERR(clocks->target); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to acquire %s: %d\n", - prefix, ret); - return ret; - } + if (IS_ERR(clocks->target)) + return dev_err_probe(dev, PTR_ERR(clocks->target), + "failed to acquire %s\n", prefix); clk_name = kasprintf(GFP_KERNEL, "%s-48000", prefix); if (clk_name) { -- cgit v1.2.3 From 2e6f557ca35aa330dbf31c5e1cc8119eff1526fa Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 14 Dec 2021 11:08:34 +0900 Subject: ASoC: fsl: Use dev_err_probe() helper Use the dev_err_probe() helper, instead of open-coding the same operation. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/20211214020843.2225831-14-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl-asoc-card.c | 3 +-- sound/soc/fsl/imx-card.c | 17 ++++++----------- sound/soc/fsl/imx-sgtl5000.c | 4 +--- sound/soc/fsl/imx-spdif.c | 4 ++-- 4 files changed, 10 insertions(+), 18 deletions(-) diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 90cbed496f98..5ee945505281 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -853,8 +853,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(&pdev->dev, &priv->card); if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n"); goto asrc_fail; } diff --git a/sound/soc/fsl/imx-card.c b/sound/soc/fsl/imx-card.c index 6f06afd23b16..e758c4f1b0bc 100644 --- a/sound/soc/fsl/imx-card.c +++ b/sound/soc/fsl/imx-card.c @@ -563,9 +563,8 @@ static int imx_card_parse_of(struct imx_card_data *data) ret = snd_soc_of_get_dai_name(cpu, &link->cpus->dai_name); if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(card->dev, "%s: error getting cpu dai name: %d\n", - link->name, ret); + dev_err_probe(card->dev, ret, + "%s: error getting cpu dai name\n", link->name); goto err; } @@ -573,9 +572,8 @@ static int imx_card_parse_of(struct imx_card_data *data) if (codec) { ret = snd_soc_of_get_dai_link_codecs(dev, codec, link); if (ret < 0) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "%s: codec dai not found: %d\n", - link->name, ret); + dev_err_probe(dev, ret, "%s: codec dai not found\n", + link->name); goto err; } @@ -814,11 +812,8 @@ static int imx_card_probe(struct platform_device *pdev) } ret = devm_snd_soc_register_card(&pdev->dev, &data->card); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n"); return 0; } diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c index 2f1acd011042..8daced42d55e 100644 --- a/sound/soc/fsl/imx-sgtl5000.c +++ b/sound/soc/fsl/imx-sgtl5000.c @@ -173,9 +173,7 @@ static int imx_sgtl5000_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(&pdev->dev, &data->card); if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", - ret); + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n"); goto fail; } diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c index 6c4dadf60355..4446fba755b9 100644 --- a/sound/soc/fsl/imx-spdif.c +++ b/sound/soc/fsl/imx-spdif.c @@ -70,8 +70,8 @@ static int imx_spdif_audio_probe(struct platform_device *pdev) goto end; ret = devm_snd_soc_register_card(&pdev->dev, &data->card); - if (ret && ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "snd_soc_register_card failed: %d\n", ret); + if (ret) + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n"); end: of_node_put(spdif_np); -- cgit v1.2.3 From 7a0299e13bc740caebbbba24b3df85fc9ffa7759 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 14 Dec 2021 11:08:35 +0900 Subject: ASoC: generic: Use dev_err_probe() helper Use the dev_err_probe() helper, instead of open-coding the same operation. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/20211214020843.2225831-15-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-card.c | 5 +---- sound/soc/generic/audio-graph-card2.c | 4 ++-- sound/soc/generic/simple-card.c | 3 +-- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 7eb027238327..2b598af8feef 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -593,10 +593,7 @@ int audio_graph_parse_of(struct asoc_simple_priv *priv, struct device *dev) err: asoc_simple_clean_reference(card); - if (ret != -EPROBE_DEFER) - dev_err(dev, "parse error %d\n", ret); - - return ret; + return dev_err_probe(dev, ret, "parse error\n"); } EXPORT_SYMBOL_GPL(audio_graph_parse_of); diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index b6049bcfb771..c3947347dda3 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -1238,8 +1238,8 @@ int audio_graph2_parse_of(struct asoc_simple_priv *priv, struct device *dev, err: devm_kfree(dev, li); - if ((ret < 0) && (ret != -EPROBE_DEFER)) - dev_err(dev, "parse error %d\n", ret); + if (ret < 0) + dev_err_probe(dev, ret, "parse error\n"); return ret; } diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index a3a7990b5cb6..a89d1cfdda32 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -642,8 +642,7 @@ static int asoc_simple_probe(struct platform_device *pdev) ret = simple_parse_of(priv, li); if (ret < 0) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "parse error %d\n", ret); + dev_err_probe(dev, ret, "parse error\n"); goto err; } -- cgit v1.2.3 From ef12f373f21d66e9d14eeace517c05fc2c9cf258 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 14 Dec 2021 11:08:36 +0900 Subject: ASoC: img: Use dev_err_probe() helper Use the dev_err_probe() helper, instead of open-coding the same operation. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/20211214020843.2225831-16-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/img/img-i2s-in.c | 8 +++----- sound/soc/img/img-i2s-out.c | 24 +++++++++--------------- sound/soc/img/img-parallel-out.c | 24 +++++++++--------------- sound/soc/img/img-spdif-in.c | 8 +++----- sound/soc/img/img-spdif-out.c | 24 +++++++++--------------- sound/soc/img/pistachio-internal-dac.c | 9 +++------ 6 files changed, 36 insertions(+), 61 deletions(-) diff --git a/sound/soc/img/img-i2s-in.c b/sound/soc/img/img-i2s-in.c index 1bf5d6edbd32..f1f36f15a503 100644 --- a/sound/soc/img/img-i2s-in.c +++ b/sound/soc/img/img-i2s-in.c @@ -451,11 +451,9 @@ static int img_i2s_in_probe(struct platform_device *pdev) i2s->channel_base = base + (max_i2s_chan_pow_2 * 0x20); i2s->clk_sys = devm_clk_get(dev, "sys"); - if (IS_ERR(i2s->clk_sys)) { - if (PTR_ERR(i2s->clk_sys) != -EPROBE_DEFER) - dev_err(dev, "Failed to acquire clock 'sys'\n"); - return PTR_ERR(i2s->clk_sys); - } + if (IS_ERR(i2s->clk_sys)) + return dev_err_probe(dev, PTR_ERR(i2s->clk_sys), + "Failed to acquire clock 'sys'\n"); pm_runtime_enable(&pdev->dev); if (!pm_runtime_enabled(&pdev->dev)) { diff --git a/sound/soc/img/img-i2s-out.c b/sound/soc/img/img-i2s-out.c index 4f90d36dc7df..28f48ca1508a 100644 --- a/sound/soc/img/img-i2s-out.c +++ b/sound/soc/img/img-i2s-out.c @@ -457,25 +457,19 @@ static int img_i2s_out_probe(struct platform_device *pdev) i2s->channel_base = base + (max_i2s_chan_pow_2 * 0x20); i2s->rst = devm_reset_control_get_exclusive(&pdev->dev, "rst"); - if (IS_ERR(i2s->rst)) { - if (PTR_ERR(i2s->rst) != -EPROBE_DEFER) - dev_err(&pdev->dev, "No top level reset found\n"); - return PTR_ERR(i2s->rst); - } + if (IS_ERR(i2s->rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->rst), + "No top level reset found\n"); i2s->clk_sys = devm_clk_get(&pdev->dev, "sys"); - if (IS_ERR(i2s->clk_sys)) { - if (PTR_ERR(i2s->clk_sys) != -EPROBE_DEFER) - dev_err(dev, "Failed to acquire clock 'sys'\n"); - return PTR_ERR(i2s->clk_sys); - } + if (IS_ERR(i2s->clk_sys)) + return dev_err_probe(dev, PTR_ERR(i2s->clk_sys), + "Failed to acquire clock 'sys'\n"); i2s->clk_ref = devm_clk_get(&pdev->dev, "ref"); - if (IS_ERR(i2s->clk_ref)) { - if (PTR_ERR(i2s->clk_ref) != -EPROBE_DEFER) - dev_err(dev, "Failed to acquire clock 'ref'\n"); - return PTR_ERR(i2s->clk_ref); - } + if (IS_ERR(i2s->clk_ref)) + return dev_err_probe(dev, PTR_ERR(i2s->clk_ref), + "Failed to acquire clock 'ref'\n"); i2s->suspend_ch_ctl = devm_kcalloc(dev, i2s->max_i2s_chan, sizeof(*i2s->suspend_ch_ctl), GFP_KERNEL); diff --git a/sound/soc/img/img-parallel-out.c b/sound/soc/img/img-parallel-out.c index ce0f08d3777c..800f247283cd 100644 --- a/sound/soc/img/img-parallel-out.c +++ b/sound/soc/img/img-parallel-out.c @@ -229,25 +229,19 @@ static int img_prl_out_probe(struct platform_device *pdev) prl->base = base; prl->rst = devm_reset_control_get_exclusive(&pdev->dev, "rst"); - if (IS_ERR(prl->rst)) { - if (PTR_ERR(prl->rst) != -EPROBE_DEFER) - dev_err(&pdev->dev, "No top level reset found\n"); - return PTR_ERR(prl->rst); - } + if (IS_ERR(prl->rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(prl->rst), + "No top level reset found\n"); prl->clk_sys = devm_clk_get(&pdev->dev, "sys"); - if (IS_ERR(prl->clk_sys)) { - if (PTR_ERR(prl->clk_sys) != -EPROBE_DEFER) - dev_err(dev, "Failed to acquire clock 'sys'\n"); - return PTR_ERR(prl->clk_sys); - } + if (IS_ERR(prl->clk_sys)) + return dev_err_probe(dev, PTR_ERR(prl->clk_sys), + "Failed to acquire clock 'sys'\n"); prl->clk_ref = devm_clk_get(&pdev->dev, "ref"); - if (IS_ERR(prl->clk_ref)) { - if (PTR_ERR(prl->clk_ref) != -EPROBE_DEFER) - dev_err(dev, "Failed to acquire clock 'ref'\n"); - return PTR_ERR(prl->clk_ref); - } + if (IS_ERR(prl->clk_ref)) + return dev_err_probe(dev, PTR_ERR(prl->clk_ref), + "Failed to acquire clock 'ref'\n"); ret = clk_prepare_enable(prl->clk_sys); if (ret) diff --git a/sound/soc/img/img-spdif-in.c b/sound/soc/img/img-spdif-in.c index 6364eb742f6d..95914d0612fe 100644 --- a/sound/soc/img/img-spdif-in.c +++ b/sound/soc/img/img-spdif-in.c @@ -739,11 +739,9 @@ static int img_spdif_in_probe(struct platform_device *pdev) spdif->base = base; spdif->clk_sys = devm_clk_get(dev, "sys"); - if (IS_ERR(spdif->clk_sys)) { - if (PTR_ERR(spdif->clk_sys) != -EPROBE_DEFER) - dev_err(dev, "Failed to acquire clock 'sys'\n"); - return PTR_ERR(spdif->clk_sys); - } + if (IS_ERR(spdif->clk_sys)) + return dev_err_probe(dev, PTR_ERR(spdif->clk_sys), + "Failed to acquire clock 'sys'\n"); pm_runtime_enable(&pdev->dev); if (!pm_runtime_enabled(&pdev->dev)) { diff --git a/sound/soc/img/img-spdif-out.c b/sound/soc/img/img-spdif-out.c index 858e1b853820..c3189d9ff72f 100644 --- a/sound/soc/img/img-spdif-out.c +++ b/sound/soc/img/img-spdif-out.c @@ -342,25 +342,19 @@ static int img_spdif_out_probe(struct platform_device *pdev) spdif->base = base; spdif->rst = devm_reset_control_get_exclusive(&pdev->dev, "rst"); - if (IS_ERR(spdif->rst)) { - if (PTR_ERR(spdif->rst) != -EPROBE_DEFER) - dev_err(&pdev->dev, "No top level reset found\n"); - return PTR_ERR(spdif->rst); - } + if (IS_ERR(spdif->rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(spdif->rst), + "No top level reset found\n"); spdif->clk_sys = devm_clk_get(&pdev->dev, "sys"); - if (IS_ERR(spdif->clk_sys)) { - if (PTR_ERR(spdif->clk_sys) != -EPROBE_DEFER) - dev_err(dev, "Failed to acquire clock 'sys'\n"); - return PTR_ERR(spdif->clk_sys); - } + if (IS_ERR(spdif->clk_sys)) + return dev_err_probe(dev, PTR_ERR(spdif->clk_sys), + "Failed to acquire clock 'sys'\n"); spdif->clk_ref = devm_clk_get(&pdev->dev, "ref"); - if (IS_ERR(spdif->clk_ref)) { - if (PTR_ERR(spdif->clk_ref) != -EPROBE_DEFER) - dev_err(dev, "Failed to acquire clock 'ref'\n"); - return PTR_ERR(spdif->clk_ref); - } + if (IS_ERR(spdif->clk_ref)) + return dev_err_probe(dev, PTR_ERR(spdif->clk_ref), + "Failed to acquire clock 'ref'\n"); pm_runtime_enable(&pdev->dev); if (!pm_runtime_enabled(&pdev->dev)) { diff --git a/sound/soc/img/pistachio-internal-dac.c b/sound/soc/img/pistachio-internal-dac.c index fe181c2e51d6..802c0ee63aa2 100644 --- a/sound/soc/img/pistachio-internal-dac.c +++ b/sound/soc/img/pistachio-internal-dac.c @@ -161,12 +161,9 @@ static int pistachio_internal_dac_probe(struct platform_device *pdev) return PTR_ERR(dac->regmap); dac->supply = devm_regulator_get(dev, "VDD"); - if (IS_ERR(dac->supply)) { - ret = PTR_ERR(dac->supply); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to acquire supply 'VDD-supply': %d\n", ret); - return ret; - } + if (IS_ERR(dac->supply)) + return dev_err_probe(dev, PTR_ERR(dac->supply), + "failed to acquire supply 'VDD-supply'\n"); ret = regulator_enable(dac->supply); if (ret) { -- cgit v1.2.3 From 2ff4e003e8e105fb65c682c876a5cb0e00f854bf Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 14 Dec 2021 11:08:37 +0900 Subject: ASoC: meson: Use dev_err_probe() helper Use the dev_err_probe() helper, instead of open-coding the same operation. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/20211214020843.2225831-17-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/meson/aiu.c | 36 ++++++++------------------ sound/soc/meson/axg-fifo.c | 16 +++--------- sound/soc/meson/axg-pdm.c | 25 +++++-------------- sound/soc/meson/axg-spdifin.c | 17 +++---------- sound/soc/meson/axg-spdifout.c | 17 +++---------- sound/soc/meson/axg-tdm-formatter.c | 50 ++++++++++--------------------------- sound/soc/meson/axg-tdm-interface.c | 25 ++++++------------- sound/soc/meson/meson-card-utils.c | 8 +++--- sound/soc/meson/t9015.c | 14 +++-------- 9 files changed, 56 insertions(+), 152 deletions(-) diff --git a/sound/soc/meson/aiu.c b/sound/soc/meson/aiu.c index ba15d5762b0b..d299a70db7e5 100644 --- a/sound/soc/meson/aiu.c +++ b/sound/soc/meson/aiu.c @@ -218,34 +218,23 @@ static int aiu_clk_get(struct device *dev) int ret; aiu->pclk = devm_clk_get(dev, "pclk"); - if (IS_ERR(aiu->pclk)) { - if (PTR_ERR(aiu->pclk) != -EPROBE_DEFER) - dev_err(dev, "Can't get the aiu pclk\n"); - return PTR_ERR(aiu->pclk); - } + if (IS_ERR(aiu->pclk)) + return dev_err_probe(dev, PTR_ERR(aiu->pclk), "Can't get the aiu pclk\n"); aiu->spdif_mclk = devm_clk_get(dev, "spdif_mclk"); - if (IS_ERR(aiu->spdif_mclk)) { - if (PTR_ERR(aiu->spdif_mclk) != -EPROBE_DEFER) - dev_err(dev, "Can't get the aiu spdif master clock\n"); - return PTR_ERR(aiu->spdif_mclk); - } + if (IS_ERR(aiu->spdif_mclk)) + return dev_err_probe(dev, PTR_ERR(aiu->spdif_mclk), + "Can't get the aiu spdif master clock\n"); ret = aiu_clk_bulk_get(dev, aiu_i2s_ids, ARRAY_SIZE(aiu_i2s_ids), &aiu->i2s); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "Can't get the i2s clocks\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Can't get the i2s clocks\n"); ret = aiu_clk_bulk_get(dev, aiu_spdif_ids, ARRAY_SIZE(aiu_spdif_ids), &aiu->spdif); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "Can't get the spdif clocks\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Can't get the spdif clocks\n"); ret = clk_prepare_enable(aiu->pclk); if (ret) { @@ -281,11 +270,8 @@ static int aiu_probe(struct platform_device *pdev) platform_set_drvdata(pdev, aiu); ret = device_reset(dev); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "Failed to reset device\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to reset device\n"); regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) diff --git a/sound/soc/meson/axg-fifo.c b/sound/soc/meson/axg-fifo.c index b9af2d513e09..bccfb770b339 100644 --- a/sound/soc/meson/axg-fifo.c +++ b/sound/soc/meson/axg-fifo.c @@ -351,20 +351,12 @@ int axg_fifo_probe(struct platform_device *pdev) } fifo->pclk = devm_clk_get(dev, NULL); - if (IS_ERR(fifo->pclk)) { - if (PTR_ERR(fifo->pclk) != -EPROBE_DEFER) - dev_err(dev, "failed to get pclk: %ld\n", - PTR_ERR(fifo->pclk)); - return PTR_ERR(fifo->pclk); - } + if (IS_ERR(fifo->pclk)) + return dev_err_probe(dev, PTR_ERR(fifo->pclk), "failed to get pclk\n"); fifo->arb = devm_reset_control_get_exclusive(dev, NULL); - if (IS_ERR(fifo->arb)) { - if (PTR_ERR(fifo->arb) != -EPROBE_DEFER) - dev_err(dev, "failed to get arb reset: %ld\n", - PTR_ERR(fifo->arb)); - return PTR_ERR(fifo->arb); - } + if (IS_ERR(fifo->arb)) + return dev_err_probe(dev, PTR_ERR(fifo->arb), "failed to get arb reset\n"); fifo->irq = of_irq_get(dev->of_node, 0); if (fifo->irq <= 0) { diff --git a/sound/soc/meson/axg-pdm.c b/sound/soc/meson/axg-pdm.c index bfd37d49a73e..672e43a9729d 100644 --- a/sound/soc/meson/axg-pdm.c +++ b/sound/soc/meson/axg-pdm.c @@ -586,7 +586,6 @@ static int axg_pdm_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct axg_pdm *priv; void __iomem *regs; - int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -611,28 +610,16 @@ static int axg_pdm_probe(struct platform_device *pdev) } priv->pclk = devm_clk_get(dev, "pclk"); - if (IS_ERR(priv->pclk)) { - ret = PTR_ERR(priv->pclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get pclk: %d\n", ret); - return ret; - } + if (IS_ERR(priv->pclk)) + return dev_err_probe(dev, PTR_ERR(priv->pclk), "failed to get pclk\n"); priv->dclk = devm_clk_get(dev, "dclk"); - if (IS_ERR(priv->dclk)) { - ret = PTR_ERR(priv->dclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get dclk: %d\n", ret); - return ret; - } + if (IS_ERR(priv->dclk)) + return dev_err_probe(dev, PTR_ERR(priv->dclk), "failed to get dclk\n"); priv->sysclk = devm_clk_get(dev, "sysclk"); - if (IS_ERR(priv->sysclk)) { - ret = PTR_ERR(priv->sysclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get dclk: %d\n", ret); - return ret; - } + if (IS_ERR(priv->sysclk)) + return dev_err_probe(dev, PTR_ERR(priv->sysclk), "failed to get dclk\n"); return devm_snd_soc_register_component(dev, &axg_pdm_component_drv, &axg_pdm_dai_drv, 1); diff --git a/sound/soc/meson/axg-spdifin.c b/sound/soc/meson/axg-spdifin.c index d0d09f945b48..4ba44e0d65d9 100644 --- a/sound/soc/meson/axg-spdifin.c +++ b/sound/soc/meson/axg-spdifin.c @@ -454,7 +454,6 @@ static int axg_spdifin_probe(struct platform_device *pdev) struct axg_spdifin *priv; struct snd_soc_dai_driver *dai_drv; void __iomem *regs; - int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -479,20 +478,12 @@ static int axg_spdifin_probe(struct platform_device *pdev) } priv->pclk = devm_clk_get(dev, "pclk"); - if (IS_ERR(priv->pclk)) { - ret = PTR_ERR(priv->pclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get pclk: %d\n", ret); - return ret; - } + if (IS_ERR(priv->pclk)) + return dev_err_probe(dev, PTR_ERR(priv->pclk), "failed to get pclk\n"); priv->refclk = devm_clk_get(dev, "refclk"); - if (IS_ERR(priv->refclk)) { - ret = PTR_ERR(priv->refclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get mclk: %d\n", ret); - return ret; - } + if (IS_ERR(priv->refclk)) + return dev_err_probe(dev, PTR_ERR(priv->refclk), "failed to get mclk\n"); dai_drv = axg_spdifin_get_dai_drv(dev, priv); if (IS_ERR(dai_drv)) { diff --git a/sound/soc/meson/axg-spdifout.c b/sound/soc/meson/axg-spdifout.c index e769a5ee6e27..3960d082e143 100644 --- a/sound/soc/meson/axg-spdifout.c +++ b/sound/soc/meson/axg-spdifout.c @@ -403,7 +403,6 @@ static int axg_spdifout_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct axg_spdifout *priv; void __iomem *regs; - int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -422,20 +421,12 @@ static int axg_spdifout_probe(struct platform_device *pdev) } priv->pclk = devm_clk_get(dev, "pclk"); - if (IS_ERR(priv->pclk)) { - ret = PTR_ERR(priv->pclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get pclk: %d\n", ret); - return ret; - } + if (IS_ERR(priv->pclk)) + return dev_err_probe(dev, PTR_ERR(priv->pclk), "failed to get pclk\n"); priv->mclk = devm_clk_get(dev, "mclk"); - if (IS_ERR(priv->mclk)) { - ret = PTR_ERR(priv->mclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get mclk: %d\n", ret); - return ret; - } + if (IS_ERR(priv->mclk)) + return dev_err_probe(dev, PTR_ERR(priv->mclk), "failed to get mclk\n"); return devm_snd_soc_register_component(dev, &axg_spdifout_component_drv, axg_spdifout_dai_drv, ARRAY_SIZE(axg_spdifout_dai_drv)); diff --git a/sound/soc/meson/axg-tdm-formatter.c b/sound/soc/meson/axg-tdm-formatter.c index cab7fa2851aa..9883dc777f63 100644 --- a/sound/soc/meson/axg-tdm-formatter.c +++ b/sound/soc/meson/axg-tdm-formatter.c @@ -255,7 +255,6 @@ int axg_tdm_formatter_probe(struct platform_device *pdev) const struct axg_tdm_formatter_driver *drv; struct axg_tdm_formatter *formatter; void __iomem *regs; - int ret; drv = of_device_get_match_data(dev); if (!drv) { @@ -282,57 +281,34 @@ int axg_tdm_formatter_probe(struct platform_device *pdev) /* Peripharal clock */ formatter->pclk = devm_clk_get(dev, "pclk"); - if (IS_ERR(formatter->pclk)) { - ret = PTR_ERR(formatter->pclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get pclk: %d\n", ret); - return ret; - } + if (IS_ERR(formatter->pclk)) + return dev_err_probe(dev, PTR_ERR(formatter->pclk), "failed to get pclk\n"); /* Formatter bit clock */ formatter->sclk = devm_clk_get(dev, "sclk"); - if (IS_ERR(formatter->sclk)) { - ret = PTR_ERR(formatter->sclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get sclk: %d\n", ret); - return ret; - } + if (IS_ERR(formatter->sclk)) + return dev_err_probe(dev, PTR_ERR(formatter->sclk), "failed to get sclk\n"); /* Formatter sample clock */ formatter->lrclk = devm_clk_get(dev, "lrclk"); - if (IS_ERR(formatter->lrclk)) { - ret = PTR_ERR(formatter->lrclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get lrclk: %d\n", ret); - return ret; - } + if (IS_ERR(formatter->lrclk)) + return dev_err_probe(dev, PTR_ERR(formatter->lrclk), "failed to get lrclk\n"); /* Formatter bit clock input multiplexer */ formatter->sclk_sel = devm_clk_get(dev, "sclk_sel"); - if (IS_ERR(formatter->sclk_sel)) { - ret = PTR_ERR(formatter->sclk_sel); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get sclk_sel: %d\n", ret); - return ret; - } + if (IS_ERR(formatter->sclk_sel)) + return dev_err_probe(dev, PTR_ERR(formatter->sclk_sel), "failed to get sclk_sel\n"); /* Formatter sample clock input multiplexer */ formatter->lrclk_sel = devm_clk_get(dev, "lrclk_sel"); - if (IS_ERR(formatter->lrclk_sel)) { - ret = PTR_ERR(formatter->lrclk_sel); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get lrclk_sel: %d\n", ret); - return ret; - } + if (IS_ERR(formatter->lrclk_sel)) + return dev_err_probe(dev, PTR_ERR(formatter->lrclk_sel), + "failed to get lrclk_sel\n"); /* Formatter dedicated reset line */ formatter->reset = devm_reset_control_get_optional_exclusive(dev, NULL); - if (IS_ERR(formatter->reset)) { - ret = PTR_ERR(formatter->reset); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get reset: %d\n", ret); - return ret; - } + if (IS_ERR(formatter->reset)) + return dev_err_probe(dev, PTR_ERR(formatter->reset), "failed to get reset\n"); return devm_snd_soc_register_component(dev, drv->component_drv, NULL, 0); diff --git a/sound/soc/meson/axg-tdm-interface.c b/sound/soc/meson/axg-tdm-interface.c index db077773af7a..0c31934a9630 100644 --- a/sound/soc/meson/axg-tdm-interface.c +++ b/sound/soc/meson/axg-tdm-interface.c @@ -533,21 +533,13 @@ static int axg_tdm_iface_probe(struct platform_device *pdev) /* Bit clock provided on the pad */ iface->sclk = devm_clk_get(dev, "sclk"); - if (IS_ERR(iface->sclk)) { - ret = PTR_ERR(iface->sclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get sclk: %d\n", ret); - return ret; - } + if (IS_ERR(iface->sclk)) + return dev_err_probe(dev, PTR_ERR(iface->sclk), "failed to get sclk\n"); /* Sample clock provided on the pad */ iface->lrclk = devm_clk_get(dev, "lrclk"); - if (IS_ERR(iface->lrclk)) { - ret = PTR_ERR(iface->lrclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get lrclk: %d\n", ret); - return ret; - } + if (IS_ERR(iface->lrclk)) + return dev_err_probe(dev, PTR_ERR(iface->lrclk), "failed to get lrclk\n"); /* * mclk maybe be missing when the cpu dai is in slave mode and @@ -558,13 +550,10 @@ static int axg_tdm_iface_probe(struct platform_device *pdev) iface->mclk = devm_clk_get(dev, "mclk"); if (IS_ERR(iface->mclk)) { ret = PTR_ERR(iface->mclk); - if (ret == -ENOENT) { + if (ret == -ENOENT) iface->mclk = NULL; - } else { - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get mclk: %d\n", ret); - return ret; - } + else + return dev_err_probe(dev, ret, "failed to get mclk\n"); } return devm_snd_soc_register_component(dev, diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c index 29b0174f4b5c..2d8d5717fd8b 100644 --- a/sound/soc/meson/meson-card-utils.c +++ b/sound/soc/meson/meson-card-utils.c @@ -85,11 +85,9 @@ int meson_card_parse_dai(struct snd_soc_card *card, ret = of_parse_phandle_with_args(node, "sound-dai", "#sound-dai-cells", 0, &args); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(card->dev, "can't parse dai %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(card->dev, ret, "can't parse dai\n"); + *dai_of_node = args.np; return snd_soc_get_dai_name(&args, dai_name); diff --git a/sound/soc/meson/t9015.c b/sound/soc/meson/t9015.c index 4c1349dd1e06..a9b8c4e77d40 100644 --- a/sound/soc/meson/t9015.c +++ b/sound/soc/meson/t9015.c @@ -258,18 +258,12 @@ static int t9015_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); priv->pclk = devm_clk_get(dev, "pclk"); - if (IS_ERR(priv->pclk)) { - if (PTR_ERR(priv->pclk) != -EPROBE_DEFER) - dev_err(dev, "failed to get core clock\n"); - return PTR_ERR(priv->pclk); - } + if (IS_ERR(priv->pclk)) + return dev_err_probe(dev, PTR_ERR(priv->pclk), "failed to get core clock\n"); priv->avdd = devm_regulator_get(dev, "AVDD"); - if (IS_ERR(priv->avdd)) { - if (PTR_ERR(priv->avdd) != -EPROBE_DEFER) - dev_err(dev, "failed to AVDD\n"); - return PTR_ERR(priv->avdd); - } + if (IS_ERR(priv->avdd)) + return dev_err_probe(dev, PTR_ERR(priv->avdd), "failed to AVDD\n"); ret = clk_prepare_enable(priv->pclk); if (ret) { -- cgit v1.2.3 From 7a17f6a95a6136cb0a5c41be2b0ac131f9238ae8 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 14 Dec 2021 11:08:38 +0900 Subject: ASoC: mxs: Use dev_err_probe() helper Use the dev_err_probe() helper, instead of open-coding the same operation. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/20211214020843.2225831-18-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/mxs/mxs-sgtl5000.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c index a6407f4388de..2412dc7e65d4 100644 --- a/sound/soc/mxs/mxs-sgtl5000.c +++ b/sound/soc/mxs/mxs-sgtl5000.c @@ -160,12 +160,8 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev) } ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", - ret); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n"); return 0; } -- cgit v1.2.3 From ab6c3e68ab6e3c545b044a00814946e2998c8c53 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 14 Dec 2021 11:08:39 +0900 Subject: ASoC: qcom: Use dev_err_probe() helper Use the dev_err_probe() helper, instead of open-coding the same operation. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/20211214020843.2225831-19-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/qcom/common.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c index 2e1c618f7529..e1bf04d00625 100644 --- a/sound/soc/qcom/common.c +++ b/sound/soc/qcom/common.c @@ -94,9 +94,8 @@ int qcom_snd_parse_of(struct snd_soc_card *card) ret = snd_soc_of_get_dai_name(cpu, &link->cpus->dai_name); if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(card->dev, "%s: error getting cpu dai name: %d\n", - link->name, ret); + dev_err_probe(card->dev, ret, + "%s: error getting cpu dai name\n", link->name); goto err; } @@ -116,9 +115,8 @@ 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) { - if (ret != -EPROBE_DEFER) - dev_err(card->dev, "%s: codec dai not found: %d\n", - link->name, ret); + dev_err_probe(card->dev, ret, + "%s: codec dai not found\n", link->name); goto err; } -- cgit v1.2.3 From b3a66d22a2fd5435bf4d0a357e220cfca88ae5e2 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 14 Dec 2021 11:08:40 +0900 Subject: ASoC: rockchip: Use dev_err_probe() helper Use the dev_err_probe() helper, instead of open-coding the same operation. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/20211214020843.2225831-20-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/rockchip/rk3288_hdmi_analog.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/sound/soc/rockchip/rk3288_hdmi_analog.c b/sound/soc/rockchip/rk3288_hdmi_analog.c index 33a00774746d..b052642ea620 100644 --- a/sound/soc/rockchip/rk3288_hdmi_analog.c +++ b/sound/soc/rockchip/rk3288_hdmi_analog.c @@ -249,13 +249,9 @@ static int snd_rk_mc_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(card, machine); ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret == -EPROBE_DEFER) - return -EPROBE_DEFER; - if (ret) { - dev_err(&pdev->dev, - "Soc register card failed %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Soc register card failed\n"); return ret; } -- cgit v1.2.3 From 27c6eaebcf75e4fac145d17c7fa76bc64b60d24c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 14 Dec 2021 11:08:41 +0900 Subject: ASoC: samsung: Use dev_err_probe() helper Use the dev_err_probe() helper, instead of open-coding the same operation. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/20211214020843.2225831-21-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/samsung/aries_wm8994.c | 17 +++++++---------- sound/soc/samsung/arndale.c | 5 ++--- sound/soc/samsung/littlemill.c | 5 ++--- sound/soc/samsung/lowland.c | 5 ++--- sound/soc/samsung/odroid.c | 4 +--- sound/soc/samsung/smdk_wm8994.c | 4 ++-- sound/soc/samsung/smdk_wm8994pcm.c | 4 ++-- sound/soc/samsung/snow.c | 9 +++------ sound/soc/samsung/speyside.c | 5 ++--- sound/soc/samsung/tm2_wm5110.c | 3 +-- sound/soc/samsung/tobermory.c | 5 ++--- 11 files changed, 26 insertions(+), 40 deletions(-) diff --git a/sound/soc/samsung/aries_wm8994.c b/sound/soc/samsung/aries_wm8994.c index 313ab650f8d9..5265e546b124 100644 --- a/sound/soc/samsung/aries_wm8994.c +++ b/sound/soc/samsung/aries_wm8994.c @@ -585,19 +585,16 @@ static int aries_audio_probe(struct platform_device *pdev) extcon_np = of_parse_phandle(np, "extcon", 0); priv->usb_extcon = extcon_find_edev_by_node(extcon_np); - if (IS_ERR(priv->usb_extcon)) { - if (PTR_ERR(priv->usb_extcon) != -EPROBE_DEFER) - dev_err(dev, "Failed to get extcon device"); - return PTR_ERR(priv->usb_extcon); - } + if (IS_ERR(priv->usb_extcon)) + return dev_err_probe(dev, PTR_ERR(priv->usb_extcon), + "Failed to get extcon device"); of_node_put(extcon_np); priv->adc = devm_iio_channel_get(dev, "headset-detect"); - if (IS_ERR(priv->adc)) { - if (PTR_ERR(priv->adc) != -EPROBE_DEFER) - dev_err(dev, "Failed to get ADC channel"); - return PTR_ERR(priv->adc); - } + if (IS_ERR(priv->adc)) + return dev_err_probe(dev, PTR_ERR(priv->adc), + "Failed to get ADC channel"); + if (priv->adc->channel->type != IIO_VOLTAGE) return -EINVAL; diff --git a/sound/soc/samsung/arndale.c b/sound/soc/samsung/arndale.c index 606ac5e33a8e..a5dc640d0d76 100644 --- a/sound/soc/samsung/arndale.c +++ b/sound/soc/samsung/arndale.c @@ -174,9 +174,8 @@ static int arndale_audio_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(card->dev, card); if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, - "snd_soc_register_card() failed: %d\n", ret); + dev_err_probe(&pdev->dev, ret, + "snd_soc_register_card() failed\n"); goto err_put_of_nodes; } return 0; diff --git a/sound/soc/samsung/littlemill.c b/sound/soc/samsung/littlemill.c index 390f2dd735ad..34067cc314ff 100644 --- a/sound/soc/samsung/littlemill.c +++ b/sound/soc/samsung/littlemill.c @@ -325,9 +325,8 @@ static int littlemill_probe(struct platform_device *pdev) card->dev = &pdev->dev; ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret && ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", - ret); + if (ret) + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); return ret; } diff --git a/sound/soc/samsung/lowland.c b/sound/soc/samsung/lowland.c index 998d10cf8c94..7b12ccd2a9b2 100644 --- a/sound/soc/samsung/lowland.c +++ b/sound/soc/samsung/lowland.c @@ -183,9 +183,8 @@ static int lowland_probe(struct platform_device *pdev) card->dev = &pdev->dev; ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret && ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", - ret); + if (ret) + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); return ret; } diff --git a/sound/soc/samsung/odroid.c b/sound/soc/samsung/odroid.c index ca643a488c3c..4ff12e2e704f 100644 --- a/sound/soc/samsung/odroid.c +++ b/sound/soc/samsung/odroid.c @@ -311,9 +311,7 @@ static int odroid_audio_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(dev, card); if (ret < 0) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "snd_soc_register_card() failed: %d\n", - ret); + dev_err_probe(dev, ret, "snd_soc_register_card() failed\n"); goto err_put_clk_i2s; } diff --git a/sound/soc/samsung/smdk_wm8994.c b/sound/soc/samsung/smdk_wm8994.c index 7661b637946d..821ad1eb1b79 100644 --- a/sound/soc/samsung/smdk_wm8994.c +++ b/sound/soc/samsung/smdk_wm8994.c @@ -179,8 +179,8 @@ static int smdk_audio_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret && ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "snd_soc_register_card() failed:%d\n", ret); + if (ret) + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); return ret; } diff --git a/sound/soc/samsung/smdk_wm8994pcm.c b/sound/soc/samsung/smdk_wm8994pcm.c index 029448f5bedb..d77dc54cae9c 100644 --- a/sound/soc/samsung/smdk_wm8994pcm.c +++ b/sound/soc/samsung/smdk_wm8994pcm.c @@ -118,8 +118,8 @@ static int snd_smdk_probe(struct platform_device *pdev) smdk_pcm.dev = &pdev->dev; ret = devm_snd_soc_register_card(&pdev->dev, &smdk_pcm); - if (ret && ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret); + if (ret) + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n"); return ret; } diff --git a/sound/soc/samsung/snow.c b/sound/soc/samsung/snow.c index 6da674e901ca..02372109c251 100644 --- a/sound/soc/samsung/snow.c +++ b/sound/soc/samsung/snow.c @@ -212,12 +212,9 @@ static int snow_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(card, priv); ret = devm_snd_soc_register_card(dev, card); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, - "snd_soc_register_card failed (%d)\n", ret); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "snd_soc_register_card failed\n"); return ret; } diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c index f5f6ba00d073..37b1f4f60b21 100644 --- a/sound/soc/samsung/speyside.c +++ b/sound/soc/samsung/speyside.c @@ -330,9 +330,8 @@ static int speyside_probe(struct platform_device *pdev) card->dev = &pdev->dev; ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret && ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", - ret); + if (ret) + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); return ret; } diff --git a/sound/soc/samsung/tm2_wm5110.c b/sound/soc/samsung/tm2_wm5110.c index a2c77e6defec..d611ec9e5325 100644 --- a/sound/soc/samsung/tm2_wm5110.c +++ b/sound/soc/samsung/tm2_wm5110.c @@ -612,8 +612,7 @@ static int tm2_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(dev, card); if (ret < 0) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "Failed to register card: %d\n", ret); + dev_err_probe(dev, ret, "Failed to register card\n"); goto dai_node_put; } diff --git a/sound/soc/samsung/tobermory.c b/sound/soc/samsung/tobermory.c index 15223d860cb7..8d3149a47a4c 100644 --- a/sound/soc/samsung/tobermory.c +++ b/sound/soc/samsung/tobermory.c @@ -229,9 +229,8 @@ static int tobermory_probe(struct platform_device *pdev) card->dev = &pdev->dev; ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret && ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", - ret); + if (ret) + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); return ret; } -- cgit v1.2.3 From efc162cbd480f1fb47d439c193ec9731bcc6c749 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 14 Dec 2021 11:08:42 +0900 Subject: ASoC: stm: Use dev_err_probe() helper Use the dev_err_probe() helper, instead of open-coding the same operation. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/20211214020843.2225831-22-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/stm/stm32_i2s.c | 62 +++++++++++++++---------------------------- sound/soc/stm/stm32_sai.c | 37 +++++++++----------------- sound/soc/stm/stm32_sai_sub.c | 25 ++++++----------- sound/soc/stm/stm32_spdifrx.c | 44 +++++++++++------------------- 4 files changed, 57 insertions(+), 111 deletions(-) diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c index 24327cabd32a..ac5dff4d1677 100644 --- a/sound/soc/stm/stm32_i2s.c +++ b/sound/soc/stm/stm32_i2s.c @@ -1045,36 +1045,24 @@ static int stm32_i2s_parse_dt(struct platform_device *pdev, /* Get clocks */ i2s->pclk = devm_clk_get(&pdev->dev, "pclk"); - if (IS_ERR(i2s->pclk)) { - if (PTR_ERR(i2s->pclk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Could not get pclk: %ld\n", - PTR_ERR(i2s->pclk)); - return PTR_ERR(i2s->pclk); - } + if (IS_ERR(i2s->pclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->pclk), + "Could not get pclk\n"); i2s->i2sclk = devm_clk_get(&pdev->dev, "i2sclk"); - if (IS_ERR(i2s->i2sclk)) { - if (PTR_ERR(i2s->i2sclk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Could not get i2sclk: %ld\n", - PTR_ERR(i2s->i2sclk)); - return PTR_ERR(i2s->i2sclk); - } + if (IS_ERR(i2s->i2sclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->i2sclk), + "Could not get i2sclk\n"); i2s->x8kclk = devm_clk_get(&pdev->dev, "x8k"); - if (IS_ERR(i2s->x8kclk)) { - if (PTR_ERR(i2s->x8kclk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Could not get x8k parent clock: %ld\n", - PTR_ERR(i2s->x8kclk)); - return PTR_ERR(i2s->x8kclk); - } + if (IS_ERR(i2s->x8kclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->x8kclk), + "Could not get x8k parent clock\n"); i2s->x11kclk = devm_clk_get(&pdev->dev, "x11k"); - if (IS_ERR(i2s->x11kclk)) { - if (PTR_ERR(i2s->x11kclk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Could not get x11k parent clock: %ld\n", - PTR_ERR(i2s->x11kclk)); - return PTR_ERR(i2s->x11kclk); - } + if (IS_ERR(i2s->x11kclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->x11kclk), + "Could not get x11k parent clock\n"); /* Register mclk provider if requested */ if (of_find_property(np, "#clock-cells", NULL)) { @@ -1097,12 +1085,10 @@ static int stm32_i2s_parse_dt(struct platform_device *pdev, /* Reset */ rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); - if (IS_ERR(rst)) { - if (PTR_ERR(rst) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Reset controller error %ld\n", - PTR_ERR(rst)); - return PTR_ERR(rst); - } + if (IS_ERR(rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(rst), + "Reset controller error\n"); + reset_control_assert(rst); udelay(2); reset_control_deassert(rst); @@ -1145,21 +1131,15 @@ static int stm32_i2s_probe(struct platform_device *pdev) i2s->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "pclk", i2s->base, i2s->regmap_conf); - if (IS_ERR(i2s->regmap)) { - if (PTR_ERR(i2s->regmap) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Regmap init error %ld\n", - PTR_ERR(i2s->regmap)); - return PTR_ERR(i2s->regmap); - } + if (IS_ERR(i2s->regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->regmap), + "Regmap init error\n"); pm_runtime_enable(&pdev->dev); ret = snd_dmaengine_pcm_register(&pdev->dev, &stm32_i2s_pcm_config, 0); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "PCM DMA register error %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "PCM DMA register error\n"); ret = snd_soc_register_component(&pdev->dev, &stm32_i2s_component, i2s->dai_drv, 1); diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c index 058757c721f0..8e21e6f886fc 100644 --- a/sound/soc/stm/stm32_sai.c +++ b/sound/soc/stm/stm32_sai.c @@ -173,29 +173,20 @@ static int stm32_sai_probe(struct platform_device *pdev) if (!STM_SAI_IS_F4(sai)) { sai->pclk = devm_clk_get(&pdev->dev, "pclk"); - if (IS_ERR(sai->pclk)) { - if (PTR_ERR(sai->pclk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "missing bus clock pclk: %ld\n", - PTR_ERR(sai->pclk)); - return PTR_ERR(sai->pclk); - } + if (IS_ERR(sai->pclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(sai->pclk), + "missing bus clock pclk\n"); } sai->clk_x8k = devm_clk_get(&pdev->dev, "x8k"); - if (IS_ERR(sai->clk_x8k)) { - if (PTR_ERR(sai->clk_x8k) != -EPROBE_DEFER) - dev_err(&pdev->dev, "missing x8k parent clock: %ld\n", - PTR_ERR(sai->clk_x8k)); - return PTR_ERR(sai->clk_x8k); - } + if (IS_ERR(sai->clk_x8k)) + return dev_err_probe(&pdev->dev, PTR_ERR(sai->clk_x8k), + "missing x8k parent clock\n"); sai->clk_x11k = devm_clk_get(&pdev->dev, "x11k"); - if (IS_ERR(sai->clk_x11k)) { - if (PTR_ERR(sai->clk_x11k) != -EPROBE_DEFER) - dev_err(&pdev->dev, "missing x11k parent clock: %ld\n", - PTR_ERR(sai->clk_x11k)); - return PTR_ERR(sai->clk_x11k); - } + if (IS_ERR(sai->clk_x11k)) + return dev_err_probe(&pdev->dev, PTR_ERR(sai->clk_x11k), + "missing x11k parent clock\n"); /* init irqs */ sai->irq = platform_get_irq(pdev, 0); @@ -204,12 +195,10 @@ static int stm32_sai_probe(struct platform_device *pdev) /* reset */ rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); - if (IS_ERR(rst)) { - if (PTR_ERR(rst) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Reset controller error %ld\n", - PTR_ERR(rst)); - return PTR_ERR(rst); - } + if (IS_ERR(rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(rst), + "Reset controller error\n"); + reset_control_assert(rst); udelay(2); reset_control_deassert(rst); diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index 95cd38a502bb..dd636af81c9b 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -1379,12 +1379,9 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, */ sai->regmap = devm_regmap_init_mmio(&pdev->dev, base, sai->regmap_config); - if (IS_ERR(sai->regmap)) { - if (PTR_ERR(sai->regmap) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Regmap init error %ld\n", - PTR_ERR(sai->regmap)); - return PTR_ERR(sai->regmap); - } + if (IS_ERR(sai->regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(sai->regmap), + "Regmap init error\n"); /* Get direction property */ if (of_property_match_string(np, "dma-names", "tx") >= 0) { @@ -1472,12 +1469,9 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, of_node_put(args.np); sai->sai_ck = devm_clk_get(&pdev->dev, "sai_ck"); - if (IS_ERR(sai->sai_ck)) { - if (PTR_ERR(sai->sai_ck) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Missing kernel clock sai_ck: %ld\n", - PTR_ERR(sai->sai_ck)); - return PTR_ERR(sai->sai_ck); - } + if (IS_ERR(sai->sai_ck)) + return dev_err_probe(&pdev->dev, PTR_ERR(sai->sai_ck), + "Missing kernel clock sai_ck\n"); ret = clk_prepare(sai->pdata->pclk); if (ret < 0) @@ -1551,11 +1545,8 @@ static int stm32_sai_sub_probe(struct platform_device *pdev) conf = &stm32_sai_pcm_config_spdif; ret = snd_dmaengine_pcm_register(&pdev->dev, conf, 0); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "Could not register pcm dma\n"); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "Could not register pcm dma\n"); ret = snd_soc_register_component(&pdev->dev, &stm32_component, &sai->cpu_dai_drv, 1); diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c index a9ccdc2c5867..6f7882c4fe6a 100644 --- a/sound/soc/stm/stm32_spdifrx.c +++ b/sound/soc/stm/stm32_spdifrx.c @@ -406,12 +406,9 @@ static int stm32_spdifrx_dma_ctrl_register(struct device *dev, int ret; spdifrx->ctrl_chan = dma_request_chan(dev, "rx-ctrl"); - if (IS_ERR(spdifrx->ctrl_chan)) { - if (PTR_ERR(spdifrx->ctrl_chan) != -EPROBE_DEFER) - dev_err(dev, "dma_request_slave_channel error %ld\n", - PTR_ERR(spdifrx->ctrl_chan)); - return PTR_ERR(spdifrx->ctrl_chan); - } + if (IS_ERR(spdifrx->ctrl_chan)) + return dev_err_probe(dev, PTR_ERR(spdifrx->ctrl_chan), + "dma_request_slave_channel error\n"); spdifrx->dmab = devm_kzalloc(dev, sizeof(struct snd_dma_buffer), GFP_KERNEL); @@ -930,12 +927,9 @@ static int stm32_spdifrx_parse_of(struct platform_device *pdev, spdifrx->phys_addr = res->start; spdifrx->kclk = devm_clk_get(&pdev->dev, "kclk"); - if (IS_ERR(spdifrx->kclk)) { - if (PTR_ERR(spdifrx->kclk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Could not get kclk: %ld\n", - PTR_ERR(spdifrx->kclk)); - return PTR_ERR(spdifrx->kclk); - } + if (IS_ERR(spdifrx->kclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(spdifrx->kclk), + "Could not get kclk\n"); spdifrx->irq = platform_get_irq(pdev, 0); if (spdifrx->irq < 0) @@ -987,12 +981,9 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) spdifrx->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "kclk", spdifrx->base, spdifrx->regmap_conf); - if (IS_ERR(spdifrx->regmap)) { - if (PTR_ERR(spdifrx->regmap) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Regmap init error %ld\n", - PTR_ERR(spdifrx->regmap)); - return PTR_ERR(spdifrx->regmap); - } + if (IS_ERR(spdifrx->regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(spdifrx->regmap), + "Regmap init error\n"); ret = devm_request_irq(&pdev->dev, spdifrx->irq, stm32_spdifrx_isr, 0, dev_name(&pdev->dev), spdifrx); @@ -1002,12 +993,10 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) } rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); - if (IS_ERR(rst)) { - if (PTR_ERR(rst) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Reset controller error %ld\n", - PTR_ERR(rst)); - return PTR_ERR(rst); - } + if (IS_ERR(rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(rst), + "Reset controller error\n"); + reset_control_assert(rst); udelay(2); reset_control_deassert(rst); @@ -1016,11 +1005,8 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) pcm_config = &stm32_spdifrx_pcm_config; ret = snd_dmaengine_pcm_register(&pdev->dev, pcm_config, 0); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "PCM DMA register error %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "PCM DMA register error\n"); ret = snd_soc_register_component(&pdev->dev, &stm32_spdifrx_component, -- cgit v1.2.3 From 11a95c583c1de215d2c338bf5cb9f929312616f8 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 14 Dec 2021 11:08:43 +0900 Subject: ASoC: sunxi: Use dev_err_probe() helper Use the dev_err_probe() helper, instead of open-coding the same operation. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/20211214020843.2225831-23-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/sunxi/sun4i-codec.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index da597e456beb..60712f24ade5 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -1752,8 +1752,7 @@ static int sun4i_codec_probe(struct platform_device *pdev) GPIOD_OUT_LOW); if (IS_ERR(scodec->gpio_pa)) { ret = PTR_ERR(scodec->gpio_pa); - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "Failed to get pa gpio: %d\n", ret); + dev_err_probe(&pdev->dev, ret, "Failed to get pa gpio\n"); return ret; } -- cgit v1.2.3 From e047d0372689f5d4231eefb731b60ac64720bbf0 Mon Sep 17 00:00:00 2001 From: Ricard Wanderlof Date: Wed, 15 Dec 2021 18:01:24 +0100 Subject: ASoC: tlv320adc3xxx: New codec bindings DT bindings for Texas Instruments TLV320ADC3001 and TLV320ADC3101 audio ADCs. Signed-off-by: Ricard Wanderlof Link: https://lore.kernel.org/r/alpine.DEB.2.21.2112151759170.27889@lap5cg0092dnk.se.axis.com Signed-off-by: Mark Brown --- .../bindings/sound/ti,tlv320adc3xxx.yaml | 137 +++++++++++++++++++++ include/dt-bindings/sound/tlv320adc3xxx.h | 28 +++++ 2 files changed, 165 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/ti,tlv320adc3xxx.yaml create mode 100644 include/dt-bindings/sound/tlv320adc3xxx.h diff --git a/Documentation/devicetree/bindings/sound/ti,tlv320adc3xxx.yaml b/Documentation/devicetree/bindings/sound/ti,tlv320adc3xxx.yaml new file mode 100644 index 000000000000..83936f594d1a --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ti,tlv320adc3xxx.yaml @@ -0,0 +1,137 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/ti,tlv320adc3xxx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments TLV320ADC3001/TLV320ADC3101 Stereo ADC + +maintainers: + - Ricard Wanderlof + +description: | + Texas Instruments TLV320ADC3001 and TLV320ADC3101 Stereo ADC + https://www.ti.com/product/TLV320ADC3001 + https://www.ti.com/product/TLV320ADC3101 + +properties: + compatible: + enum: + - ti,tlv320adc3001 + - ti,tlv320adc3101 + + reg: + maxItems: 1 + description: I2C address + + '#sound-dai-cells': + const: 0 + + '#gpio-cells': + const: 2 + + gpio-controller: true + + reset-gpios: + maxItems: 1 + description: GPIO pin used for codec reset (RESET pin) + + clocks: + maxItems: 1 + description: Master clock (MCLK) + + ti,dmdin-gpio1: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 0 # ADC3XXX_GPIO_DISABLED - I/O buffers powered down and not used + - 1 # ADC3XXX_GPIO_INPUT - Various non-GPIO input functions + - 2 # ADC3XXX_GPIO_GPI - General purpose input + - 3 # ADC3XXX_GPIO_GPO - General purpose output + - 4 # ADC3XXX_GPIO_CLKOUT - Clock source set in CLKOUT_MUX reg + - 5 # ADC3XXX_GPIO_INT1 - INT1 output + - 6 # ADC3XXX_GPIO_SECONDARY_BCLK - Codec interface secondary BCLK + - 7 # ADC3XXX_GPIO_SECONDARY_WCLK - Codec interface secondary WCLK + default: 0 + description: | + Configuration for DMDIN/GPIO1 pin. + + When ADC3XXX_GPIO_GPO is configured, this causes corresponding the + ALSA control "GPIOx Output" to appear, as a switch control. + + ti,dmclk-gpio2: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 0 # ADC3XXX_GPIO_DISABLED - I/O buffers powered down and not used + - 1 # ADC3XXX_GPIO_INPUT - Various non-GPIO input functions + - 2 # ADC3XXX_GPIO_GPI - General purpose input + - 3 # ADC3XXX_GPIO_GPO - General purpose output + - 4 # ADC3XXX_GPIO_CLKOUT - Clock source set in CLKOUT_MUX reg + - 5 # ADC3XXX_GPIO_INT1 - INT1 output + - 6 # ADC3XXX_GPIO_SECONDARY_BCLK - Codec interface secondary BCLK + - 7 # ADC3XXX_GPIO_SECONDARY_WCLK - Codec interface secondary WCLK + default: 0 + description: | + Configuration for DMCLK/GPIO2 pin. + + When ADC3XXX_GPIO_GPO is configured, this causes corresponding the + ALSA control "GPIOx Output" to appear, as a switch control. + + Note that there is currently no support for reading the GPIO pins as + inputs. + + ti,micbias1-vg: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 0 # ADC3XXX_MICBIAS_OFF - Mic bias is powered down + - 1 # ADC3XXX_MICBIAS_2_0V - Mic bias is set to 2.0V + - 2 # ADC3XXX_MICBIAS_2_5V - Mic bias is set to 2.5V + - 3 # ADC3XXX_MICBIAS_AVDD - Mic bias is same as AVDD supply + default: 0 + description: | + Mic bias voltage output on MICBIAS1 pin + + ti,micbias2-vg: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 0 # ADC3XXX_MICBIAS_OFF - Mic bias is powered down + - 1 # ADC3XXX_MICBIAS_2_0V - Mic bias is set to 2.0V + - 2 # ADC3XXX_MICBIAS_2_5V - Mic bias is set to 2.5V + - 3 # ADC3XXX_MICBIAS_AVDD - Mic bias is same as AVDD supply + default: 0 + description: | + Mic bias voltage output on MICBIAS2 pin + +required: + - compatible + - reg + - clocks + +additionalProperties: false + +examples: + - | + + #include + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + tlv320adc3101: audio-codec@18 { + compatible = "ti,tlv320adc3101"; + reg = <0x18>; + reset-gpios = <&gpio_pc 3 GPIO_ACTIVE_LOW>; + clocks = <&audio_mclk>; + gpio-controller; + #gpio-cells = <2>; + ti,dmdin-gpio1 = ; + ti,micbias1-vg = ; + }; + }; + + audio_mclk: clock { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <24576000>; + }; +... diff --git a/include/dt-bindings/sound/tlv320adc3xxx.h b/include/dt-bindings/sound/tlv320adc3xxx.h new file mode 100644 index 000000000000..ec988439da20 --- /dev/null +++ b/include/dt-bindings/sound/tlv320adc3xxx.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Devicetree bindings definitions for tlv320adc3xxx driver. + * + * Copyright (C) 2021 Axis Communications AB + */ +#ifndef __DT_TLV320ADC3XXX_H +#define __DT_TLV320ADC3XXX_H + +#define ADC3XXX_GPIO_DISABLED 0 /* I/O buffers powered down */ +#define ADC3XXX_GPIO_INPUT 1 /* Various non-GPIO inputs */ +#define ADC3XXX_GPIO_GPI 2 /* General purpose input */ +#define ADC3XXX_GPIO_GPO 3 /* General purpose output */ +#define ADC3XXX_GPIO_CLKOUT 4 /* Source set in reg. CLKOUT_MUX */ +#define ADC3XXX_GPIO_INT1 5 /* INT1 output */ +#define ADC3XXX_GPIO_INT2 6 /* INT2 output */ +/* value 7 is reserved */ +#define ADC3XXX_GPIO_SECONDARY_BCLK 8 /* Codec interface secondary BCLK */ +#define ADC3XXX_GPIO_SECONDARY_WCLK 9 /* Codec interface secondary WCLK */ +#define ADC3XXX_GPIO_ADC_MOD_CLK 10 /* Clock output for digital mics */ +/* values 11-15 reserved */ + +#define ADC3XXX_MICBIAS_OFF 0 /* Micbias pin powered off */ +#define ADC3XXX_MICBIAS_2_0V 1 /* Micbias pin set to 2.0V */ +#define ADC3XXX_MICBIAS_2_5V 2 /* Micbias pin set to 2.5V */ +#define ADC3XXX_MICBIAS_AVDD 3 /* Use AVDD voltage for micbias pin */ + +#endif /* __DT_TLV320ADC3XXX_H */ -- cgit v1.2.3 From e9a3b57efd28fe889a98171bdc1e9e0dd7eb9a50 Mon Sep 17 00:00:00 2001 From: Ricard Wanderlof Date: Wed, 15 Dec 2021 18:04:23 +0100 Subject: ASoC: codec: tlv320adc3xxx: New codec driver New codec driver for Texas Instruments TLV320ADC3001 and TLV320ADC3101 audio ADCs. Signed-off-by: Ricard Wanderlof Link: https://lore.kernel.org/r/alpine.DEB.2.21.2112151801370.27889@lap5cg0092dnk.se.axis.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 8 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/tlv320adc3xxx.c | 1311 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 1321 insertions(+) create mode 100644 sound/soc/codecs/tlv320adc3xxx.c diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index c033ee7d82e4..22836ca9b478 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -220,6 +220,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_TDA7419 imply SND_SOC_TFA9879 imply SND_SOC_TFA989X + imply SND_SOC_TLV320ADC3XXX imply SND_SOC_TLV320ADCX140 imply SND_SOC_TLV320AIC23_I2C imply SND_SOC_TLV320AIC23_SPI @@ -1496,6 +1497,13 @@ config SND_SOC_TFA989X Note that the driver currently bypasses the built-in "CoolFlux DSP" and does not support (hardware) volume control. +config SND_SOC_TLV320ADC3XXX + tristate "Texas Instruments TLV320ADC3001/3101 audio ADC" + depends on I2C + help + Enable support for Texas Instruments TLV320ADC3001 and TLV320ADC3101 + ADCs. + config SND_SOC_TLV320AIC23 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 485eee75502b..24bc6b34ba2f 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -239,6 +239,7 @@ snd-soc-tda7419-objs := tda7419.o snd-soc-tas2770-objs := tas2770.o snd-soc-tfa9879-objs := tfa9879.o snd-soc-tfa989x-objs := tfa989x.o +snd-soc-tlv320adc3xxx-objs := tlv320adc3xxx.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o snd-soc-tlv320aic23-spi-objs := tlv320aic23-spi.o @@ -574,6 +575,7 @@ obj-$(CONFIG_SND_SOC_TDA7419) += snd-soc-tda7419.o obj-$(CONFIG_SND_SOC_TAS2770) += snd-soc-tas2770.o obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o obj-$(CONFIG_SND_SOC_TFA989X) += snd-soc-tfa989x.o +obj-$(CONFIG_SND_SOC_TLV320ADC3XXX) += snd-soc-tlv320adc3xxx.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o obj-$(CONFIG_SND_SOC_TLV320AIC23_SPI) += snd-soc-tlv320aic23-spi.o diff --git a/sound/soc/codecs/tlv320adc3xxx.c b/sound/soc/codecs/tlv320adc3xxx.c new file mode 100644 index 000000000000..a683bda7eb36 --- /dev/null +++ b/sound/soc/codecs/tlv320adc3xxx.c @@ -0,0 +1,1311 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Based on sound/soc/codecs/tlv320aic3x.c by Vladimir Barinov +// +// Copyright (C) 2010 Mistral Solutions Pvt Ltd. +// Author: Shahina Shaik +// +// Copyright (C) 2014-2018, Ambarella, Inc. +// Author: Dongge wu +// +// Copyright (C) 2021 Axis Communications AB +// Author: Ricard Wanderlof +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * General definitions defining exported functionality. + */ + +#define ADC3XXX_MICBIAS_PINS 2 + +/* Number of GPIO pins exposed via the gpiolib interface */ +#define ADC3XXX_GPIOS_MAX 2 + +#define ADC3XXX_RATES SNDRV_PCM_RATE_8000_96000 +#define ADC3XXX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +/* + * PLL modes, to be used for clk_id for set_sysclk callback. + * + * The default behavior (AUTO) is to take the first matching entry in the clock + * table, which is intended to be the PLL based one if there is more than one. + * + * Setting the clock source using simple-card (clocks or + * system-clock-frequency property) sets clk_id = 0 = ADC3XXX_PLL_AUTO. + */ +#define ADC3XXX_PLL_AUTO 0 /* Use first available mode */ +#define ADC3XXX_PLL_ENABLE 1 /* Use PLL for clock generation */ +#define ADC3XXX_PLL_BYPASS 2 /* Don't use PLL for clock generation */ + +/* Register definitions. */ + +#define ADC3XXX_PAGE_SIZE 128 +#define ADC3XXX_REG(page, reg) ((page * ADC3XXX_PAGE_SIZE) + reg) + +/* + * Page 0 registers. + */ + +#define ADC3XXX_PAGE_SELECT ADC3XXX_REG(0, 0) +#define ADC3XXX_RESET ADC3XXX_REG(0, 1) + +/* 2-3 Reserved */ + +#define ADC3XXX_CLKGEN_MUX ADC3XXX_REG(0, 4) +#define ADC3XXX_PLL_PROG_PR ADC3XXX_REG(0, 5) +#define ADC3XXX_PLL_PROG_J ADC3XXX_REG(0, 6) +#define ADC3XXX_PLL_PROG_D_MSB ADC3XXX_REG(0, 7) +#define ADC3XXX_PLL_PROG_D_LSB ADC3XXX_REG(0, 8) + +/* 9-17 Reserved */ + +#define ADC3XXX_ADC_NADC ADC3XXX_REG(0, 18) +#define ADC3XXX_ADC_MADC ADC3XXX_REG(0, 19) +#define ADC3XXX_ADC_AOSR ADC3XXX_REG(0, 20) +#define ADC3XXX_ADC_IADC ADC3XXX_REG(0, 21) + +/* 23-24 Reserved */ + +#define ADC3XXX_CLKOUT_MUX ADC3XXX_REG(0, 25) +#define ADC3XXX_CLKOUT_M_DIV ADC3XXX_REG(0, 26) +#define ADC3XXX_INTERFACE_CTRL_1 ADC3XXX_REG(0, 27) +#define ADC3XXX_CH_OFFSET_1 ADC3XXX_REG(0, 28) +#define ADC3XXX_INTERFACE_CTRL_2 ADC3XXX_REG(0, 29) +#define ADC3XXX_BCLK_N_DIV ADC3XXX_REG(0, 30) +#define ADC3XXX_INTERFACE_CTRL_3 ADC3XXX_REG(0, 31) +#define ADC3XXX_INTERFACE_CTRL_4 ADC3XXX_REG(0, 32) +#define ADC3XXX_INTERFACE_CTRL_5 ADC3XXX_REG(0, 33) +#define ADC3XXX_I2S_SYNC ADC3XXX_REG(0, 34) +/* 35 Reserved */ +#define ADC3XXX_ADC_FLAG ADC3XXX_REG(0, 36) +#define ADC3XXX_CH_OFFSET_2 ADC3XXX_REG(0, 37) +#define ADC3XXX_I2S_TDM_CTRL ADC3XXX_REG(0, 38) +/* 39-41 Reserved */ +#define ADC3XXX_INTR_FLAG_1 ADC3XXX_REG(0, 42) +#define ADC3XXX_INTR_FLAG_2 ADC3XXX_REG(0, 43) +/* 44 Reserved */ +#define ADC3XXX_INTR_FLAG_ADC1 ADC3XXX_REG(0, 45) +/* 46 Reserved */ +#define ADC3XXX_INTR_FLAG_ADC2 ADC3XXX_REG(0, 47) +#define ADC3XXX_INT1_CTRL ADC3XXX_REG(0, 48) +#define ADC3XXX_INT2_CTRL ADC3XXX_REG(0, 49) +/* 50 Reserved */ +#define ADC3XXX_GPIO2_CTRL ADC3XXX_REG(0, 51) +#define ADC3XXX_GPIO1_CTRL ADC3XXX_REG(0, 52) +#define ADC3XXX_DOUT_CTRL ADC3XXX_REG(0, 53) +/* 54-56 Reserved */ +#define ADC3XXX_SYNC_CTRL_1 ADC3XXX_REG(0, 57) +#define ADC3XXX_SYNC_CTRL_2 ADC3XXX_REG(0, 58) +#define ADC3XXX_CIC_GAIN_CTRL ADC3XXX_REG(0, 59) +/* 60 Reserved */ +#define ADC3XXX_PRB_SELECT ADC3XXX_REG(0, 61) +#define ADC3XXX_INST_MODE_CTRL ADC3XXX_REG(0, 62) +/* 63-79 Reserved */ +#define ADC3XXX_MIC_POLARITY_CTRL ADC3XXX_REG(0, 80) +#define ADC3XXX_ADC_DIGITAL ADC3XXX_REG(0, 81) +#define ADC3XXX_ADC_FGA ADC3XXX_REG(0, 82) +#define ADC3XXX_LADC_VOL ADC3XXX_REG(0, 83) +#define ADC3XXX_RADC_VOL ADC3XXX_REG(0, 84) +#define ADC3XXX_ADC_PHASE_COMP ADC3XXX_REG(0, 85) +#define ADC3XXX_LEFT_CHN_AGC_1 ADC3XXX_REG(0, 86) +#define ADC3XXX_LEFT_CHN_AGC_2 ADC3XXX_REG(0, 87) +#define ADC3XXX_LEFT_CHN_AGC_3 ADC3XXX_REG(0, 88) +#define ADC3XXX_LEFT_CHN_AGC_4 ADC3XXX_REG(0, 89) +#define ADC3XXX_LEFT_CHN_AGC_5 ADC3XXX_REG(0, 90) +#define ADC3XXX_LEFT_CHN_AGC_6 ADC3XXX_REG(0, 91) +#define ADC3XXX_LEFT_CHN_AGC_7 ADC3XXX_REG(0, 92) +#define ADC3XXX_LEFT_AGC_GAIN ADC3XXX_REG(0, 93) +#define ADC3XXX_RIGHT_CHN_AGC_1 ADC3XXX_REG(0, 94) +#define ADC3XXX_RIGHT_CHN_AGC_2 ADC3XXX_REG(0, 95) +#define ADC3XXX_RIGHT_CHN_AGC_3 ADC3XXX_REG(0, 96) +#define ADC3XXX_RIGHT_CHN_AGC_4 ADC3XXX_REG(0, 97) +#define ADC3XXX_RIGHT_CHN_AGC_5 ADC3XXX_REG(0, 98) +#define ADC3XXX_RIGHT_CHN_AGC_6 ADC3XXX_REG(0, 99) +#define ADC3XXX_RIGHT_CHN_AGC_7 ADC3XXX_REG(0, 100) +#define ADC3XXX_RIGHT_AGC_GAIN ADC3XXX_REG(0, 101) +/* 102-127 Reserved */ + +/* + * Page 1 registers. + */ + +/* 1-25 Reserved */ +#define ADC3XXX_DITHER_CTRL ADC3XXX_REG(1, 26) +/* 27-50 Reserved */ +#define ADC3XXX_MICBIAS_CTRL ADC3XXX_REG(1, 51) +#define ADC3XXX_LEFT_PGA_SEL_1 ADC3XXX_REG(1, 52) +/* 53 Reserved */ +#define ADC3XXX_LEFT_PGA_SEL_2 ADC3XXX_REG(1, 54) +#define ADC3XXX_RIGHT_PGA_SEL_1 ADC3XXX_REG(1, 55) +#define ADC3XXX_RIGHT_PGA_SEL_2 ADC3XXX_REG(1, 57) +#define ADC3XXX_LEFT_APGA_CTRL ADC3XXX_REG(1, 59) +#define ADC3XXX_RIGHT_APGA_CTRL ADC3XXX_REG(1, 60) +#define ADC3XXX_LOW_CURRENT_MODES ADC3XXX_REG(1, 61) +#define ADC3XXX_ANALOG_PGA_FLAGS ADC3XXX_REG(1, 62) +/* 63-127 Reserved */ + +/* + * Register bits. + */ + +/* PLL Enable bits */ +#define ADC3XXX_ENABLE_PLL_SHIFT 7 +#define ADC3XXX_ENABLE_PLL (1 << ADC3XXX_ENABLE_PLL_SHIFT) +#define ADC3XXX_ENABLE_NADC_SHIFT 7 +#define ADC3XXX_ENABLE_NADC (1 << ADC3XXX_ENABLE_NADC_SHIFT) +#define ADC3XXX_ENABLE_MADC_SHIFT 7 +#define ADC3XXX_ENABLE_MADC (1 << ADC3XXX_ENABLE_MADC_SHIFT) +#define ADC3XXX_ENABLE_BCLK_SHIFT 7 +#define ADC3XXX_ENABLE_BCLK (1 << ADC3XXX_ENABLE_BCLK_SHIFT) + +/* Power bits */ +#define ADC3XXX_LADC_PWR_ON 0x80 +#define ADC3XXX_RADC_PWR_ON 0x40 + +#define ADC3XXX_SOFT_RESET 0x01 +#define ADC3XXX_BCLK_MASTER 0x08 +#define ADC3XXX_WCLK_MASTER 0x04 + +/* Interface register masks */ +#define ADC3XXX_FORMAT_MASK 0xc0 +#define ADC3XXX_FORMAT_SHIFT 6 +#define ADC3XXX_WLENGTH_MASK 0x30 +#define ADC3XXX_WLENGTH_SHIFT 4 +#define ADC3XXX_CLKDIR_MASK 0x0c +#define ADC3XXX_CLKDIR_SHIFT 2 + +/* Interface register bit patterns */ +#define ADC3XXX_FORMAT_I2S (0 << ADC3XXX_FORMAT_SHIFT) +#define ADC3XXX_FORMAT_DSP (1 << ADC3XXX_FORMAT_SHIFT) +#define ADC3XXX_FORMAT_RJF (2 << ADC3XXX_FORMAT_SHIFT) +#define ADC3XXX_FORMAT_LJF (3 << ADC3XXX_FORMAT_SHIFT) + +#define ADC3XXX_IFACE_16BITS (0 << ADC3XXX_WLENGTH_SHIFT) +#define ADC3XXX_IFACE_20BITS (1 << ADC3XXX_WLENGTH_SHIFT) +#define ADC3XXX_IFACE_24BITS (2 << ADC3XXX_WLENGTH_SHIFT) +#define ADC3XXX_IFACE_32BITS (3 << ADC3XXX_WLENGTH_SHIFT) + +/* PLL P/R bit offsets */ +#define ADC3XXX_PLLP_SHIFT 4 +#define ADC3XXX_PLLR_SHIFT 0 +#define ADC3XXX_PLL_PR_MASK 0x7f +#define ADC3XXX_PLLJ_MASK 0x3f +#define ADC3XXX_PLLD_MSB_MASK 0x3f +#define ADC3XXX_PLLD_LSB_MASK 0xff +#define ADC3XXX_NADC_MASK 0x7f +#define ADC3XXX_MADC_MASK 0x7f +#define ADC3XXX_AOSR_MASK 0xff +#define ADC3XXX_IADC_MASK 0xff +#define ADC3XXX_BDIV_MASK 0x7f + +/* PLL_CLKIN bits */ +#define ADC3XXX_PLL_CLKIN_SHIFT 2 +#define ADC3XXX_PLL_CLKIN_MCLK 0x0 +#define ADC3XXX_PLL_CLKIN_BCLK 0x1 +#define ADC3XXX_PLL_CLKIN_ZERO 0x3 + +/* CODEC_CLKIN bits */ +#define ADC3XXX_CODEC_CLKIN_SHIFT 0 +#define ADC3XXX_CODEC_CLKIN_MCLK 0x0 +#define ADC3XXX_CODEC_CLKIN_BCLK 0x1 +#define ADC3XXX_CODEC_CLKIN_PLL_CLK 0x3 + +#define ADC3XXX_USE_PLL ((ADC3XXX_PLL_CLKIN_MCLK << ADC3XXX_PLL_CLKIN_SHIFT) | \ + (ADC3XXX_CODEC_CLKIN_PLL_CLK << ADC3XXX_CODEC_CLKIN_SHIFT)) +#define ADC3XXX_NO_PLL ((ADC3XXX_PLL_CLKIN_ZERO << ADC3XXX_PLL_CLKIN_SHIFT) | \ + (ADC3XXX_CODEC_CLKIN_MCLK << ADC3XXX_CODEC_CLKIN_SHIFT)) + +/* Analog PGA control bits */ +#define ADC3XXX_LPGA_MUTE 0x80 +#define ADC3XXX_RPGA_MUTE 0x80 + +#define ADC3XXX_LPGA_GAIN_MASK 0x7f +#define ADC3XXX_RPGA_GAIN_MASK 0x7f + +/* ADC current modes */ +#define ADC3XXX_ADC_LOW_CURR_MODE 0x01 + +/* Left ADC Input selection bits */ +#define ADC3XXX_LCH_SEL1_SHIFT 0 +#define ADC3XXX_LCH_SEL2_SHIFT 2 +#define ADC3XXX_LCH_SEL3_SHIFT 4 +#define ADC3XXX_LCH_SEL4_SHIFT 6 + +#define ADC3XXX_LCH_SEL1X_SHIFT 0 +#define ADC3XXX_LCH_SEL2X_SHIFT 2 +#define ADC3XXX_LCH_SEL3X_SHIFT 4 +#define ADC3XXX_LCH_COMMON_MODE 0x40 +#define ADC3XXX_BYPASS_LPGA 0x80 + +/* Right ADC Input selection bits */ +#define ADC3XXX_RCH_SEL1_SHIFT 0 +#define ADC3XXX_RCH_SEL2_SHIFT 2 +#define ADC3XXX_RCH_SEL3_SHIFT 4 +#define ADC3XXX_RCH_SEL4_SHIFT 6 + +#define ADC3XXX_RCH_SEL1X_SHIFT 0 +#define ADC3XXX_RCH_SEL2X_SHIFT 2 +#define ADC3XXX_RCH_SEL3X_SHIFT 4 +#define ADC3XXX_RCH_COMMON_MODE 0x40 +#define ADC3XXX_BYPASS_RPGA 0x80 + +/* MICBIAS control bits */ +#define ADC3XXX_MICBIAS_MASK 0x2 +#define ADC3XXX_MICBIAS1_SHIFT 5 +#define ADC3XXX_MICBIAS2_SHIFT 3 + +#define ADC3XXX_ADC_MAX_VOLUME 64 +#define ADC3XXX_ADC_POS_VOL 24 + +/* GPIO control bits (GPIO1_CTRL and GPIO2_CTRL) */ +#define ADC3XXX_GPIO_CTRL_CFG_MASK 0x3c +#define ADC3XXX_GPIO_CTRL_CFG_SHIFT 2 +#define ADC3XXX_GPIO_CTRL_OUTPUT_CTRL_MASK 0x01 +#define ADC3XXX_GPIO_CTRL_OUTPUT_CTRL_SHIFT 0 +#define ADC3XXX_GPIO_CTRL_INPUT_VALUE_MASK 0x02 +#define ADC3XXX_GPIO_CTRL_INPUT_VALUE_SHIFT 1 + +enum adc3xxx_type { + ADC3001 = 0, + ADC3101 +}; + +struct adc3xxx { + struct device *dev; + enum adc3xxx_type type; + struct clk *mclk; + struct regmap *regmap; + struct gpio_desc *rst_pin; + unsigned int pll_mode; + unsigned int sysclk; + unsigned int gpio_cfg[ADC3XXX_GPIOS_MAX]; /* value+1 (0 => not set) */ + unsigned int micbias_vg[ADC3XXX_MICBIAS_PINS]; + int master; + u8 page_no; + int use_pll; + struct gpio_chip gpio_chip; +}; + +static const unsigned int adc3xxx_gpio_ctrl_reg[ADC3XXX_GPIOS_MAX] = { + ADC3XXX_GPIO1_CTRL, + ADC3XXX_GPIO2_CTRL +}; + +static const unsigned int adc3xxx_micbias_shift[ADC3XXX_MICBIAS_PINS] = { + ADC3XXX_MICBIAS1_SHIFT, + ADC3XXX_MICBIAS2_SHIFT +}; + +static const struct reg_default adc3xxx_defaults[] = { + /* Page 0 */ + { 0, 0x00 }, { 1, 0x00 }, { 2, 0x00 }, { 3, 0x00 }, + { 4, 0x00 }, { 5, 0x11 }, { 6, 0x04 }, { 7, 0x00 }, + { 8, 0x00 }, { 9, 0x00 }, { 10, 0x00 }, { 11, 0x00 }, + { 12, 0x00 }, { 13, 0x00 }, { 14, 0x00 }, { 15, 0x00 }, + { 16, 0x00 }, { 17, 0x00 }, { 18, 0x01 }, { 19, 0x01 }, + { 20, 0x80 }, { 21, 0x80 }, { 22, 0x04 }, { 23, 0x00 }, + { 24, 0x00 }, { 25, 0x00 }, { 26, 0x01 }, { 27, 0x00 }, + { 28, 0x00 }, { 29, 0x02 }, { 30, 0x01 }, { 31, 0x00 }, + { 32, 0x00 }, { 33, 0x10 }, { 34, 0x00 }, { 35, 0x00 }, + { 36, 0x00 }, { 37, 0x00 }, { 38, 0x02 }, { 39, 0x00 }, + { 40, 0x00 }, { 41, 0x00 }, { 42, 0x00 }, { 43, 0x00 }, + { 44, 0x00 }, { 45, 0x00 }, { 46, 0x00 }, { 47, 0x00 }, + { 48, 0x00 }, { 49, 0x00 }, { 50, 0x00 }, { 51, 0x00 }, + { 52, 0x00 }, { 53, 0x12 }, { 54, 0x00 }, { 55, 0x00 }, + { 56, 0x00 }, { 57, 0x00 }, { 58, 0x00 }, { 59, 0x44 }, + { 60, 0x00 }, { 61, 0x01 }, { 62, 0x00 }, { 63, 0x00 }, + { 64, 0x00 }, { 65, 0x00 }, { 66, 0x00 }, { 67, 0x00 }, + { 68, 0x00 }, { 69, 0x00 }, { 70, 0x00 }, { 71, 0x00 }, + { 72, 0x00 }, { 73, 0x00 }, { 74, 0x00 }, { 75, 0x00 }, + { 76, 0x00 }, { 77, 0x00 }, { 78, 0x00 }, { 79, 0x00 }, + { 80, 0x00 }, { 81, 0x00 }, { 82, 0x88 }, { 83, 0x00 }, + { 84, 0x00 }, { 85, 0x00 }, { 86, 0x00 }, { 87, 0x00 }, + { 88, 0x7f }, { 89, 0x00 }, { 90, 0x00 }, { 91, 0x00 }, + { 92, 0x00 }, { 93, 0x00 }, { 94, 0x00 }, { 95, 0x00 }, + { 96, 0x7f }, { 97, 0x00 }, { 98, 0x00 }, { 99, 0x00 }, + { 100, 0x00 }, { 101, 0x00 }, { 102, 0x00 }, { 103, 0x00 }, + { 104, 0x00 }, { 105, 0x00 }, { 106, 0x00 }, { 107, 0x00 }, + { 108, 0x00 }, { 109, 0x00 }, { 110, 0x00 }, { 111, 0x00 }, + { 112, 0x00 }, { 113, 0x00 }, { 114, 0x00 }, { 115, 0x00 }, + { 116, 0x00 }, { 117, 0x00 }, { 118, 0x00 }, { 119, 0x00 }, + { 120, 0x00 }, { 121, 0x00 }, { 122, 0x00 }, { 123, 0x00 }, + { 124, 0x00 }, { 125, 0x00 }, { 126, 0x00 }, { 127, 0x00 }, + + /* Page 1 */ + { 128, 0x00 }, { 129, 0x00 }, { 130, 0x00 }, { 131, 0x00 }, + { 132, 0x00 }, { 133, 0x00 }, { 134, 0x00 }, { 135, 0x00 }, + { 136, 0x00 }, { 137, 0x00 }, { 138, 0x00 }, { 139, 0x00 }, + { 140, 0x00 }, { 141, 0x00 }, { 142, 0x00 }, { 143, 0x00 }, + { 144, 0x00 }, { 145, 0x00 }, { 146, 0x00 }, { 147, 0x00 }, + { 148, 0x00 }, { 149, 0x00 }, { 150, 0x00 }, { 151, 0x00 }, + { 152, 0x00 }, { 153, 0x00 }, { 154, 0x00 }, { 155, 0x00 }, + { 156, 0x00 }, { 157, 0x00 }, { 158, 0x00 }, { 159, 0x00 }, + { 160, 0x00 }, { 161, 0x00 }, { 162, 0x00 }, { 163, 0x00 }, + { 164, 0x00 }, { 165, 0x00 }, { 166, 0x00 }, { 167, 0x00 }, + { 168, 0x00 }, { 169, 0x00 }, { 170, 0x00 }, { 171, 0x00 }, + { 172, 0x00 }, { 173, 0x00 }, { 174, 0x00 }, { 175, 0x00 }, + { 176, 0x00 }, { 177, 0x00 }, { 178, 0x00 }, { 179, 0x00 }, + { 180, 0xff }, { 181, 0x00 }, { 182, 0x3f }, { 183, 0xff }, + { 184, 0x00 }, { 185, 0x3f }, { 186, 0x00 }, { 187, 0x80 }, + { 188, 0x80 }, { 189, 0x00 }, { 190, 0x00 }, { 191, 0x00 }, +}; + +static bool adc3xxx_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ADC3XXX_RESET: + return true; + default: + return false; + } +} + +static const struct regmap_range_cfg adc3xxx_ranges[] = { + { + .range_min = 0, + .range_max = 2 * ADC3XXX_PAGE_SIZE, + .selector_reg = ADC3XXX_PAGE_SELECT, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = ADC3XXX_PAGE_SIZE, + } +}; + +static const struct regmap_config adc3xxx_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .reg_defaults = adc3xxx_defaults, + .num_reg_defaults = ARRAY_SIZE(adc3xxx_defaults), + + .volatile_reg = adc3xxx_volatile_reg, + + .cache_type = REGCACHE_RBTREE, + + .ranges = adc3xxx_ranges, + .num_ranges = ARRAY_SIZE(adc3xxx_ranges), + .max_register = 2 * ADC3XXX_PAGE_SIZE, +}; + +struct adc3xxx_rate_divs { + u32 mclk; + u32 rate; + u8 pll_p; + u8 pll_r; + u8 pll_j; + u16 pll_d; + u8 nadc; + u8 madc; + u8 aosr; +}; + +/* + * PLL and Clock settings. + * If p member is 0, PLL is not used. + * The order of the entries in this table have the PLL entries before + * the non-PLL entries, so that the PLL modes are preferred unless + * the PLL mode setting says otherwise. + */ +static const struct adc3xxx_rate_divs adc3xxx_divs[] = { + /* mclk, rate, p, r, j, d, nadc, madc, aosr */ + /* 8k rate */ + { 12000000, 8000, 1, 1, 7, 1680, 42, 2, 128 }, + { 12288000, 8000, 1, 1, 7, 0000, 42, 2, 128 }, + /* 11.025k rate */ + { 12000000, 11025, 1, 1, 6, 8208, 29, 2, 128 }, + /* 16k rate */ + { 12000000, 16000, 1, 1, 7, 1680, 21, 2, 128 }, + { 12288000, 16000, 1, 1, 7, 0000, 21, 2, 128 }, + /* 22.05k rate */ + { 12000000, 22050, 1, 1, 7, 560, 15, 2, 128 }, + /* 32k rate */ + { 12000000, 32000, 1, 1, 8, 1920, 12, 2, 128 }, + { 12288000, 32000, 1, 1, 8, 0000, 12, 2, 128 }, + /* 44.1k rate */ + { 12000000, 44100, 1, 1, 7, 5264, 8, 2, 128 }, + /* 48k rate */ + { 12000000, 48000, 1, 1, 7, 1680, 7, 2, 128 }, + { 12288000, 48000, 1, 1, 7, 0000, 7, 2, 128 }, + { 24576000, 48000, 1, 1, 3, 5000, 7, 2, 128 }, /* With PLL */ + { 24576000, 48000, 0, 0, 0, 0000, 2, 2, 128 }, /* Without PLL */ + /* 88.2k rate */ + { 12000000, 88200, 1, 1, 7, 5264, 4, 4, 64 }, + /* 96k rate */ + { 12000000, 96000, 1, 1, 8, 1920, 4, 4, 64 }, +}; + +static int adc3xxx_get_divs(struct device *dev, int mclk, int rate, int pll_mode) +{ + int i; + + dev_dbg(dev, "mclk = %d, rate = %d, clock mode %u\n", + mclk, rate, pll_mode); + for (i = 0; i < ARRAY_SIZE(adc3xxx_divs); i++) { + const struct adc3xxx_rate_divs *mode = &adc3xxx_divs[i]; + + /* Skip this entry if it doesn't fulfill the intended clock + * mode requirement. We consider anything besides the two + * modes below to be the same as ADC3XXX_PLL_AUTO. + */ + if ((pll_mode == ADC3XXX_PLL_BYPASS && mode->pll_p) || + (pll_mode == ADC3XXX_PLL_ENABLE && !mode->pll_p)) + continue; + + if (mode->rate == rate && mode->mclk == mclk) + return i; + } + + dev_info(dev, "Master clock rate %d and sample rate %d is not supported\n", + mclk, rate); + return -EINVAL; +} + +static int adc3xxx_pll_delay(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + /* 10msec delay needed after PLL power-up to allow + * PLL and dividers to stabilize (datasheet p13). + */ + usleep_range(10000, 20000); + + return 0; +} + +static const char * const adc_softstepping_text[] = { "1 step", "2 step", "off" }; +static SOC_ENUM_SINGLE_DECL(adc_softstepping_enum, ADC3XXX_ADC_DIGITAL, 0, + adc_softstepping_text); + +static const char * const multiplier_text[] = { "1", "2", "4", "8", "16", "32", "64", "128" }; +static SOC_ENUM_SINGLE_DECL(left_agc_attack_mult_enum, + ADC3XXX_LEFT_CHN_AGC_4, 0, multiplier_text); +static SOC_ENUM_SINGLE_DECL(right_agc_attack_mult_enum, + ADC3XXX_RIGHT_CHN_AGC_4, 0, multiplier_text); +static SOC_ENUM_SINGLE_DECL(left_agc_decay_mult_enum, + ADC3XXX_LEFT_CHN_AGC_5, 0, multiplier_text); +static SOC_ENUM_SINGLE_DECL(right_agc_decay_mult_enum, + ADC3XXX_RIGHT_CHN_AGC_5, 0, multiplier_text); + +static const char * const dither_dc_offset_text[] = { + "0mV", "15mV", "30mV", "45mV", "60mV", "75mV", "90mV", "105mV", + "-15mV", "-30mV", "-45mV", "-60mV", "-75mV", "-90mV", "-105mV" +}; +static const unsigned int dither_dc_offset_values[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15 +}; +static SOC_VALUE_ENUM_DOUBLE_DECL(dither_dc_offset_enum, + ADC3XXX_DITHER_CTRL, + 4, 0, 0xf, dither_dc_offset_text, + dither_dc_offset_values); + +static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 50, 0); +static const DECLARE_TLV_DB_SCALE(adc_tlv, -1200, 50, 0); +static const DECLARE_TLV_DB_SCALE(adc_fine_tlv, -40, 10, 0); +/* AGC target: 8 values: -5.5, -8, -10, -12, -14, -17, -20, -24 dB */ +/* It would be nice to declare these in the order above, but empirically + * TLV_DB_SCALE_ITEM doesn't take lightly to the increment (second) parameter + * being negative, despite there being examples to the contrary in other + * drivers. So declare these in the order from lowest to highest, and + * set the invert flag in the SOC_DOUBLE_R_TLV declaration instead. + */ +static const DECLARE_TLV_DB_RANGE(agc_target_tlv, + 0, 0, TLV_DB_SCALE_ITEM(-2400, 0, 0), + 1, 3, TLV_DB_SCALE_ITEM(-2000, 300, 0), + 4, 6, TLV_DB_SCALE_ITEM(-1200, 200, 0), + 7, 7, TLV_DB_SCALE_ITEM(-550, 0, 0)); +/* Since the 'disabled' value (mute) is at the highest value in the dB + * range (i.e. just before -32 dB) rather than the lowest, we need to resort + * to using a TLV_DB_RANGE in order to get the mute value in the right place. + */ +static const DECLARE_TLV_DB_RANGE(agc_thresh_tlv, + 0, 30, TLV_DB_SCALE_ITEM(-9000, 200, 0), + 31, 31, TLV_DB_SCALE_ITEM(0, 0, 1)); /* disabled = mute */ +/* AGC hysteresis: 4 values: 1, 2, 4 dB, disabled (= mute) */ +static const DECLARE_TLV_DB_RANGE(agc_hysteresis_tlv, + 0, 1, TLV_DB_SCALE_ITEM(100, 100, 0), + 2, 2, TLV_DB_SCALE_ITEM(400, 0, 0), + 3, 3, TLV_DB_SCALE_ITEM(0, 0, 1)); /* disabled = mute */ +static const DECLARE_TLV_DB_SCALE(agc_max_tlv, 0, 50, 0); +/* Input attenuation: -6 dB or 0 dB */ +static const DECLARE_TLV_DB_SCALE(input_attenuation_tlv, -600, 600, 0); + +static const struct snd_kcontrol_new adc3xxx_snd_controls[] = { + SOC_DOUBLE_R_TLV("PGA Capture Volume", ADC3XXX_LEFT_APGA_CTRL, + ADC3XXX_RIGHT_APGA_CTRL, 0, 80, 0, pga_tlv), + SOC_DOUBLE("PGA Capture Switch", ADC3XXX_ADC_FGA, 7, 3, 1, 1), + SOC_DOUBLE_R("AGC Capture Switch", ADC3XXX_LEFT_CHN_AGC_1, + ADC3XXX_RIGHT_CHN_AGC_1, 7, 1, 0), + SOC_DOUBLE_R_TLV("AGC Target Level Capture Volume", ADC3XXX_LEFT_CHN_AGC_1, + ADC3XXX_RIGHT_CHN_AGC_2, 4, 0x07, 1, agc_target_tlv), + SOC_DOUBLE_R_TLV("AGC Noise Threshold Capture Volume", ADC3XXX_LEFT_CHN_AGC_2, + ADC3XXX_RIGHT_CHN_AGC_2, 1, 0x1f, 1, agc_thresh_tlv), + SOC_DOUBLE_R_TLV("AGC Hysteresis Capture Volume", ADC3XXX_LEFT_CHN_AGC_2, + ADC3XXX_RIGHT_CHN_AGC_2, 6, 3, 0, agc_hysteresis_tlv), + SOC_DOUBLE_R("AGC Clip Stepping Capture Switch", ADC3XXX_LEFT_CHN_AGC_2, + ADC3XXX_RIGHT_CHN_AGC_2, 0, 1, 0), + /* + * Oddly enough, the data sheet says the default value + * for the left/right AGC maximum gain register field + * (ADC3XXX_LEFT/RIGHT_CHN_AGC_3 bits 0..6) is 0x7f = 127 + * (verified empirically) even though this value (indeed, above + * 0x50) is specified as 'Reserved. Do not use.' in the accompanying + * table in the data sheet. + */ + SOC_DOUBLE_R_TLV("AGC Maximum Capture Volume", ADC3XXX_LEFT_CHN_AGC_3, + ADC3XXX_RIGHT_CHN_AGC_3, 0, 0x50, 0, agc_max_tlv), + SOC_DOUBLE_R("AGC Attack Time", ADC3XXX_LEFT_CHN_AGC_4, + ADC3XXX_RIGHT_CHN_AGC_4, 3, 0x1f, 0), + /* Would like to have the multipliers as LR pairs, but there is + * no SOC_ENUM_foo which accepts two values in separate registers. + */ + SOC_ENUM("AGC Left Attack Time Multiplier", left_agc_attack_mult_enum), + SOC_ENUM("AGC Right Attack Time Multiplier", right_agc_attack_mult_enum), + SOC_DOUBLE_R("AGC Decay Time", ADC3XXX_LEFT_CHN_AGC_5, + ADC3XXX_RIGHT_CHN_AGC_5, 3, 0x1f, 0), + SOC_ENUM("AGC Left Decay Time Multiplier", left_agc_decay_mult_enum), + SOC_ENUM("AGC Right Decay Time Multiplier", right_agc_decay_mult_enum), + SOC_DOUBLE_R("AGC Noise Debounce", ADC3XXX_LEFT_CHN_AGC_6, + ADC3XXX_RIGHT_CHN_AGC_6, 0, 0x1f, 0), + SOC_DOUBLE_R("AGC Signal Debounce", ADC3XXX_LEFT_CHN_AGC_7, + ADC3XXX_RIGHT_CHN_AGC_7, 0, 0x0f, 0), + /* Read only register */ + SOC_DOUBLE_R_S_TLV("AGC Applied Capture Volume", ADC3XXX_LEFT_AGC_GAIN, + ADC3XXX_RIGHT_AGC_GAIN, 0, -24, 40, 6, 0, adc_tlv), + /* ADC soft stepping */ + SOC_ENUM("ADC Soft Stepping", adc_softstepping_enum), + /* Left/Right Input attenuation */ + SOC_SINGLE_TLV("Left Input IN_1L Capture Volume", + ADC3XXX_LEFT_PGA_SEL_1, 0, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Left Input IN_2L Capture Volume", + ADC3XXX_LEFT_PGA_SEL_1, 2, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Left Input IN_3L Capture Volume", + ADC3XXX_LEFT_PGA_SEL_1, 4, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Left Input IN_1R Capture Volume", + ADC3XXX_LEFT_PGA_SEL_2, 0, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Left Input DIF_2L_3L Capture Volume", + ADC3XXX_LEFT_PGA_SEL_1, 6, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Left Input DIF_1L_1R Capture Volume", + ADC3XXX_LEFT_PGA_SEL_2, 4, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Left Input DIF_2R_3R Capture Volume", + ADC3XXX_LEFT_PGA_SEL_2, 2, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Right Input IN_1R Capture Volume", + ADC3XXX_RIGHT_PGA_SEL_1, 0, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Right Input IN_2R Capture Volume", + ADC3XXX_RIGHT_PGA_SEL_1, 2, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Right Input IN_3R Capture Volume", + ADC3XXX_RIGHT_PGA_SEL_1, 4, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Right Input IN_1L Capture Volume", + ADC3XXX_RIGHT_PGA_SEL_2, 0, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Right Input DIF_2R_3R Capture Volume", + ADC3XXX_RIGHT_PGA_SEL_1, 6, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Right Input DIF_1L_1R Capture Volume", + ADC3XXX_RIGHT_PGA_SEL_2, 4, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Right Input DIF_2L_3L Capture Volume", + ADC3XXX_RIGHT_PGA_SEL_2, 2, 1, 1, input_attenuation_tlv), + SOC_DOUBLE_R_S_TLV("ADC Volume Control Capture Volume", ADC3XXX_LADC_VOL, + ADC3XXX_RADC_VOL, 0, -24, 40, 6, 0, adc_tlv), + /* Empirically, the following doesn't work the way it's supposed + * to. Values 0, -0.1, -0.2 and -0.3 dB result in the same level, and + * -0.4 dB drops about 0.12 dB on a specific chip. + */ + SOC_DOUBLE_TLV("ADC Fine Volume Control Capture Volume", ADC3XXX_ADC_FGA, + 4, 0, 4, 1, adc_fine_tlv), + SOC_SINGLE("Left ADC Unselected CM Bias Capture Switch", + ADC3XXX_LEFT_PGA_SEL_2, 6, 1, 0), + SOC_SINGLE("Right ADC Unselected CM Bias Capture Switch", + ADC3XXX_RIGHT_PGA_SEL_2, 6, 1, 0), + SOC_ENUM("Dither Control DC Offset", dither_dc_offset_enum), +}; + +/* Left input selection, Single Ended inputs and Differential inputs */ +static const struct snd_kcontrol_new left_input_mixer_controls[] = { + SOC_DAPM_SINGLE("IN_1L Capture Switch", + ADC3XXX_LEFT_PGA_SEL_1, 1, 0x1, 1), + SOC_DAPM_SINGLE("IN_2L Capture Switch", + ADC3XXX_LEFT_PGA_SEL_1, 3, 0x1, 1), + SOC_DAPM_SINGLE("IN_3L Capture Switch", + ADC3XXX_LEFT_PGA_SEL_1, 5, 0x1, 1), + SOC_DAPM_SINGLE("DIF_2L_3L Capture Switch", + ADC3XXX_LEFT_PGA_SEL_1, 7, 0x1, 1), + SOC_DAPM_SINGLE("DIF_1L_1R Capture Switch", + ADC3XXX_LEFT_PGA_SEL_2, 5, 0x1, 1), + SOC_DAPM_SINGLE("DIF_2R_3R Capture Switch", + ADC3XXX_LEFT_PGA_SEL_2, 3, 0x1, 1), + SOC_DAPM_SINGLE("IN_1R Capture Switch", + ADC3XXX_LEFT_PGA_SEL_2, 1, 0x1, 1), +}; + +/* Right input selection, Single Ended inputs and Differential inputs */ +static const struct snd_kcontrol_new right_input_mixer_controls[] = { + SOC_DAPM_SINGLE("IN_1R Capture Switch", + ADC3XXX_RIGHT_PGA_SEL_1, 1, 0x1, 1), + SOC_DAPM_SINGLE("IN_2R Capture Switch", + ADC3XXX_RIGHT_PGA_SEL_1, 3, 0x1, 1), + SOC_DAPM_SINGLE("IN_3R Capture Switch", + ADC3XXX_RIGHT_PGA_SEL_1, 5, 0x1, 1), + SOC_DAPM_SINGLE("DIF_2R_3R Capture Switch", + ADC3XXX_RIGHT_PGA_SEL_1, 7, 0x1, 1), + SOC_DAPM_SINGLE("DIF_1L_1R Capture Switch", + ADC3XXX_RIGHT_PGA_SEL_2, 5, 0x1, 1), + SOC_DAPM_SINGLE("DIF_2L_3L Capture Switch", + ADC3XXX_RIGHT_PGA_SEL_2, 3, 0x1, 1), + SOC_DAPM_SINGLE("IN_1L Capture Switch", + ADC3XXX_RIGHT_PGA_SEL_2, 1, 0x1, 1), +}; + +/* Left Digital Mic input for left ADC */ +static const struct snd_kcontrol_new left_input_dmic_controls[] = { + SOC_DAPM_SINGLE("Left ADC Capture Switch", + ADC3XXX_ADC_DIGITAL, 3, 0x1, 0), +}; + +/* Right Digital Mic input for Right ADC */ +static const struct snd_kcontrol_new right_input_dmic_controls[] = { + SOC_DAPM_SINGLE("Right ADC Capture Switch", + ADC3XXX_ADC_DIGITAL, 2, 0x1, 0), +}; + +/* DAPM widgets */ +static const struct snd_soc_dapm_widget adc3xxx_dapm_widgets[] = { + + /* Left Input Selection */ + SND_SOC_DAPM_MIXER("Left Input", SND_SOC_NOPM, 0, 0, + &left_input_mixer_controls[0], + ARRAY_SIZE(left_input_mixer_controls)), + /* Right Input Selection */ + SND_SOC_DAPM_MIXER("Right Input", SND_SOC_NOPM, 0, 0, + &right_input_mixer_controls[0], + ARRAY_SIZE(right_input_mixer_controls)), + /* PGA selection */ + SND_SOC_DAPM_PGA("Left PGA", ADC3XXX_LEFT_APGA_CTRL, 7, 1, NULL, 0), + SND_SOC_DAPM_PGA("Right PGA", ADC3XXX_RIGHT_APGA_CTRL, 7, 1, NULL, 0), + + /* Digital Microphone Input Control for Left/Right ADC */ + SND_SOC_DAPM_MIXER("Left DMic Input", SND_SOC_NOPM, 0, 0, + &left_input_dmic_controls[0], + ARRAY_SIZE(left_input_dmic_controls)), + SND_SOC_DAPM_MIXER("Right DMic Input", SND_SOC_NOPM, 0, 0, + &right_input_dmic_controls[0], + ARRAY_SIZE(right_input_dmic_controls)), + + /* Left/Right ADC */ + SND_SOC_DAPM_ADC("Left ADC", "Left Capture", ADC3XXX_ADC_DIGITAL, 7, 0), + SND_SOC_DAPM_ADC("Right ADC", "Right Capture", ADC3XXX_ADC_DIGITAL, 6, 0), + + /* Inputs */ + SND_SOC_DAPM_INPUT("IN_1L"), + SND_SOC_DAPM_INPUT("IN_1R"), + SND_SOC_DAPM_INPUT("IN_2L"), + SND_SOC_DAPM_INPUT("IN_2R"), + SND_SOC_DAPM_INPUT("IN_3L"), + SND_SOC_DAPM_INPUT("IN_3R"), + SND_SOC_DAPM_INPUT("DIFL_1L_1R"), + SND_SOC_DAPM_INPUT("DIFL_2L_3L"), + SND_SOC_DAPM_INPUT("DIFL_2R_3R"), + SND_SOC_DAPM_INPUT("DIFR_1L_1R"), + SND_SOC_DAPM_INPUT("DIFR_2L_3L"), + SND_SOC_DAPM_INPUT("DIFR_2R_3R"), + SND_SOC_DAPM_INPUT("DMic_L"), + SND_SOC_DAPM_INPUT("DMic_R"), + + /* Digital audio interface output */ + SND_SOC_DAPM_AIF_OUT("AIF_OUT", "Capture", 0, SND_SOC_NOPM, 0, 0), + + /* Clocks */ + SND_SOC_DAPM_SUPPLY("PLL_CLK", ADC3XXX_PLL_PROG_PR, ADC3XXX_ENABLE_PLL_SHIFT, + 0, adc3xxx_pll_delay, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_SUPPLY("ADC_CLK", ADC3XXX_ADC_NADC, ADC3XXX_ENABLE_NADC_SHIFT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC_MOD_CLK", ADC3XXX_ADC_MADC, ADC3XXX_ENABLE_MADC_SHIFT, + 0, NULL, 0), + + /* This refers to the generated BCLK in master mode. */ + SND_SOC_DAPM_SUPPLY("BCLK", ADC3XXX_BCLK_N_DIV, ADC3XXX_ENABLE_BCLK_SHIFT, + 0, NULL, 0), +}; + +static const struct snd_soc_dapm_route adc3xxx_intercon[] = { + /* Left input selection from switches */ + { "Left Input", "IN_1L Capture Switch", "IN_1L" }, + { "Left Input", "IN_2L Capture Switch", "IN_2L" }, + { "Left Input", "IN_3L Capture Switch", "IN_3L" }, + { "Left Input", "DIF_2L_3L Capture Switch", "DIFL_2L_3L" }, + { "Left Input", "DIF_1L_1R Capture Switch", "DIFL_1L_1R" }, + { "Left Input", "DIF_2R_3R Capture Switch", "DIFL_2R_3R" }, + { "Left Input", "IN_1R Capture Switch", "IN_1R" }, + + /* Left input selection to left PGA */ + { "Left PGA", NULL, "Left Input" }, + + /* Left PGA to left ADC */ + { "Left ADC", NULL, "Left PGA" }, + + /* Right input selection from switches */ + { "Right Input", "IN_1R Capture Switch", "IN_1R" }, + { "Right Input", "IN_2R Capture Switch", "IN_2R" }, + { "Right Input", "IN_3R Capture Switch", "IN_3R" }, + { "Right Input", "DIF_2R_3R Capture Switch", "DIFR_2R_3R" }, + { "Right Input", "DIF_1L_1R Capture Switch", "DIFR_1L_1R" }, + { "Right Input", "DIF_2L_3L Capture Switch", "DIFR_2L_3L" }, + { "Right Input", "IN_1L Capture Switch", "IN_1L" }, + + /* Right input selection to right PGA */ + { "Right PGA", NULL, "Right Input" }, + + /* Right PGA to right ADC */ + { "Right ADC", NULL, "Right PGA" }, + + /* Left DMic Input selection from switch */ + { "Left DMic Input", "Left ADC Capture Switch", "DMic_L" }, + + /* Left DMic to left ADC */ + { "Left ADC", NULL, "Left DMic Input" }, + + /* Right DMic Input selection from switch */ + { "Right DMic Input", "Right ADC Capture Switch", "DMic_R" }, + + /* Right DMic to right ADC */ + { "Right ADC", NULL, "Right DMic Input" }, + + /* ADC to AIF output */ + { "AIF_OUT", NULL, "Left ADC" }, + { "AIF_OUT", NULL, "Right ADC" }, + + /* Clocking */ + { "ADC_MOD_CLK", NULL, "ADC_CLK" }, + { "Left ADC", NULL, "ADC_MOD_CLK" }, + { "Right ADC", NULL, "ADC_MOD_CLK" }, + + { "BCLK", NULL, "ADC_CLK" }, +}; + +static const struct snd_soc_dapm_route adc3xxx_pll_intercon[] = { + { "ADC_CLK", NULL, "PLL_CLK" }, +}; + +static const struct snd_soc_dapm_route adc3xxx_bclk_out_intercon[] = { + { "AIF_OUT", NULL, "BCLK" } +}; + +static int adc3xxx_gpio_request(struct gpio_chip *chip, unsigned int offset) +{ + struct adc3xxx *adc3xxx = gpiochip_get_data(chip); + + if (offset >= ADC3XXX_GPIOS_MAX) + return -EINVAL; + + /* GPIO1 is offset 0, GPIO2 is offset 1 */ + /* We check here that the GPIO pins are either not configured in the + * DT, or that they purposely are set as outputs. + * (Input mode not yet implemented). + */ + if (adc3xxx->gpio_cfg[offset] != 0 && + adc3xxx->gpio_cfg[offset] != ADC3XXX_GPIO_GPO + 1) + return -EINVAL; + + return 0; +} + +static int adc3xxx_gpio_direction_out(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct adc3xxx *adc3xxx = gpiochip_get_data(chip); + + /* Set GPIO output function. */ + return regmap_update_bits(adc3xxx->regmap, + adc3xxx_gpio_ctrl_reg[offset], + ADC3XXX_GPIO_CTRL_CFG_MASK | + ADC3XXX_GPIO_CTRL_OUTPUT_CTRL_MASK, + ADC3XXX_GPIO_GPO << ADC3XXX_GPIO_CTRL_CFG_SHIFT | + !!value << ADC3XXX_GPIO_CTRL_OUTPUT_CTRL_SHIFT); +} + +/* With only GPIO outputs configured, we never get the .direction_out call, + * so we set the output mode and output value in the same call. Hence + * .set in practice does the same thing as .direction_out . + */ +static void adc3xxx_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + (void) adc3xxx_gpio_direction_out(chip, offset, value); +} + +/* Even though we only support GPIO output for now, some GPIO clients + * want to read the current pin state using the .get callback. + */ +static int adc3xxx_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct adc3xxx *adc3xxx = gpiochip_get_data(chip); + unsigned int regval; + int ret; + + /* We only allow output pins, so just read the value set in the output + * pin register field. + */ + ret = regmap_read(adc3xxx->regmap, adc3xxx_gpio_ctrl_reg[offset], ®val); + if (ret) + return ret; + return !!(regval & ADC3XXX_GPIO_CTRL_OUTPUT_CTRL_MASK); +} + +static const struct gpio_chip adc3xxx_gpio_chip = { + .label = "adc3xxx", + .owner = THIS_MODULE, + .request = adc3xxx_gpio_request, + .direction_output = adc3xxx_gpio_direction_out, + .set = adc3xxx_gpio_set, + .get = adc3xxx_gpio_get, + .can_sleep = 1, +}; + +static void adc3xxx_free_gpio(struct adc3xxx *adc3xxx) +{ + gpiochip_remove(&adc3xxx->gpio_chip); +} + +static void adc3xxx_init_gpio(struct adc3xxx *adc3xxx) +{ + int gpio, micbias; + int ret; + + adc3xxx->gpio_chip = adc3xxx_gpio_chip; + adc3xxx->gpio_chip.ngpio = ADC3XXX_GPIOS_MAX; + adc3xxx->gpio_chip.parent = adc3xxx->dev; + adc3xxx->gpio_chip.base = -1; + + ret = gpiochip_add_data(&adc3xxx->gpio_chip, adc3xxx); + if (ret) + dev_err(adc3xxx->dev, "Failed to add gpios: %d\n", ret); + + /* Set up potential GPIO configuration from the devicetree. + * This allows us to set up things which are not software + * controllable GPIOs, such as PDM microphone I/O, + */ + for (gpio = 0; gpio < ADC3XXX_GPIOS_MAX; gpio++) { + unsigned int cfg = adc3xxx->gpio_cfg[gpio]; + + if (cfg) { + cfg--; /* actual value to use is stored +1 */ + regmap_update_bits(adc3xxx->regmap, + adc3xxx_gpio_ctrl_reg[gpio], + ADC3XXX_GPIO_CTRL_CFG_MASK, + cfg << ADC3XXX_GPIO_CTRL_CFG_SHIFT); + } + } + + /* Set up micbias voltage */ + for (micbias = 0; micbias < ADC3XXX_MICBIAS_PINS; micbias++) { + unsigned int vg = adc3xxx->micbias_vg[micbias]; + + regmap_update_bits(adc3xxx->regmap, + ADC3XXX_MICBIAS_CTRL, + ADC3XXX_MICBIAS_MASK << adc3xxx_micbias_shift[micbias], + vg << adc3xxx_micbias_shift[micbias]); + } +} + +static int adc3xxx_parse_dt_gpio(struct adc3xxx *adc3xxx, + const char *propname, unsigned int *cfg) +{ + struct device *dev = adc3xxx->dev; + struct device_node *np = dev->of_node; + unsigned int val; + + if (!of_property_read_u32(np, propname, &val)) { + if (val & ~15 || val == 7 || val >= 11) { + dev_err(dev, "Invalid property value for '%s'\n", propname); + return -EINVAL; + } + if (val == ADC3XXX_GPIO_GPI) + dev_warn(dev, "GPIO Input read not yet implemented\n"); + *cfg = val + 1; /* 0 => not set up, all others shifted +1 */ + } + return 0; +} + +static int adc3xxx_parse_dt_micbias(struct adc3xxx *adc3xxx, + const char *propname, unsigned int *vg) +{ + struct device *dev = adc3xxx->dev; + struct device_node *np = dev->of_node; + unsigned int val; + + if (!of_property_read_u32(np, propname, &val)) { + if (val >= ADC3XXX_MICBIAS_AVDD) { + dev_err(dev, "Invalid property value for '%s'\n", propname); + return -EINVAL; + } + *vg = val; + } + return 0; +} + +static int adc3xxx_parse_pll_mode(uint32_t val, unsigned int *pll_mode) +{ + if (val != ADC3XXX_PLL_ENABLE && val != ADC3XXX_PLL_BYPASS && + val != ADC3XXX_PLL_AUTO) + return -EINVAL; + + *pll_mode = val; + + return 0; +} + +static void adc3xxx_setup_pll(struct snd_soc_component *component, + int div_entry) +{ + int i = div_entry; + + /* P & R values */ + snd_soc_component_write(component, ADC3XXX_PLL_PROG_PR, + (adc3xxx_divs[i].pll_p << ADC3XXX_PLLP_SHIFT) | + (adc3xxx_divs[i].pll_r << ADC3XXX_PLLR_SHIFT)); + /* J value */ + snd_soc_component_write(component, ADC3XXX_PLL_PROG_J, + adc3xxx_divs[i].pll_j & ADC3XXX_PLLJ_MASK); + /* D value */ + snd_soc_component_write(component, ADC3XXX_PLL_PROG_D_LSB, + adc3xxx_divs[i].pll_d & ADC3XXX_PLLD_LSB_MASK); + snd_soc_component_write(component, ADC3XXX_PLL_PROG_D_MSB, + (adc3xxx_divs[i].pll_d >> 8) & ADC3XXX_PLLD_MSB_MASK); +} + +static int adc3xxx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(dai->component); + struct adc3xxx *adc3xxx = snd_soc_component_get_drvdata(component); + int i, width = 16; + u8 iface_len, bdiv; + + i = adc3xxx_get_divs(component->dev, adc3xxx->sysclk, + params_rate(params), adc3xxx->pll_mode); + + if (i < 0) + return i; + + /* select data word length */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + iface_len = ADC3XXX_IFACE_16BITS; + width = 16; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + iface_len = ADC3XXX_IFACE_20BITS; + width = 20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + iface_len = ADC3XXX_IFACE_24BITS; + width = 24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + iface_len = ADC3XXX_IFACE_32BITS; + width = 32; + break; + default: + dev_err(component->dev, "Unsupported serial data format\n"); + return -EINVAL; + } + snd_soc_component_update_bits(component, ADC3XXX_INTERFACE_CTRL_1, + ADC3XXX_WLENGTH_MASK, iface_len); + if (adc3xxx_divs[i].pll_p) { /* If PLL used for this mode */ + adc3xxx_setup_pll(component, i); + snd_soc_component_write(component, ADC3XXX_CLKGEN_MUX, ADC3XXX_USE_PLL); + if (!adc3xxx->use_pll) { + snd_soc_dapm_add_routes(dapm, adc3xxx_pll_intercon, + ARRAY_SIZE(adc3xxx_pll_intercon)); + adc3xxx->use_pll = 1; + } + } else { + snd_soc_component_write(component, ADC3XXX_CLKGEN_MUX, ADC3XXX_NO_PLL); + if (adc3xxx->use_pll) { + snd_soc_dapm_del_routes(dapm, adc3xxx_pll_intercon, + ARRAY_SIZE(adc3xxx_pll_intercon)); + adc3xxx->use_pll = 0; + } + } + + /* NADC */ + snd_soc_component_update_bits(component, ADC3XXX_ADC_NADC, + ADC3XXX_NADC_MASK, adc3xxx_divs[i].nadc); + /* MADC */ + snd_soc_component_update_bits(component, ADC3XXX_ADC_MADC, + ADC3XXX_MADC_MASK, adc3xxx_divs[i].madc); + /* AOSR */ + snd_soc_component_update_bits(component, ADC3XXX_ADC_AOSR, + ADC3XXX_AOSR_MASK, adc3xxx_divs[i].aosr); + /* BDIV N Value */ + /* BCLK is (by default) set up to be derived from ADC_CLK */ + bdiv = (adc3xxx_divs[i].aosr * adc3xxx_divs[i].madc) / (2 * width); + snd_soc_component_update_bits(component, ADC3XXX_BCLK_N_DIV, + ADC3XXX_BDIV_MASK, bdiv); + + return 0; +} + +static const char *adc3xxx_pll_mode_text(int pll_mode) +{ + switch (pll_mode) { + case ADC3XXX_PLL_AUTO: + return "PLL auto"; + case ADC3XXX_PLL_ENABLE: + return "PLL enable"; + case ADC3XXX_PLL_BYPASS: + return "PLL bypass"; + default: + break; + } + + return "PLL unknown"; +} + +static int adc3xxx_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct adc3xxx *adc3xxx = snd_soc_component_get_drvdata(component); + int ret; + + ret = adc3xxx_parse_pll_mode(clk_id, &adc3xxx->pll_mode); + if (ret < 0) + return ret; + + adc3xxx->sysclk = freq; + dev_dbg(component->dev, "Set sysclk to %u Hz, %s\n", + freq, adc3xxx_pll_mode_text(adc3xxx->pll_mode)); + return 0; +} + +static int adc3xxx_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct adc3xxx *adc3xxx = snd_soc_component_get_drvdata(component); + u8 clkdir = 0, format = 0; + int master = 0; + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + master = 1; + clkdir = ADC3XXX_BCLK_MASTER | ADC3XXX_WCLK_MASTER; + break; + case SND_SOC_DAIFMT_CBC_CFC: + master = 0; + break; + default: + dev_err(component->dev, "Invalid DAI clock setup\n"); + return -EINVAL; + } + + /* + * match both interface format and signal polarities since they + * are fixed + */ + switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK)) { + case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF: + format = ADC3XXX_FORMAT_I2S; + break; + case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF: + format = ADC3XXX_FORMAT_DSP; + break; + case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF: + format = ADC3XXX_FORMAT_DSP; + break; + case SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_NB_NF: + format = ADC3XXX_FORMAT_RJF; + break; + case SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF: + format = ADC3XXX_FORMAT_LJF; + break; + default: + dev_err(component->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + /* Add/del route enabling BCLK output as applicable */ + if (master && !adc3xxx->master) + snd_soc_dapm_add_routes(dapm, adc3xxx_bclk_out_intercon, + ARRAY_SIZE(adc3xxx_bclk_out_intercon)); + else if (!master && adc3xxx->master) + snd_soc_dapm_del_routes(dapm, adc3xxx_bclk_out_intercon, + ARRAY_SIZE(adc3xxx_bclk_out_intercon)); + adc3xxx->master = master; + + /* set clock direction and format */ + return snd_soc_component_update_bits(component, + ADC3XXX_INTERFACE_CTRL_1, + ADC3XXX_CLKDIR_MASK | ADC3XXX_FORMAT_MASK, + clkdir | format); +} + +static const struct snd_soc_dai_ops adc3xxx_dai_ops = { + .hw_params = adc3xxx_hw_params, + .set_sysclk = adc3xxx_set_dai_sysclk, + .set_fmt = adc3xxx_set_dai_fmt, +}; + +static struct snd_soc_dai_driver adc3xxx_dai = { + .name = "tlv320adc3xxx-hifi", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = ADC3XXX_RATES, + .formats = ADC3XXX_FORMATS, + }, + .ops = &adc3xxx_dai_ops, +}; + +static const struct snd_soc_component_driver soc_component_dev_adc3xxx = { + .controls = adc3xxx_snd_controls, + .num_controls = ARRAY_SIZE(adc3xxx_snd_controls), + .dapm_widgets = adc3xxx_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(adc3xxx_dapm_widgets), + .dapm_routes = adc3xxx_intercon, + .num_dapm_routes = ARRAY_SIZE(adc3xxx_intercon), +}; + +static int adc3xxx_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct device *dev = &i2c->dev; + struct adc3xxx *adc3xxx = NULL; + int ret; + + adc3xxx = devm_kzalloc(dev, sizeof(struct adc3xxx), GFP_KERNEL); + if (!adc3xxx) + return -ENOMEM; + adc3xxx->dev = dev; + + adc3xxx->rst_pin = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(adc3xxx->rst_pin)) { + return dev_err_probe(dev, PTR_ERR(adc3xxx->rst_pin), + "Failed to request rst_pin\n"); + } + + adc3xxx->mclk = devm_clk_get(dev, NULL); + if (IS_ERR(adc3xxx->mclk)) { + /* + * The chip itself supports running off the BCLK either + * directly or via the PLL, but the driver does not (yet), so + * having a specified mclk is required. Otherwise, we could + * use the lack of a clocks property to indicate when BCLK is + * intended as the clock source. + */ + return dev_err_probe(dev, PTR_ERR(adc3xxx->mclk), + "Failed to acquire MCLK\n"); + } else if (adc3xxx->mclk) { + ret = clk_prepare_enable(adc3xxx->mclk); + if (ret < 0) + return ret; + dev_dbg(dev, "Enabled MCLK, freq %lu Hz\n", clk_get_rate(adc3xxx->mclk)); + } + + ret = adc3xxx_parse_dt_gpio(adc3xxx, "ti,dmdin-gpio1", &adc3xxx->gpio_cfg[0]); + if (ret < 0) + return ret; + ret = adc3xxx_parse_dt_gpio(adc3xxx, "ti,dmclk-gpio2", &adc3xxx->gpio_cfg[1]); + if (ret < 0) + return ret; + ret = adc3xxx_parse_dt_micbias(adc3xxx, "ti,micbias1-vg", &adc3xxx->micbias_vg[0]); + if (ret < 0) + return ret; + ret = adc3xxx_parse_dt_micbias(adc3xxx, "ti,micbias2-vg", &adc3xxx->micbias_vg[1]); + if (ret < 0) + return ret; + + adc3xxx->regmap = devm_regmap_init_i2c(i2c, &adc3xxx_regmap); + if (IS_ERR(adc3xxx->regmap)) { + ret = PTR_ERR(adc3xxx->regmap); + return ret; + } + + i2c_set_clientdata(i2c, adc3xxx); + + adc3xxx->type = id->driver_data; + + /* Reset codec chip */ + gpiod_set_value_cansleep(adc3xxx->rst_pin, 1); + usleep_range(2000, 100000); /* Requirement: > 10 ns (datasheet p13) */ + gpiod_set_value_cansleep(adc3xxx->rst_pin, 0); + + /* Potentially set up pins used as GPIOs */ + adc3xxx_init_gpio(adc3xxx); + + ret = snd_soc_register_component(dev, + &soc_component_dev_adc3xxx, &adc3xxx_dai, 1); + if (ret < 0) + dev_err(dev, "Failed to register codec: %d\n", ret); + + return ret; +} + +static int __exit adc3xxx_i2c_remove(struct i2c_client *client) +{ + struct adc3xxx *adc3xxx = i2c_get_clientdata(client); + + if (adc3xxx->mclk) + clk_disable_unprepare(adc3xxx->mclk); + adc3xxx_free_gpio(adc3xxx); + snd_soc_unregister_component(&client->dev); + return 0; +} + +static const struct of_device_id tlv320adc3xxx_of_match[] = { + { .compatible = "ti,tlv320adc3001", }, + { .compatible = "ti,tlv320adc3101", }, + {}, +}; +MODULE_DEVICE_TABLE(of, tlv320adc3xxx_of_match); + +static const struct i2c_device_id adc3xxx_i2c_id[] = { + { "tlv320adc3001", ADC3001 }, + { "tlv320adc3101", ADC3101 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, adc3xxx_i2c_id); + +static struct i2c_driver adc3xxx_i2c_driver = { + .driver = { + .name = "tlv320adc3xxx-codec", + .of_match_table = tlv320adc3xxx_of_match, + }, + .probe = adc3xxx_i2c_probe, + .remove = adc3xxx_i2c_remove, + .id_table = adc3xxx_i2c_id, +}; + +module_i2c_driver(adc3xxx_i2c_driver); + +MODULE_DESCRIPTION("ASoC TLV320ADC3xxx codec driver"); +MODULE_AUTHOR("shahina.s@mistralsolutions.com"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 98bf33ca3f00d76659aa1be1586a433efa74d34e Mon Sep 17 00:00:00 2001 From: Tzung-Bi Shih Date: Mon, 20 Dec 2021 17:34:07 +0800 Subject: ASoC: mediatek: mt8195-mt6359: reduce log verbosity in probe() Eliminates error messages if snd_soc_register_card() failed. Kernel emits messages if device probe error anyway. This is mainly for removing the following error messages during boot. >>> snd_soc_register_card fail -517 Signed-off-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20211220093408.207206-1-tzungbi@google.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c | 2 -- sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c | 2 -- 2 files changed, 4 deletions(-) diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c index 5cdbfaafd479..9cf907c49ea8 100644 --- a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c +++ b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c @@ -1114,8 +1114,6 @@ static int mt8195_mt6359_rt1011_rt5682_dev_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { - dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", - __func__, ret); of_node_put(priv->hdmi_node); of_node_put(priv->dp_node); of_node_put(priv->platform_node); diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c index fa50a31e9718..fdd444138728 100644 --- a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c +++ b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c @@ -1358,8 +1358,6 @@ static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { - dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", - __func__, ret); of_node_put(priv->hdmi_node); of_node_put(priv->dp_node); of_node_put(priv->platform_node); -- cgit v1.2.3 From f487201343312faa697ac40124085a834e0e26d8 Mon Sep 17 00:00:00 2001 From: Ajit Kumar Pandey Date: Tue, 21 Dec 2021 21:48:08 +0530 Subject: ASoC: amd: acp-config: Enable SOF audio for Google chrome boards. We need to support sof audio on different variants of Google boards. Add new entry in dmi table to enable SOF flag on Google chrome boards. Also add newer machines to sof_machines list with codecs and amps acpi id check to register sof sound cards on different variants. Signed-off-by: Ajit Kumar Pandey Reviewed-by: Pierre-Louis Bossart Reviewed-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211221161814.236318-2-AjitKumar.Pandey@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp-config.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c index 1493d52c9290..c0bbcdb1761d 100644 --- a/sound/soc/amd/acp-config.c +++ b/sound/soc/amd/acp-config.c @@ -35,6 +35,18 @@ static const struct config_entry config_table[] = { {} }, }, + { + .flags = FLAG_AMD_SOF, + .device = ACP_PCI_DEV_ID, + .dmi_table = (const struct dmi_system_id []) { + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + }, + }, + {} + }, + }, }; int snd_amd_acp_find_config(struct pci_dev *pci) @@ -43,6 +55,10 @@ int snd_amd_acp_find_config(struct pci_dev *pci) u16 device = pci->device; int i; + /* Do not enable FLAGS on older platforms with Rev id zero */ + if (!pci->revision) + return 0; + for (i = 0; i < ARRAY_SIZE(config_table); i++, table++) { if (table->device != device) continue; @@ -56,7 +72,44 @@ int snd_amd_acp_find_config(struct pci_dev *pci) } EXPORT_SYMBOL(snd_amd_acp_find_config); +static struct snd_soc_acpi_codecs amp_rt1019 = { + .num_codecs = 1, + .codecs = {"10EC1019"} +}; + +static struct snd_soc_acpi_codecs amp_max = { + .num_codecs = 1, + .codecs = {"MX98360A"} +}; + struct snd_soc_acpi_mach snd_soc_acpi_amd_sof_machines[] = { + { + .id = "10EC5682", + .drv_name = "rt5682-rt1019", + .pdata = (void *)&acp_quirk_data, + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &_rt1019, + .fw_filename = "sof-rn.ri", + .sof_tplg_filename = "sof-acp.tplg", + }, + { + .id = "10EC5682", + .drv_name = "rt5682-max", + .pdata = (void *)&acp_quirk_data, + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &_max, + .fw_filename = "sof-rn.ri", + .sof_tplg_filename = "sof-acp.tplg", + }, + { + .id = "RTL5682", + .drv_name = "rt5682s-max", + .pdata = (void *)&acp_quirk_data, + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &_max, + .fw_filename = "sof-rn.ri", + .sof_tplg_filename = "sof-acp.tplg", + }, { .id = "AMDI1019", .drv_name = "renoir-dsp", -- cgit v1.2.3 From 0082e3299a49286a7761f4d237530b07c00676fb Mon Sep 17 00:00:00 2001 From: Ajit Kumar Pandey Date: Tue, 21 Dec 2021 21:48:09 +0530 Subject: ASoC: amd: acp-config: Update sof_tplg_filename for SOF machines SOF machines support different codec end points and hence required different topologies configuration. Update tplg filename in machine struct to load different topology files for SOF machines. Signed-off-by: Ajit Kumar Pandey Reviewed-by: Curtis Malainey Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20211221161814.236318-3-AjitKumar.Pandey@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp-config.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c index c0bbcdb1761d..c9e1c08364f3 100644 --- a/sound/soc/amd/acp-config.c +++ b/sound/soc/amd/acp-config.c @@ -90,7 +90,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_sof_machines[] = { .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &_rt1019, .fw_filename = "sof-rn.ri", - .sof_tplg_filename = "sof-acp.tplg", + .sof_tplg_filename = "sof-rn-rt5682-rt1019.tplg", }, { .id = "10EC5682", @@ -99,7 +99,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_sof_machines[] = { .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &_max, .fw_filename = "sof-rn.ri", - .sof_tplg_filename = "sof-acp.tplg", + .sof_tplg_filename = "sof-rn-rt5682-max98360.tplg", }, { .id = "RTL5682", @@ -108,7 +108,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_sof_machines[] = { .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &_max, .fw_filename = "sof-rn.ri", - .sof_tplg_filename = "sof-acp.tplg", + .sof_tplg_filename = "sof-rn-rt5682-max98360.tplg", }, { .id = "AMDI1019", -- cgit v1.2.3 From 3bf4fb25d5c2455396a1decd43f5e6b775f0b377 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Thu, 16 Dec 2021 19:02:29 +0300 Subject: ASoC: tegra-audio-rt5677: Correct example Remove non-existent properties from the example of the binding. These properties were borrower from the old txt binding, but they were never used in practice and aren't documented in the new binding. They aren't reported by the binding checker because dtschema needs extra patch that hasn't been upstreamed yet to make unevaluatedProperties work properly. Signed-off-by: Dmitry Osipenko Acked-by: Rob Herring Link: https://lore.kernel.org/r/20211216160229.17049-1-digetx@gmail.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5677.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5677.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5677.yaml index 03ff691c26c8..a49997d6028b 100644 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5677.yaml +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5677.yaml @@ -92,9 +92,6 @@ examples: nvidia,audio-codec = <&rt5677>; nvidia,hp-det-gpios = <&gpio 143 0>; - nvidia,mic-present-gpios = <&gpio 132 1>; - nvidia,hp-en-gpios = <&rt5677 1 0>; - nvidia,dmic-clk-en-gpios = <&rt5677 2 1>; clocks = <&clk 216>, <&clk 217>, -- cgit v1.2.3 From 2dc643cd756398c3013fcc2d3c2a07c9c4a0a3bd Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 21 Dec 2021 22:27:57 +0530 Subject: ASoC: SOF: AMD: simplify return status handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cppcheck warning: sound/soc/sof/amd/acp.c:222:9: warning: Identical condition and return expression 'ret', return value is always 0 [identicalConditionAfterEarlyExit] return ret; ^ sound/soc/sof/amd/acp.c:213:6: note: If condition 'ret' is true, the function will return/exit if (ret) ^ sound/soc/sof/amd/acp.c:222:9: note: Returning identical expression 'ret' return ret; ^ Just return 0; on success. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Signed-off-by: Ajit Kumar Pandey Link: https://lore.kernel.org/r/20211221165802.236843-1-AjitKumar.Pandey@amd.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index 4c5550e8d364..fe9b7dc5bc86 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -219,7 +219,7 @@ int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr, return -EINVAL; } - return ret; + return 0; } int acp_dma_status(struct acp_dev_data *adata, unsigned char ch) -- cgit v1.2.3 From ac1e6bc146d45e15f0a5c0908338f918f6261388 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 17 Dec 2021 18:00:07 +0300 Subject: ASoC: qdsp6: fix a use after free bug in open() This code frees "graph" and then dereferences to save the error code. Save the error code first and then use gotos to unwind the allocation. Fixes: 59716aa3f976 ("ASoC: qdsp6: Fix an IS_ERR() vs NULL bug") Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/20211217150007.GB16611@kili Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6apm.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 3e007d609a9b..f424d7aa389a 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -615,7 +615,7 @@ struct q6apm_graph *q6apm_graph_open(struct device *dev, q6apm_cb cb, graph = kzalloc(sizeof(*graph), GFP_KERNEL); if (!graph) { ret = -ENOMEM; - goto err; + goto put_ar_graph; } graph->apm = apm; @@ -631,13 +631,15 @@ struct q6apm_graph *q6apm_graph_open(struct device *dev, q6apm_cb cb, graph->port = gpr_alloc_port(apm->gdev, dev, graph_callback, graph); if (IS_ERR(graph->port)) { - kfree(graph); ret = PTR_ERR(graph->port); - goto err; + goto free_graph; } return graph; -err: + +free_graph: + kfree(graph); +put_ar_graph: kref_put(&ar_graph->refcount, q6apm_put_audioreach_graph); return ERR_PTR(ret); } -- cgit v1.2.3 From 15443f6cab25762272312373226d3fd2a742404f Mon Sep 17 00:00:00 2001 From: Ajit Kumar Pandey Date: Tue, 21 Dec 2021 22:49:10 +0530 Subject: ASoC: amd: acp: Remove duplicate dependency in Kconfig Remove duplicate depends on statement in Kconfig file. Signed-off-by: Ajit Kumar Pandey Link: https://lore.kernel.org/r/20211221171912.237792-1-AjitKumar.Pandey@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/Kconfig | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig index 52a1371f9e61..154be5e70821 100644 --- a/sound/soc/amd/acp/Kconfig +++ b/sound/soc/amd/acp/Kconfig @@ -46,7 +46,6 @@ config SND_SOC_AMD_LEGACY_MACH tristate "AMD Legacy Machine Driver Support" depends on X86 && PCI && I2C select SND_SOC_AMD_MACH_COMMON - depends on X86 && PCI && I2C help This option enables legacy sound card support for ACP audio. @@ -54,7 +53,6 @@ config SND_SOC_AMD_SOF_MACH tristate "AMD SOF Machine Driver Support" depends on X86 && PCI && I2C select SND_SOC_AMD_MACH_COMMON - depends on X86 && PCI && I2C help This option enables SOF sound card support for ACP audio. -- cgit v1.2.3 From c2efaf8f2d53ffa2ecc487e21c62d13bbb8d88c3 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 21 Dec 2021 17:00:59 +0000 Subject: ASoC: xlnx: Use platform_get_irq() to get the interrupt platform_get_resource(pdev, IORESOURCE_IRQ, ..) relies on static allocation of IRQ resources in DT core code, this causes an issue when using hierarchical interrupt domains using "interrupts" property in the node as this bypasses the hierarchical setup and messes up the irq chaining. In preparation for removal of static setup of IRQ resource from DT core code use platform_get_irq(). Signed-off-by: Lad Prabhakar Link: https://lore.kernel.org/r/20211221170100.27423-2-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Mark Brown --- sound/soc/xilinx/xlnx_spdif.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/sound/soc/xilinx/xlnx_spdif.c b/sound/soc/xilinx/xlnx_spdif.c index e2ca087adee6..cba0e868a7d7 100644 --- a/sound/soc/xilinx/xlnx_spdif.c +++ b/sound/soc/xilinx/xlnx_spdif.c @@ -237,7 +237,6 @@ MODULE_DEVICE_TABLE(of, xlnx_spdif_of_match); static int xlnx_spdif_probe(struct platform_device *pdev) { int ret; - struct resource *res; struct snd_soc_dai_driver *dai_drv; struct spdif_dev_data *ctx; @@ -273,13 +272,10 @@ static int xlnx_spdif_probe(struct platform_device *pdev) if (ctx->mode) { dai_drv = &xlnx_spdif_tx_dai; } else { - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) { - dev_err(dev, "No IRQ resource found\n"); - ret = -ENODEV; + ret = platform_get_irq(pdev, 0); + if (ret < 0) goto clk_err; - } - ret = devm_request_irq(dev, res->start, + ret = devm_request_irq(dev, ret, xlnx_spdifrx_irq_handler, 0, "XLNX_SPDIF_RX", ctx); if (ret) { -- cgit v1.2.3 From 5de035c270047e7ae754fbfb69031707aa5b54f7 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 21 Dec 2021 17:01:00 +0000 Subject: ASoC: bcm: Use platform_get_irq() to get the interrupt platform_get_resource(pdev, IORESOURCE_IRQ, ..) relies on static allocation of IRQ resources in DT core code, this causes an issue when using hierarchical interrupt domains using "interrupts" property in the node as this bypasses the hierarchical setup and messes up the irq chaining. In preparation for removal of static setup of IRQ resource from DT core code use platform_get_irq(). While at it also drop "r_irq" member from struct bcm_i2s_priv as there are no users of it. Signed-off-by: Lad Prabhakar Reviewed-by: Florian Fainelli Link: https://lore.kernel.org/r/20211221170100.27423-3-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Mark Brown --- sound/soc/bcm/bcm63xx-i2s.h | 1 - sound/soc/bcm/bcm63xx-pcm-whistler.c | 13 ++++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/sound/soc/bcm/bcm63xx-i2s.h b/sound/soc/bcm/bcm63xx-i2s.h index edc328ba53d3..f30556bec89e 100644 --- a/sound/soc/bcm/bcm63xx-i2s.h +++ b/sound/soc/bcm/bcm63xx-i2s.h @@ -74,7 +74,6 @@ struct bcm_i2s_priv { struct device *dev; - struct resource *r_irq; struct regmap *regmap_i2s; struct clk *i2s_clk; struct snd_pcm_substream *play_substream; diff --git a/sound/soc/bcm/bcm63xx-pcm-whistler.c b/sound/soc/bcm/bcm63xx-pcm-whistler.c index b5096f64c576..2c600b017524 100644 --- a/sound/soc/bcm/bcm63xx-pcm-whistler.c +++ b/sound/soc/bcm/bcm63xx-pcm-whistler.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -387,14 +388,12 @@ int bcm63xx_soc_platform_probe(struct platform_device *pdev, { int ret; - i2s_priv->r_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!i2s_priv->r_irq) { - dev_err(&pdev->dev, "Unable to get register irq resource.\n"); - return -ENODEV; - } + ret = platform_get_irq(pdev, 0); + if (ret < 0) + return ret; - ret = devm_request_irq(&pdev->dev, i2s_priv->r_irq->start, i2s_dma_isr, - i2s_priv->r_irq->flags, "i2s_dma", (void *)i2s_priv); + ret = devm_request_irq(&pdev->dev, ret, i2s_dma_isr, + irq_get_trigger_type(ret), "i2s_dma", (void *)i2s_priv); if (ret) { dev_err(&pdev->dev, "i2s_init: failed to request interrupt.ret=%d\n", ret); -- cgit v1.2.3 From 70ba14cf6dfd7ebd1275562bb9637b8d0ddb8f49 Mon Sep 17 00:00:00 2001 From: Vincent Knecht Date: Mon, 20 Dec 2021 20:37:24 +0100 Subject: ASoC: dt-bindings: codecs: Add bindings for ak4375 AK4375 is an audio DAC with headphones amplifier controlled via I2C. Add simple device tree bindings that describe how to set it up. Reviewed-by: Rob Herring Signed-off-by: Vincent Knecht Link: https://lore.kernel.org/r/20211220193725.2650356-1-vincent.knecht@mailoo.org Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/ak4375.yaml | 57 ++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/ak4375.yaml diff --git a/Documentation/devicetree/bindings/sound/ak4375.yaml b/Documentation/devicetree/bindings/sound/ak4375.yaml new file mode 100644 index 000000000000..f1d5074a024d --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ak4375.yaml @@ -0,0 +1,57 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/ak4375.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: AK4375 DAC and headphones amplifier Device Tree Bindings + +maintainers: + - Vincent Knecht + +properties: + compatible: + const: asahi-kasei,ak4375 + + reg: + maxItems: 1 + + '#sound-dai-cells': + const: 0 + + avdd-supply: + description: regulator phandle for the AVDD power supply. + + tvdd-supply: + description: regulator phandle for the TVDD power supply. + + pdn-gpios: + description: optional GPIO to set the PDN pin. + +required: + - compatible + - reg + - '#sound-dai-cells' + - avdd-supply + - tvdd-supply + +additionalProperties: false + +examples: + - | + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + + headphones: audio-codec@10 { + compatible = "asahi-kasei,ak4375"; + reg = <0x10>; + avdd-supply = <®_headphones_avdd>; + tvdd-supply = <&pm8916_l6>; + pdn-gpios = <&msmgpio 114 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&headphones_pdn_default>; + #sound-dai-cells = <0>; + }; + }; -- cgit v1.2.3 From 53778b8292b5492ec3ecf1efb84163eac2a6e422 Mon Sep 17 00:00:00 2001 From: Vincent Knecht Date: Mon, 20 Dec 2021 20:37:25 +0100 Subject: ASoC: Add AK4375 support AK4375 is a 32-bit stereo DAC with headphones amplifier. There's no documentation for it on akm.com, and only a brief datasheet can be found floating on the internets [1]. Thanks to Oriane BAYERD for finally answering my inquiries through akm.com, if only to tell me that this chip is EOL following AKM factory burning in october 2020 and thus no detailed documentation is available anymore... AK4331 is advertised [2] as pin and register compatible with AK4375 so some scraps of its datasheet were used and this driver might be used as a base for it, but this is totally untested. So this driver is mainly based on downstream code [3] and [4] by Hu Jin from AKM (no known email). Tested on msm8916-alcatel-idol347 and msm8939-alcatel-idol3, which both use PLL driven clock with bypass of SRC (sample rate converter), so only this setup is supported for now. [1] https://datasheetspdf.com/pdf-file/1400317/AKM/AK4375A/1 [2] https://www.akm.com/content/dam/documents/products/audio/audio-dac/ak4331ecb/ak4331ecb-en-datasheet.pdf [3] https://github.com/msm8916-mainline/android_kernel_qcom_msm8916/blob/alcatel-idol347/sound/soc/codecs/idol347/ak4375.c [4] https://github.com/msm8916-mainline/android_kernel_qcom_msm8916/blob/alcatel-idol347/sound/soc/codecs/ak4375.c Signed-off-by: Vincent Knecht Link: https://lore.kernel.org/r/20211220193725.2650356-2-vincent.knecht@mailoo.org Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 11 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/ak4375.c | 619 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 632 insertions(+) create mode 100644 sound/soc/codecs/ak4375.c diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 22836ca9b478..5fe9ec924864 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -41,6 +41,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_ADS117X imply SND_SOC_AK4104 imply SND_SOC_AK4118 + imply SND_SOC_AK4375 imply SND_SOC_AK4458 imply SND_SOC_AK4535 imply SND_SOC_AK4554 @@ -523,6 +524,16 @@ config SND_SOC_AK4118 depends on I2C select REGMAP_I2C +config SND_SOC_AK4375 + tristate "AKM AK4375 CODEC" + depends on I2C + select REGMAP_I2C + help + Enable support for the Asahi-Kasei AK4375 codec. + + To compile this driver as a module, choose M here: the module + will be called snd-soc-ak4375. + config SND_SOC_AK4458 tristate "AKM AK4458 CODEC" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 24bc6b34ba2f..8dbdf3518bda 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -34,6 +34,7 @@ snd-soc-adav803-objs := adav803.o snd-soc-ads117x-objs := ads117x.o snd-soc-ak4104-objs := ak4104.o snd-soc-ak4118-objs := ak4118.o +snd-soc-ak4375-objs := ak4375.o snd-soc-ak4458-objs := ak4458.o snd-soc-ak4535-objs := ak4535.o snd-soc-ak4554-objs := ak4554.o @@ -371,6 +372,7 @@ obj-$(CONFIG_SND_SOC_ADAV803) += snd-soc-adav803.o obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o obj-$(CONFIG_SND_SOC_AK4118) += snd-soc-ak4118.o +obj-$(CONFIG_SND_SOC_AK4375) += snd-soc-ak4375.o obj-$(CONFIG_SND_SOC_AK4458) += snd-soc-ak4458.o obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o obj-$(CONFIG_SND_SOC_AK4554) += snd-soc-ak4554.o diff --git a/sound/soc/codecs/ak4375.c b/sound/soc/codecs/ak4375.c new file mode 100644 index 000000000000..a893aff42a01 --- /dev/null +++ b/sound/soc/codecs/ak4375.c @@ -0,0 +1,619 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/* + * Based on code by Hu Jin + * Copyright (C) 2014 Asahi Kasei Microdevices Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Registers and fields */ +#define AK4375_00_POWER_MANAGEMENT1 0x00 +#define PMPLL BIT(0) /* 0: PLL off, 1: PLL on */ +#define AK4375_01_POWER_MANAGEMENT2 0x01 +#define PMCP1 BIT(0) /* Charge Pump 1: LDO1 and DAC */ +#define PMCP2 BIT(1) /* Charge Pump 2: Class-G HP Amp */ +#define PMLDO1P BIT(4) +#define PMLDO1N BIT(5) +#define PMLDO (PMLDO1P | PMLDO1N) +#define AK4375_02_POWER_MANAGEMENT3 0x02 +#define AK4375_03_POWER_MANAGEMENT4 0x03 +#define AK4375_04_OUTPUT_MODE_SETTING 0x04 +#define AK4375_05_CLOCK_MODE_SELECT 0x05 +#define FS_MASK GENMASK(4, 0) +#define FS_8KHZ 0x00 +#define FS_11_025KHZ 0x01 +#define FS_16KHZ 0x04 +#define FS_22_05KHZ 0x05 +#define FS_32KHZ 0x08 +#define FS_44_1KHZ 0x09 +#define FS_48KHZ 0x0a +#define FS_88_2KHZ 0x0d +#define FS_96KHZ 0x0e +#define FS_176_4KHZ 0x11 +#define FS_192KHZ 0x12 +#define CM_MASK GENMASK(6, 5) /* For SRC Bypass mode */ +#define CM_0 (0x0 << 5) +#define CM_1 (0x1 << 5) +#define CM_2 (0x2 << 5) +#define CM_3 (0x3 << 5) +#define AK4375_06_DIGITAL_FILTER_SELECT 0x06 +#define DADFSEL BIT(5) /* 0: in SRC Bypass mode, 1: in SRC mode */ +#define DASL BIT(6) +#define DASD BIT(7) +#define AK4375_07_DAC_MONO_MIXING 0x07 +#define DACMUTE_MASK (GENMASK(5, 4) | GENMASK(1, 0)) /* Clear to mute */ +#define AK4375_08_JITTER_CLEANER_SETTING1 0x08 +#define AK4375_09_JITTER_CLEANER_SETTING2 0x09 +#define AK4375_0A_JITTER_CLEANER_SETTING3 0x0a +#define SELDAIN BIT(1) /* 0: SRC Bypass mode, 1: SRC mode */ +#define XCKSEL BIT(6) /* 0: PLL0, 1: MCKI */ +#define XCKCPSEL BIT(7) /* Should be equal to SELDAIN and XCKSEL */ +#define AK4375_0B_LCH_OUTPUT_VOLUME 0x0b +#define AK4375_0C_RCH_OUTPUT_VOLUME 0x0c +#define AK4375_0D_HP_VOLUME_CONTROL 0x0d +#define AK4375_0E_PLL_CLK_SOURCE_SELECT 0x0e +#define PLS BIT(0) /* 0: MCKI, 1: BCLK */ +#define AK4375_0F_PLL_REF_CLK_DIVIDER1 0x0f /* Reference clock divider [15:8] bits */ +#define AK4375_10_PLL_REF_CLK_DIVIDER2 0x10 /* Reference clock divider [7:0] bis */ +#define AK4375_11_PLL_FB_CLK_DIVIDER1 0x11 /* Feedback clock divider [15:8] bits */ +#define AK4375_12_PLL_FB_CLK_DIVIDER2 0x12 /* Feedback clock divider [7:0] bits */ +#define AK4375_13_SRC_CLK_SOURCE 0x13 /* SRC Bypass: SRCCKS=XCKSEL=SELDAIN=0 */ +#define SRCCKS BIT(0) /* SRC Clock source 0: MCKI, 1: PLL0 */ +#define DIV BIT(4) +#define AK4375_14_DAC_CLK_DIVIDER 0x14 +#define AK4375_15_AUDIO_IF_FORMAT 0x15 +#define DEVICEID_MASK GENMASK(7, 5) +#define AK4375_24_MODE_CONTROL 0x24 + +#define AK4375_PLL_FREQ_OUT_112896000 112896000 /* 44.1 kHz base rate */ +#define AK4375_PLL_FREQ_OUT_122880000 122880000 /* 32 and 48 kHz base rates */ + +#define DEVICEID_AK4375 0x00 +#define DEVICEID_AK4375A 0x01 +#define DEVICEID_AK4376A 0x02 +#define DEVICEID_AK4377 0x03 +#define DEVICEID_AK4331 0x07 + +static const char * const supply_names[] = { + "avdd", "tvdd" +}; + +struct ak4375_drvdata { + struct snd_soc_dai_driver *dai_drv; + const struct snd_soc_component_driver *comp_drv; +}; + +struct ak4375_priv { + struct device *dev; + struct regmap *regmap; + struct gpio_desc *pdn_gpiod; + struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; + unsigned int rate; + unsigned int pld; + u8 mute_save; +}; + +static const struct reg_default ak4375_reg_defaults[] = { + { 0x00, 0x00 }, { 0x01, 0x00 }, { 0x02, 0x00 }, + { 0x03, 0x00 }, { 0x04, 0x00 }, { 0x05, 0x00 }, + { 0x06, 0x00 }, { 0x07, 0x00 }, { 0x08, 0x00 }, + { 0x09, 0x00 }, { 0x0a, 0x00 }, { 0x0b, 0x19 }, + { 0x0c, 0x19 }, { 0x0d, 0x75 }, { 0x0e, 0x01 }, + { 0x0f, 0x00 }, { 0x10, 0x00 }, { 0x11, 0x00 }, + { 0x12, 0x00 }, { 0x13, 0x00 }, { 0x14, 0x00 }, + { 0x15, 0x00 }, { 0x24, 0x00 }, +}; + +/* + * Output Digital volume control: + * from -12.5 to 3 dB in 0.5 dB steps (mute instead of -12.5 dB) + */ +static DECLARE_TLV_DB_SCALE(dac_tlv, -1250, 50, 0); + +/* + * HP-Amp Analog volume control: + * from -4.2 to 6 dB in 2 dB steps (mute instead of -4.2 dB) + */ +static DECLARE_TLV_DB_SCALE(hpg_tlv, -4200, 20, 0); + +static const char * const ak4375_ovolcn_select_texts[] = { "Dependent", "Independent" }; +static const char * const ak4375_mdac_select_texts[] = { "x1", "x1/2" }; +static const char * const ak4375_inv_select_texts[] = { "Normal", "Inverting" }; +static const char * const ak4375_cpmode_select_texts[] = { + "Automatic Switching", + "+-VDD Operation", + "+-1/2VDD Operation" +}; + +/* + * DASD, DASL bits Digital Filter Setting + * 0, 0 : Sharp Roll-Off Filter + * 0, 1 : Slow Roll-Off Filter + * 1, 0 : Short delay Sharp Roll-Off Filter + * 1, 1 : Short delay Slow Roll-Off Filter + */ +static const char * const ak4375_digfil_select_texts[] = { + "Sharp Roll-Off Filter", + "Slow Roll-Off Filter", + "Short delay Sharp Roll-Off Filter", + "Short delay Slow Roll-Off Filter", +}; + +static const struct soc_enum ak4375_ovolcn_enum = + SOC_ENUM_SINGLE(AK4375_0B_LCH_OUTPUT_VOLUME, 7, + ARRAY_SIZE(ak4375_ovolcn_select_texts), ak4375_ovolcn_select_texts); +static const struct soc_enum ak4375_mdacl_enum = + SOC_ENUM_SINGLE(AK4375_07_DAC_MONO_MIXING, 2, + ARRAY_SIZE(ak4375_mdac_select_texts), ak4375_mdac_select_texts); +static const struct soc_enum ak4375_mdacr_enum = + SOC_ENUM_SINGLE(AK4375_07_DAC_MONO_MIXING, 6, + ARRAY_SIZE(ak4375_mdac_select_texts), ak4375_mdac_select_texts); +static const struct soc_enum ak4375_invl_enum = + SOC_ENUM_SINGLE(AK4375_07_DAC_MONO_MIXING, 3, + ARRAY_SIZE(ak4375_inv_select_texts), ak4375_inv_select_texts); +static const struct soc_enum ak4375_invr_enum = + SOC_ENUM_SINGLE(AK4375_07_DAC_MONO_MIXING, 7, + ARRAY_SIZE(ak4375_inv_select_texts), ak4375_inv_select_texts); +static const struct soc_enum ak4375_cpmode_enum = + SOC_ENUM_SINGLE(AK4375_03_POWER_MANAGEMENT4, 2, + ARRAY_SIZE(ak4375_cpmode_select_texts), ak4375_cpmode_select_texts); +static const struct soc_enum ak4375_digfil_enum = + SOC_ENUM_SINGLE(AK4375_06_DIGITAL_FILTER_SELECT, 6, + ARRAY_SIZE(ak4375_digfil_select_texts), ak4375_digfil_select_texts); + +static const struct snd_kcontrol_new ak4375_snd_controls[] = { + SOC_DOUBLE_R_TLV("Digital Output Volume", AK4375_0B_LCH_OUTPUT_VOLUME, + AK4375_0C_RCH_OUTPUT_VOLUME, 0, 0x1f, 0, dac_tlv), + SOC_SINGLE_TLV("HP-Amp Analog Volume", + AK4375_0D_HP_VOLUME_CONTROL, 0, 0x1f, 0, hpg_tlv), + + SOC_ENUM("Digital Volume Control", ak4375_ovolcn_enum), + SOC_ENUM("DACL Signal Level", ak4375_mdacl_enum), + SOC_ENUM("DACR Signal Level", ak4375_mdacr_enum), + SOC_ENUM("DACL Signal Invert", ak4375_invl_enum), + SOC_ENUM("DACR Signal Invert", ak4375_invr_enum), + SOC_ENUM("Charge Pump Mode", ak4375_cpmode_enum), + SOC_ENUM("DAC Digital Filter Mode", ak4375_digfil_enum), +}; + +static const struct snd_kcontrol_new ak4375_hpl_mixer_controls[] = { + SOC_DAPM_SINGLE("LDACL Switch", AK4375_07_DAC_MONO_MIXING, 0, 1, 0), + SOC_DAPM_SINGLE("RDACL Switch", AK4375_07_DAC_MONO_MIXING, 1, 1, 0), +}; + +static const struct snd_kcontrol_new ak4375_hpr_mixer_controls[] = { + SOC_DAPM_SINGLE("LDACR Switch", AK4375_07_DAC_MONO_MIXING, 4, 1, 0), + SOC_DAPM_SINGLE("RDACR Switch", AK4375_07_DAC_MONO_MIXING, 5, 1, 0), +}; + +static int ak4375_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, AK4375_00_POWER_MANAGEMENT1, PMPLL, PMPLL); + snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMCP1, PMCP1); + usleep_range(6500, 7000); + snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMLDO, PMLDO); + usleep_range(1000, 2000); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMCP2, PMCP2); + usleep_range(4500, 5000); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMCP2, 0x0); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMLDO, 0x0); + snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMCP1, 0x0); + snd_soc_component_update_bits(component, AK4375_00_POWER_MANAGEMENT1, PMPLL, 0x0); + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget ak4375_dapm_widgets[] = { + SND_SOC_DAPM_DAC_E("DAC", NULL, AK4375_02_POWER_MANAGEMENT3, 0, 0, ak4375_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_IN("SDTI", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), + + SND_SOC_DAPM_MIXER("HPR Mixer", AK4375_03_POWER_MANAGEMENT4, 1, 0, + &ak4375_hpr_mixer_controls[0], ARRAY_SIZE(ak4375_hpr_mixer_controls)), + SND_SOC_DAPM_MIXER("HPL Mixer", AK4375_03_POWER_MANAGEMENT4, 0, 0, + &ak4375_hpl_mixer_controls[0], ARRAY_SIZE(ak4375_hpl_mixer_controls)), +}; + +static const struct snd_soc_dapm_route ak4375_intercon[] = { + { "DAC", NULL, "SDTI" }, + + { "HPL Mixer", "LDACL Switch", "DAC" }, + { "HPL Mixer", "RDACL Switch", "DAC" }, + { "HPR Mixer", "LDACR Switch", "DAC" }, + { "HPR Mixer", "RDACR Switch", "DAC" }, + + { "HPL", NULL, "HPL Mixer" }, + { "HPR", NULL, "HPR Mixer" }, +}; + +static int ak4375_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct ak4375_priv *ak4375 = snd_soc_component_get_drvdata(component); + unsigned int freq_in, freq_out; + + ak4375->rate = params_rate(params); + + if (ak4375->rate <= 96000) + ak4375->pld = 0; + else + ak4375->pld = 1; + + freq_in = 32 * ak4375->rate / (ak4375->pld + 1); + + if ((ak4375->rate % 8000) == 0) + freq_out = AK4375_PLL_FREQ_OUT_122880000; + else + freq_out = AK4375_PLL_FREQ_OUT_112896000; + + return snd_soc_dai_set_pll(dai, 0, 0, freq_in, freq_out); +} + +static int ak4375_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_component *component = dai->component; + struct ak4375_priv *ak4375 = snd_soc_component_get_drvdata(component); + unsigned int mclk, plm, mdiv, div; + u8 cms, fs, cm; + + cms = snd_soc_component_read(component, AK4375_05_CLOCK_MODE_SELECT); + fs = cms & ~FS_MASK; + cm = cms & ~CM_MASK; + + switch (ak4375->rate) { + case 8000: + fs |= FS_8KHZ; + break; + case 11025: + fs |= FS_11_025KHZ; + break; + case 16000: + fs |= FS_16KHZ; + break; + case 22050: + fs |= FS_22_05KHZ; + break; + case 32000: + fs |= FS_32KHZ; + break; + case 44100: + fs |= FS_44_1KHZ; + break; + case 48000: + fs |= FS_48KHZ; + break; + case 88200: + fs |= FS_88_2KHZ; + break; + case 96000: + fs |= FS_96KHZ; + break; + case 176400: + fs |= FS_176_4KHZ; + break; + case 192000: + fs |= FS_192KHZ; + break; + default: + return -EINVAL; + } + + if (ak4375->rate <= 24000) { + cm |= CM_1; + mclk = 512 * ak4375->rate; + mdiv = freq_out / mclk - 1; + div = 0; + } else if (ak4375->rate <= 96000) { + cm |= CM_0; + mclk = 256 * ak4375->rate; + mdiv = freq_out / mclk - 1; + div = 0; + } else { + cm |= CM_3; + mclk = 128 * ak4375->rate; + mdiv = 4; + div = 1; + } + + /* Writing both fields in one go seems to make playback choppy on start */ + snd_soc_component_update_bits(component, AK4375_05_CLOCK_MODE_SELECT, FS_MASK, fs); + snd_soc_component_update_bits(component, AK4375_05_CLOCK_MODE_SELECT, CM_MASK, cm); + + snd_soc_component_write(component, AK4375_0F_PLL_REF_CLK_DIVIDER1, + (ak4375->pld & 0xff00) >> 8); + snd_soc_component_write(component, AK4375_10_PLL_REF_CLK_DIVIDER2, + ak4375->pld & 0x00ff); + + plm = freq_out / freq_in - 1; + snd_soc_component_write(component, AK4375_11_PLL_FB_CLK_DIVIDER1, (plm & 0xff00) >> 8); + snd_soc_component_write(component, AK4375_12_PLL_FB_CLK_DIVIDER2, plm & 0x00ff); + + snd_soc_component_update_bits(component, AK4375_13_SRC_CLK_SOURCE, DIV, div); + + /* SRCCKS bit: force to 1 for SRC PLL source clock */ + snd_soc_component_update_bits(component, AK4375_13_SRC_CLK_SOURCE, SRCCKS, SRCCKS); + + snd_soc_component_write(component, AK4375_14_DAC_CLK_DIVIDER, mdiv); + + dev_dbg(ak4375->dev, "rate=%d mclk=%d f_in=%d f_out=%d PLD=%d PLM=%d MDIV=%d DIV=%d\n", + ak4375->rate, mclk, freq_in, freq_out, ak4375->pld, plm, mdiv, div); + + return 0; +} + +static int ak4375_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + struct ak4375_priv *ak4375 = snd_soc_component_get_drvdata(component); + u8 val = snd_soc_component_read(component, AK4375_07_DAC_MONO_MIXING); + + dev_dbg(ak4375->dev, "mute=%d val=%d\n", mute, val); + + if (mute) { + ak4375->mute_save = val & DACMUTE_MASK; + val &= ~DACMUTE_MASK; + } else { + val |= ak4375->mute_save; + } + + snd_soc_component_write(component, AK4375_07_DAC_MONO_MIXING, val); + + return 0; +} + +#define AK4375_RATES (SNDRV_PCM_RATE_8000_48000 |\ + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000) + +#define AK4375_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops ak4375_dai_ops = { + .hw_params = ak4375_hw_params, + .mute_stream = ak4375_mute, + .set_pll = ak4375_dai_set_pll, +}; + +static struct snd_soc_dai_driver ak4375_dai = { + .name = "ak4375-hifi", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AK4375_RATES, + .rate_min = 8000, + .rate_max = 192000, + .formats = AK4375_FORMATS, + }, + .ops = &ak4375_dai_ops, +}; + +static void ak4375_power_off(struct ak4375_priv *ak4375) +{ + gpiod_set_value_cansleep(ak4375->pdn_gpiod, 0); + usleep_range(1000, 2000); + + regulator_bulk_disable(ARRAY_SIZE(ak4375->supplies), ak4375->supplies); +} + +static int ak4375_power_on(struct ak4375_priv *ak4375) +{ + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(ak4375->supplies), ak4375->supplies); + if (ret < 0) { + dev_err(ak4375->dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + + usleep_range(3000, 4000); + + gpiod_set_value_cansleep(ak4375->pdn_gpiod, 1); + usleep_range(1000, 2000); + + return 0; +} + +#ifdef CONFIG_PM +static int __maybe_unused ak4375_runtime_suspend(struct device *dev) +{ + struct ak4375_priv *ak4375 = dev_get_drvdata(dev); + + regcache_cache_only(ak4375->regmap, true); + ak4375_power_off(ak4375); + + return 0; +} + +static int __maybe_unused ak4375_runtime_resume(struct device *dev) +{ + struct ak4375_priv *ak4375 = dev_get_drvdata(dev); + int ret; + + ret = ak4375_power_on(ak4375); + if (ret < 0) + return ret; + + regcache_cache_only(ak4375->regmap, false); + regcache_mark_dirty(ak4375->regmap); + + return regcache_sync(ak4375->regmap); +} +#endif /* CONFIG_PM */ + +static const struct snd_soc_component_driver soc_codec_dev_ak4375 = { + .controls = ak4375_snd_controls, + .num_controls = ARRAY_SIZE(ak4375_snd_controls), + .dapm_widgets = ak4375_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4375_dapm_widgets), + .dapm_routes = ak4375_intercon, + .num_dapm_routes = ARRAY_SIZE(ak4375_intercon), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static const struct regmap_config ak4375_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = AK4375_24_MODE_CONTROL, + .reg_defaults = ak4375_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ak4375_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; + +static const struct ak4375_drvdata ak4375_drvdata = { + .dai_drv = &ak4375_dai, + .comp_drv = &soc_codec_dev_ak4375, +}; + +static const struct dev_pm_ops ak4375_pm = { + SET_RUNTIME_PM_OPS(ak4375_runtime_suspend, ak4375_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + +static int ak4375_i2c_probe(struct i2c_client *i2c) +{ + struct ak4375_priv *ak4375; + const struct ak4375_drvdata *drvdata; + unsigned int deviceid; + int ret, i; + + ak4375 = devm_kzalloc(&i2c->dev, sizeof(*ak4375), GFP_KERNEL); + if (!ak4375) + return -ENOMEM; + + ak4375->regmap = devm_regmap_init_i2c(i2c, &ak4375_regmap); + if (IS_ERR(ak4375->regmap)) + return PTR_ERR(ak4375->regmap); + + i2c_set_clientdata(i2c, ak4375); + ak4375->dev = &i2c->dev; + + drvdata = of_device_get_match_data(&i2c->dev); + + for (i = 0; i < ARRAY_SIZE(supply_names); i++) + ak4375->supplies[i].supply = supply_names[i]; + + ret = devm_regulator_bulk_get(ak4375->dev, ARRAY_SIZE(ak4375->supplies), ak4375->supplies); + if (ret < 0) { + dev_err(ak4375->dev, "Failed to get regulators: %d\n", ret); + return ret; + } + + ak4375->pdn_gpiod = devm_gpiod_get_optional(ak4375->dev, "pdn", GPIOD_OUT_LOW); + if (IS_ERR(ak4375->pdn_gpiod)) + return dev_err_probe(ak4375->dev, PTR_ERR(ak4375->pdn_gpiod), + "failed to get pdn\n"); + + ret = ak4375_power_on(ak4375); + if (ret < 0) + return ret; + + /* Don't read deviceid from cache */ + regcache_cache_bypass(ak4375->regmap, true); + + ret = regmap_read(ak4375->regmap, AK4375_15_AUDIO_IF_FORMAT, &deviceid); + if (ret < 0) { + dev_err(ak4375->dev, "unable to read DEVICEID!\n"); + return ret; + } + + regcache_cache_bypass(ak4375->regmap, false); + + deviceid = (deviceid & DEVICEID_MASK) >> 5; + + switch (deviceid) { + case DEVICEID_AK4331: + dev_err(ak4375->dev, "found untested AK4331\n"); + return -EINVAL; + case DEVICEID_AK4375: + dev_dbg(ak4375->dev, "found AK4375\n"); + break; + case DEVICEID_AK4375A: + dev_dbg(ak4375->dev, "found AK4375A\n"); + break; + case DEVICEID_AK4376A: + dev_err(ak4375->dev, "found unsupported AK4376/A!\n"); + return -EINVAL; + case DEVICEID_AK4377: + dev_err(ak4375->dev, "found unsupported AK4377!\n"); + return -EINVAL; + default: + dev_err(ak4375->dev, "unrecognized DEVICEID!\n"); + return -EINVAL; + } + + pm_runtime_set_active(ak4375->dev); + pm_runtime_enable(ak4375->dev); + + ret = devm_snd_soc_register_component(ak4375->dev, drvdata->comp_drv, + drvdata->dai_drv, 1); + if (ret < 0) { + dev_err(ak4375->dev, "Failed to register CODEC: %d\n", ret); + return ret; + } + + return 0; +} + +static int ak4375_i2c_remove(struct i2c_client *i2c) +{ + pm_runtime_disable(&i2c->dev); + + return 0; +} + +static const struct of_device_id ak4375_of_match[] = { + { .compatible = "asahi-kasei,ak4375", .data = &ak4375_drvdata }, + { }, +}; +MODULE_DEVICE_TABLE(of, ak4375_of_match); + +static struct i2c_driver ak4375_i2c_driver = { + .driver = { + .name = "ak4375", + .pm = &ak4375_pm, + .of_match_table = ak4375_of_match, + }, + .probe_new = ak4375_i2c_probe, + .remove = ak4375_i2c_remove, +}; +module_i2c_driver(ak4375_i2c_driver); + +MODULE_AUTHOR("Vincent Knecht "); +MODULE_DESCRIPTION("ASoC AK4375 DAC driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 0d422a466ef7fdbbe402194ac06144d1bbcdc227 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Sat, 18 Dec 2021 15:34:21 +0100 Subject: ASoC: dt-bindings: Use name-prefix schema name-prefix.txt does not exist anymore, just reference the schema instead. Signed-off-by: Alexander Stein Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20211218143423.18768-1-alexander.stein@mailbox.org Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/simple-audio-amplifier.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/simple-audio-amplifier.yaml b/Documentation/devicetree/bindings/sound/simple-audio-amplifier.yaml index 26379377a7ac..8327846356d3 100644 --- a/Documentation/devicetree/bindings/sound/simple-audio-amplifier.yaml +++ b/Documentation/devicetree/bindings/sound/simple-audio-amplifier.yaml @@ -9,6 +9,9 @@ title: Simple Audio Amplifier Device Tree Bindings maintainers: - Jerome Brunet +allOf: + - $ref: name-prefix.yaml# + properties: compatible: enum: @@ -22,10 +25,7 @@ properties: description: > power supply for the device - sound-name-prefix: - $ref: /schemas/types.yaml#/definitions/string - description: > - See ./name-prefix.txt + sound-name-prefix: true required: - compatible -- cgit v1.2.3 From 847cbea6459d5beb3f0f960fde4337f28b663eae Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Sat, 18 Dec 2021 15:34:22 +0100 Subject: ASoC: meson: t9015: add missing sound-name-prefix property This is used in meson-gxl and meson-g12-common .dtsi. Add the property to the binding. This fixes the dtschema warning: audio-controller@32000: 'sound-name-prefix' does not match any of the regexes: 'pinctrl-[0-9]+' Signed-off-by: Alexander Stein Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20211218143423.18768-2-alexander.stein@mailbox.org Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/amlogic,t9015.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/amlogic,t9015.yaml b/Documentation/devicetree/bindings/sound/amlogic,t9015.yaml index db7b04da0b39..580a3d040abc 100644 --- a/Documentation/devicetree/bindings/sound/amlogic,t9015.yaml +++ b/Documentation/devicetree/bindings/sound/amlogic,t9015.yaml @@ -9,6 +9,9 @@ title: Amlogic T9015 Internal Audio DAC maintainers: - Jerome Brunet +allOf: + - $ref: name-prefix.yaml# + properties: $nodename: pattern: "^audio-controller@.*" @@ -38,6 +41,8 @@ properties: description: Analogue power supply. + sound-name-prefix: true + required: - "#sound-dai-cells" - compatible -- cgit v1.2.3 From 1f6532073e3e9caee1dbc3f9b4be28359a181ea4 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Sat, 18 Dec 2021 15:34:23 +0100 Subject: ASoC: meson: g12a: add missing sound-name-prefix property This is used in meson-sm1 and meson-g12 .dtsi. Add the property to the binding. This fixes the dtschema warning: audio-controller@740: 'sound-name-prefix' does not match any of the regexes: 'pinctrl-[0-9]+' Signed-off-by: Alexander Stein Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20211218143423.18768-3-alexander.stein@mailbox.org Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/amlogic,g12a-toacodec.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/amlogic,g12a-toacodec.yaml b/Documentation/devicetree/bindings/sound/amlogic,g12a-toacodec.yaml index 3c3891d17238..77469a45bb7a 100644 --- a/Documentation/devicetree/bindings/sound/amlogic,g12a-toacodec.yaml +++ b/Documentation/devicetree/bindings/sound/amlogic,g12a-toacodec.yaml @@ -9,6 +9,9 @@ title: Amlogic G12a Internal DAC Control Glue maintainers: - Jerome Brunet +allOf: + - $ref: name-prefix.yaml# + properties: $nodename: pattern: "^audio-controller@.*" @@ -31,6 +34,8 @@ properties: resets: maxItems: 1 + sound-name-prefix: true + required: - "#sound-dai-cells" - compatible -- cgit v1.2.3 From 37daf8d9e0bd85a2859721aec28e1eb6e9973262 Mon Sep 17 00:00:00 2001 From: Vincent Knecht Date: Wed, 22 Dec 2021 14:54:03 +0100 Subject: ASoC: codecs: ak4375: Change invert controls to a stereo switch Don't use enums for DACL/DACR Signal Invert controls, and change them into a stereo "DAC Signal Invert Switch" control. Signed-off-by: Vincent Knecht Link: https://lore.kernel.org/r/20211222135403.2991657-1-vincent.knecht@mailoo.org Signed-off-by: Mark Brown --- sound/soc/codecs/ak4375.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/sound/soc/codecs/ak4375.c b/sound/soc/codecs/ak4375.c index a893aff42a01..22cda0699341 100644 --- a/sound/soc/codecs/ak4375.c +++ b/sound/soc/codecs/ak4375.c @@ -127,7 +127,6 @@ static DECLARE_TLV_DB_SCALE(hpg_tlv, -4200, 20, 0); static const char * const ak4375_ovolcn_select_texts[] = { "Dependent", "Independent" }; static const char * const ak4375_mdac_select_texts[] = { "x1", "x1/2" }; -static const char * const ak4375_inv_select_texts[] = { "Normal", "Inverting" }; static const char * const ak4375_cpmode_select_texts[] = { "Automatic Switching", "+-VDD Operation", @@ -157,12 +156,6 @@ static const struct soc_enum ak4375_mdacl_enum = static const struct soc_enum ak4375_mdacr_enum = SOC_ENUM_SINGLE(AK4375_07_DAC_MONO_MIXING, 6, ARRAY_SIZE(ak4375_mdac_select_texts), ak4375_mdac_select_texts); -static const struct soc_enum ak4375_invl_enum = - SOC_ENUM_SINGLE(AK4375_07_DAC_MONO_MIXING, 3, - ARRAY_SIZE(ak4375_inv_select_texts), ak4375_inv_select_texts); -static const struct soc_enum ak4375_invr_enum = - SOC_ENUM_SINGLE(AK4375_07_DAC_MONO_MIXING, 7, - ARRAY_SIZE(ak4375_inv_select_texts), ak4375_inv_select_texts); static const struct soc_enum ak4375_cpmode_enum = SOC_ENUM_SINGLE(AK4375_03_POWER_MANAGEMENT4, 2, ARRAY_SIZE(ak4375_cpmode_select_texts), ak4375_cpmode_select_texts); @@ -176,11 +169,11 @@ static const struct snd_kcontrol_new ak4375_snd_controls[] = { SOC_SINGLE_TLV("HP-Amp Analog Volume", AK4375_0D_HP_VOLUME_CONTROL, 0, 0x1f, 0, hpg_tlv), + SOC_DOUBLE("DAC Signal Invert Switch", AK4375_07_DAC_MONO_MIXING, 3, 7, 1, 0), + SOC_ENUM("Digital Volume Control", ak4375_ovolcn_enum), SOC_ENUM("DACL Signal Level", ak4375_mdacl_enum), SOC_ENUM("DACR Signal Level", ak4375_mdacr_enum), - SOC_ENUM("DACL Signal Invert", ak4375_invl_enum), - SOC_ENUM("DACR Signal Invert", ak4375_invr_enum), SOC_ENUM("Charge Pump Mode", ak4375_cpmode_enum), SOC_ENUM("DAC Digital Filter Mode", ak4375_digfil_enum), }; -- cgit v1.2.3 From 72b8ed83f7eccf84c54b68a551beae400949cc29 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 23 Dec 2021 13:36:09 +0200 Subject: ASoC: SOF: ops: Use dev_warn() if the panic offsets differ Catch the cases when the stored sdev->dsp_oops_offset and the offset received via the panic message differs and print a warning, but keep using the dsp_oops_offset for the oops query. Signed-off-by: Peter Ujfalusi Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Reviewed-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211223113628.18582-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ops.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/sound/soc/sof/ops.c b/sound/soc/sof/ops.c index 160b88a2d59f..1d6a95a00cf5 100644 --- a/sound/soc/sof/ops.c +++ b/sound/soc/sof/ops.c @@ -144,18 +144,23 @@ EXPORT_SYMBOL(snd_sof_dsp_update_bits_forced); void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset) { - dev_err(sdev->dev, "error : DSP panic!\n"); - /* - * check if DSP is not ready and did not set the dsp_oops_offset. - * if the dsp_oops_offset is not set, set it from the panic message. - * Also add a check to memory window setting with panic message. + * if DSP is not ready and the dsp_oops_offset is not yet set, use the + * offset from the panic message. */ if (!sdev->dsp_oops_offset) sdev->dsp_oops_offset = offset; - else - dev_dbg(sdev->dev, "panic: dsp_oops_offset %zu offset %d\n", - sdev->dsp_oops_offset, offset); + + /* + * Print warning if the offset from the panic message differs from + * dsp_oops_offset + */ + if (sdev->dsp_oops_offset != offset) + dev_warn(sdev->dev, + "%s: dsp_oops_offset %zu differs from panic offset %u\n", + __func__, sdev->dsp_oops_offset, offset); + + dev_err(sdev->dev, "DSP panic!\n"); /* We want to see the DSP panic! */ sdev->dbg_dump_printed = false; -- cgit v1.2.3 From b2539ef00e4427350b26896540ccabd98e88c7bb Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 23 Dec 2021 13:36:10 +0200 Subject: ASoC: SOF: Intel: hda-loader: Avoid re-defining the HDA_FW_BOOT_ATTEMPTS HDA_FW_BOOT_ATTEMPTS is defined in hda.h, do not define it again locally in hda-loader.c At the same time correct the indentation for the define in hda.h Signed-off-by: Peter Ujfalusi Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Reviewed-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211223113628.18582-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-loader.c | 1 - sound/soc/sof/intel/hda.h | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index bfb0e374ebab..5f5f396f4fb8 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -23,7 +23,6 @@ #include "../ops.h" #include "hda.h" -#define HDA_FW_BOOT_ATTEMPTS 3 #define HDA_CL_STREAM_FORMAT 0x40 static struct hdac_ext_stream *cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format, diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 5b4d59647a1d..03a6bb7a165c 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -273,7 +273,7 @@ #define BXT_D0I3_DELAY 5000 #define FW_CL_STREAM_NUMBER 0x1 -#define HDA_FW_BOOT_ATTEMPTS 3 +#define HDA_FW_BOOT_ATTEMPTS 3 /* ADSPCS - Audio DSP Control & Status */ -- cgit v1.2.3 From f902b21adba98f28eaa1cf5e509d99eaa7b1b36e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 23 Dec 2021 13:36:11 +0200 Subject: ASoC: SOF: core: Add simple wrapper to check flags in sof_core_debug The sof_debug_check_flag() can be used to check a flag or a combination of them in sof_core_debug. Signed-off-by: Peter Ujfalusi Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Reviewed-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211223113628.18582-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/core.c | 16 ++++++++++++++++ sound/soc/sof/sof-priv.h | 1 + 2 files changed, 17 insertions(+) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 40549cdd6d58..1224a7da053a 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -27,6 +27,22 @@ MODULE_PARM_DESC(sof_debug, "SOF core debug options (0x0 all off)"); #define TIMEOUT_DEFAULT_IPC_MS 500 #define TIMEOUT_DEFAULT_BOOT_MS 2000 +/** + * sof_debug_check_flag - check if a given flag(s) is set in sof_core_debug + * @mask: Flag or combination of flags to check + * + * Returns true if all bits set in mask is also set in sof_core_debug, otherwise + * false + */ +bool sof_debug_check_flag(int mask) +{ + if ((sof_core_debug & mask) == mask) + return true; + + return false; +} +EXPORT_SYMBOL(sof_debug_check_flag); + /* * FW Panic/fault handling. */ diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 114882e4370f..35c5b2d6930b 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -43,6 +43,7 @@ /* global debug state set by SOF_DBG_ flags */ extern int sof_core_debug; +bool sof_debug_check_flag(int mask); /* max BARs mmaped devices can use */ #define SND_SOF_BARS 8 -- cgit v1.2.3 From 12b401f4de787627f4a25784a0278bbbf93122b6 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 23 Dec 2021 13:36:12 +0200 Subject: ASoC: SOF: Use sof_debug_check_flag() instead of sof_core_debug directly The sof_debug_check_flag() is available for checking flags set in sof_core_debug. sof_core_debug can be marked static in core.c Signed-off-by: Peter Ujfalusi Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Reviewed-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211223113628.18582-5-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/core.c | 4 ++-- sound/soc/sof/debug.c | 6 +++--- sound/soc/sof/sof-priv.h | 1 - sound/soc/sof/topology.c | 12 ++++++------ 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 1224a7da053a..00f8ffee2866 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -19,7 +19,7 @@ #endif /* see SOF_DBG_ flags */ -int sof_core_debug = IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE); +static int sof_core_debug = IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE); module_param_named(sof_debug, sof_core_debug, int, 0444); MODULE_PARM_DESC(sof_debug, "SOF core debug options (0x0 all off)"); @@ -218,7 +218,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) goto fw_run_err; } - if (sof_core_debug & SOF_DBG_ENABLE_TRACE) { + if (sof_debug_check_flag(SOF_DBG_ENABLE_TRACE)) { sdev->dtrace_is_supported = true; /* init DMA trace */ diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index 2f8b5ac9b78a..9e4a128b5918 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -957,7 +957,7 @@ static void snd_sof_dbg_print_fw_state(struct snd_sof_dev *sdev) void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, u32 flags) { - bool print_all = !!(sof_core_debug & SOF_DBG_PRINT_ALL_DUMPS); + bool print_all = sof_debug_check_flag(SOF_DBG_PRINT_ALL_DUMPS); if (flags & SOF_DBG_DUMP_OPTIONAL && !print_all) return; @@ -979,7 +979,7 @@ static void snd_sof_ipc_dump(struct snd_sof_dev *sdev) dev_err(sdev->dev, "------------[ IPC dump start ]------------\n"); sof_ops(sdev)->ipc_dump(sdev); dev_err(sdev->dev, "------------[ IPC dump end ]------------\n"); - if (!(sof_core_debug & SOF_DBG_PRINT_ALL_DUMPS)) + if (!sof_debug_check_flag(SOF_DBG_PRINT_ALL_DUMPS)) sdev->ipc_dump_printed = true; } } @@ -987,7 +987,7 @@ static void snd_sof_ipc_dump(struct snd_sof_dev *sdev) void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev) { if (IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT) || - (sof_core_debug & SOF_DBG_RETAIN_CTX)) { + sof_debug_check_flag(SOF_DBG_RETAIN_CTX)) { /* should we prevent DSP entering D3 ? */ if (!sdev->ipc_dump_printed) dev_info(sdev->dev, diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 35c5b2d6930b..0f849cdbfbc8 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -42,7 +42,6 @@ #define SOF_DBG_DUMP_OPTIONAL BIT(4) /* only dump if SOF_DBG_PRINT_ALL_DUMPS is set */ /* global debug state set by SOF_DBG_ flags */ -extern int sof_core_debug; bool sof_debug_check_flag(int mask); /* max BARs mmaped devices can use */ diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index ec59baf32699..e72dcae5e7ee 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1695,12 +1695,12 @@ static int sof_widget_load_pipeline(struct snd_soc_component *scomp, int index, goto err; } - if (sof_core_debug & SOF_DBG_DISABLE_MULTICORE) + if (sof_debug_check_flag(SOF_DBG_DISABLE_MULTICORE)) pipeline->core = SOF_DSP_PRIMARY_CORE; - if (sof_core_debug & SOF_DBG_DYNAMIC_PIPELINES_OVERRIDE) - swidget->dynamic_pipeline_widget = sof_core_debug & - SOF_DBG_DYNAMIC_PIPELINES_ENABLE; + if (sof_debug_check_flag(SOF_DBG_DYNAMIC_PIPELINES_OVERRIDE)) + swidget->dynamic_pipeline_widget = + sof_debug_check_flag(SOF_DBG_DYNAMIC_PIPELINES_ENABLE); dev_dbg(scomp->dev, "pipeline %s: period %d pri %d mips %d core %d frames %d dynamic %d\n", swidget->widget->name, pipeline->period, pipeline->priority, @@ -2295,7 +2295,7 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, return ret; } - if (sof_core_debug & SOF_DBG_DISABLE_MULTICORE) + if (sof_debug_check_flag(SOF_DBG_DISABLE_MULTICORE)) comp.core = SOF_DSP_PRIMARY_CORE; swidget->core = comp.core; @@ -3542,7 +3542,7 @@ static int sof_complete(struct snd_soc_component *scomp) } /* verify topology components loading including dynamic pipelines */ - if (sof_core_debug & SOF_DBG_VERIFY_TPLG) { + if (sof_debug_check_flag(SOF_DBG_VERIFY_TPLG)) { ret = sof_set_up_pipelines(sdev, true); if (ret < 0) { dev_err(sdev->dev, "error: topology verification failed %d\n", ret); -- cgit v1.2.3 From b2b10aa79fe2fb3d3393d0e90ffb5c1802992412 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 23 Dec 2021 13:36:13 +0200 Subject: ASoC: SOF: Add 'non_recoverable' parameter to snd_sof_dsp_panic() Some platforms use retries during firmware boot to overcome DSP startup issues. In these cases we might receive a DSP panic message which should not be treated as fatal if it happens during boot. Pass this information to snd_sof_dsp_panic() and omit the panic print if it is not fatal or the user does not want to see all dumps. Signed-off-by: Peter Ujfalusi Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Reviewed-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211223113628.18582-6-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/imx/imx8.c | 2 +- sound/soc/sof/imx/imx8m.c | 2 +- sound/soc/sof/intel/atom.c | 4 ++-- sound/soc/sof/intel/bdw.c | 4 ++-- sound/soc/sof/intel/cnl.c | 21 ++++++++++++++++++--- sound/soc/sof/intel/hda-ipc.c | 19 +++++++++++++++++-- sound/soc/sof/intel/hda-loader.c | 8 ++++++-- sound/soc/sof/ops.c | 24 ++++++++++++++++++------ sound/soc/sof/ops.h | 2 +- 9 files changed, 66 insertions(+), 20 deletions(-) diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c index 099b4356122c..f6baecbb57fb 100644 --- a/sound/soc/sof/imx/imx8.c +++ b/sound/soc/sof/imx/imx8.c @@ -97,7 +97,7 @@ static void imx8_dsp_handle_request(struct imx_dsp_ipc *ipc) /* Check to see if the message is a panic code (0x0dead***) */ if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) - snd_sof_dsp_panic(priv->sdev, p); + snd_sof_dsp_panic(priv->sdev, p, true); else snd_sof_ipc_msgs_rx(priv->sdev); } diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c index c026caea4c8b..788e77bcb603 100644 --- a/sound/soc/sof/imx/imx8m.c +++ b/sound/soc/sof/imx/imx8m.c @@ -90,7 +90,7 @@ static void imx8m_dsp_handle_request(struct imx_dsp_ipc *ipc) /* Check to see if the message is a panic code (0x0dead***) */ if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) - snd_sof_dsp_panic(priv->sdev, p); + snd_sof_dsp_panic(priv->sdev, p, true); else snd_sof_ipc_msgs_rx(priv->sdev); } diff --git a/sound/soc/sof/intel/atom.c b/sound/soc/sof/intel/atom.c index 5aa064b28fca..bcb2eb2acf2e 100644 --- a/sound/soc/sof/intel/atom.c +++ b/sound/soc/sof/intel/atom.c @@ -165,8 +165,8 @@ irqreturn_t atom_irq_thread(int irq, void *context) /* Handle messages from DSP Core */ if ((ipcd & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { - snd_sof_dsp_panic(sdev, PANIC_OFFSET(ipcd) + - MBOX_OFFSET); + snd_sof_dsp_panic(sdev, PANIC_OFFSET(ipcd) + MBOX_OFFSET, + true); } else { snd_sof_ipc_msgs_rx(sdev); } diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c index 1121711e9029..10c9a0b39371 100644 --- a/sound/soc/sof/intel/bdw.c +++ b/sound/soc/sof/intel/bdw.c @@ -344,8 +344,8 @@ static irqreturn_t bdw_irq_thread(int irq, void *context) /* Handle messages from DSP Core */ if ((ipcd & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { - snd_sof_dsp_panic(sdev, BDW_PANIC_OFFSET(ipcx) + - MBOX_OFFSET); + snd_sof_dsp_panic(sdev, BDW_PANIC_OFFSET(ipcx) + MBOX_OFFSET, + true); } else { snd_sof_ipc_msgs_rx(sdev); } diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 3da158d08980..e615125d575e 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -82,9 +82,24 @@ irqreturn_t cnl_ipc_irq_thread(int irq, void *context) msg, msg_ext); /* handle messages from DSP */ - if ((hipctdr & SOF_IPC_PANIC_MAGIC_MASK) == - SOF_IPC_PANIC_MAGIC) { - snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext)); + if ((hipctdr & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + bool non_recoverable = true; + + /* + * This is a PANIC message! + * + * If it is arriving during firmware boot and it is not + * the last boot attempt then change the non_recoverable + * to false as the DSP might be able to boot in the next + * iteration(s) + */ + if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS && + hda->boot_iteration < HDA_FW_BOOT_ATTEMPTS) + non_recoverable = false; + + snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext), + non_recoverable); } else { snd_sof_ipc_msgs_rx(sdev); } diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c index 2019087a84ce..f0cf8019d72d 100644 --- a/sound/soc/sof/intel/hda-ipc.c +++ b/sound/soc/sof/intel/hda-ipc.c @@ -173,8 +173,23 @@ irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context) /* handle messages from DSP */ if ((hipct & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { - /* this is a PANIC message !! */ - snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext)); + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + bool non_recoverable = true; + + /* + * This is a PANIC message! + * + * If it is arriving during firmware boot and it is not + * the last boot attempt then change the non_recoverable + * to false as the DSP might be able to boot in the next + * iteration(s) + */ + if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS && + hda->boot_iteration < HDA_FW_BOOT_ATTEMPTS) + non_recoverable = false; + + snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext), + non_recoverable); } else { /* normal message - process normally */ snd_sof_ipc_msgs_rx(sdev); diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 5f5f396f4fb8..8ef16f1082e3 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -413,9 +413,13 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) hda_sdw_process_wakeen(sdev); /* - * at this point DSP ROM has been initialized and - * should be ready for code loading and firmware boot + * Set the boot_iteration to the last attempt, indicating that the + * DSP ROM has been initialized and from this point there will be no + * retry done to boot. + * + * Continue with code loading and firmware boot */ + hda->boot_iteration = HDA_FW_BOOT_ATTEMPTS; ret = cl_copy_fw(sdev, stream); if (!ret) { dev_dbg(sdev->dev, "Firmware download successful, booting...\n"); diff --git a/sound/soc/sof/ops.c b/sound/soc/sof/ops.c index 1d6a95a00cf5..9abf7a8e55e0 100644 --- a/sound/soc/sof/ops.c +++ b/sound/soc/sof/ops.c @@ -142,7 +142,13 @@ void snd_sof_dsp_update_bits_forced(struct snd_sof_dev *sdev, u32 bar, } EXPORT_SYMBOL(snd_sof_dsp_update_bits_forced); -void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset) +/** + * snd_sof_dsp_panic - handle a received DSP panic message + * @sdev: Pointer to the device's sdev + * @offset: offset of panic information + * @non_recoverable: the panic is fatal, no recovery will be done by the caller + */ +void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset, bool non_recoverable) { /* * if DSP is not ready and the dsp_oops_offset is not yet set, use the @@ -160,12 +166,18 @@ void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset) "%s: dsp_oops_offset %zu differs from panic offset %u\n", __func__, sdev->dsp_oops_offset, offset); - dev_err(sdev->dev, "DSP panic!\n"); + /* + * Only print the panic information if we have non recoverable panic or + * if all dumps should be printed + */ + if (non_recoverable || sof_debug_check_flag(SOF_DBG_PRINT_ALL_DUMPS)) { + dev_err(sdev->dev, "DSP panic!\n"); - /* We want to see the DSP panic! */ - sdev->dbg_dump_printed = false; + /* We want to see the DSP panic! */ + sdev->dbg_dump_printed = false; - snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); - snd_sof_trace_notify_for_error(sdev); + snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); + snd_sof_trace_notify_for_error(sdev); + } } EXPORT_SYMBOL(snd_sof_dsp_panic); diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index b0ffb2a93bcc..bca7d35536e4 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -643,5 +643,5 @@ int snd_sof_dsp_register_poll(struct snd_sof_dev *sdev, u32 bar, u32 offset, u32 mask, u32 target, u32 timeout_ms, u32 interval_us); -void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset); +void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset, bool non_recoverable); #endif -- cgit v1.2.3 From 2f148430b96e975e895163d763bfc9c5088100eb Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 23 Dec 2021 13:36:14 +0200 Subject: ASoC: SOF: Add a 'message' parameter to snd_sof_dsp_dbg_dump() When snd_sof_dsp_dbg_dump() is called we have an explanatory message to give some hint on the reason why we have the dump on the caller level. Pass this message to snd_sof_dsp_dbg_dump() and handle the print according to the dump rules. This way we can finally print information on the HDA boot iteration if all dumps are enabled. Signed-off-by: Peter Ujfalusi Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Reviewed-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211223113628.18582-7-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/debug.c | 9 +++++++-- sound/soc/sof/intel/hda-loader.c | 15 +++++++++------ sound/soc/sof/loader.c | 8 ++++---- sound/soc/sof/ops.c | 5 ++--- sound/soc/sof/ops.h | 2 +- 5 files changed, 23 insertions(+), 16 deletions(-) diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index 9e4a128b5918..cf7d95c33afe 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -955,7 +955,7 @@ static void snd_sof_dbg_print_fw_state(struct snd_sof_dev *sdev) dev_err(sdev->dev, "fw_state: UNKNOWN (%d)\n", sdev->fw_state); } -void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, u32 flags) +void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, const char *msg, u32 flags) { bool print_all = sof_debug_check_flag(SOF_DBG_PRINT_ALL_DUMPS); @@ -964,11 +964,15 @@ void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, u32 flags) if (sof_ops(sdev)->dbg_dump && !sdev->dbg_dump_printed) { dev_err(sdev->dev, "------------[ DSP dump start ]------------\n"); + if (msg) + dev_err(sdev->dev, "%s\n", msg); snd_sof_dbg_print_fw_state(sdev); sof_ops(sdev)->dbg_dump(sdev, flags); dev_err(sdev->dev, "------------[ DSP dump end ]------------\n"); if (!print_all) sdev->dbg_dump_printed = true; + } else if (msg) { + dev_err(sdev->dev, "%s\n", msg); } } EXPORT_SYMBOL(snd_sof_dsp_dbg_dump); @@ -997,7 +1001,8 @@ void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev) /* dump vital information to the logs */ snd_sof_ipc_dump(sdev); - snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); + snd_sof_dsp_dbg_dump(sdev, "Firmware exception", + SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); snd_sof_trace_notify_for_error(sdev); } EXPORT_SYMBOL(snd_sof_handle_fw_exception); diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 8ef16f1082e3..33306d2023a7 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -88,6 +88,7 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag) const struct sof_intel_dsp_desc *chip = hda->desc; unsigned int status; unsigned long mask; + char *dump_msg; u32 flags, j; int ret; int i; @@ -189,9 +190,12 @@ err: if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS) flags &= ~SOF_DBG_DUMP_OPTIONAL; - snd_sof_dsp_dbg_dump(sdev, flags); + dump_msg = kasprintf(GFP_KERNEL, "Boot iteration failed: %d/%d", + hda->boot_iteration, HDA_FW_BOOT_ATTEMPTS); + snd_sof_dsp_dbg_dump(sdev, dump_msg, flags); hda_dsp_core_reset_power_down(sdev, chip->host_managed_cores_mask); + kfree(dump_msg); return ret; } @@ -421,12 +425,11 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) */ hda->boot_iteration = HDA_FW_BOOT_ATTEMPTS; ret = cl_copy_fw(sdev, stream); - if (!ret) { + if (!ret) dev_dbg(sdev->dev, "Firmware download successful, booting...\n"); - } else { - snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_MBOX); - dev_err(sdev->dev, "error: load fw failed ret: %d\n", ret); - } + else + snd_sof_dsp_dbg_dump(sdev, "Firmware download failed", + SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_MBOX); cleanup: /* diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index c04646647637..8977a65b5704 100644 --- a/sound/soc/sof/loader.c +++ b/sound/soc/sof/loader.c @@ -820,8 +820,8 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev) /* boot the firmware on the DSP */ ret = snd_sof_dsp_run(sdev); if (ret < 0) { - dev_err(sdev->dev, "error: failed to start DSP\n"); - snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_PCI); + snd_sof_dsp_dbg_dump(sdev, "Failed to start DSP", + SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_PCI); return ret; } @@ -835,8 +835,8 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev) sdev->fw_state > SOF_FW_BOOT_IN_PROGRESS, msecs_to_jiffies(sdev->boot_timeout)); if (ret == 0) { - dev_err(sdev->dev, "error: firmware boot failure\n"); - snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX | + snd_sof_dsp_dbg_dump(sdev, "Firmware boot failure due to timeout", + SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_TEXT | SOF_DBG_DUMP_PCI); sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED); return -EIO; diff --git a/sound/soc/sof/ops.c b/sound/soc/sof/ops.c index 9abf7a8e55e0..edfd080a3e4f 100644 --- a/sound/soc/sof/ops.c +++ b/sound/soc/sof/ops.c @@ -171,12 +171,11 @@ void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset, bool non_recoverabl * if all dumps should be printed */ if (non_recoverable || sof_debug_check_flag(SOF_DBG_PRINT_ALL_DUMPS)) { - dev_err(sdev->dev, "DSP panic!\n"); - /* We want to see the DSP panic! */ sdev->dbg_dump_printed = false; - snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); + snd_sof_dsp_dbg_dump(sdev, "DSP panic!", + SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); snd_sof_trace_notify_for_error(sdev); } } diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index bca7d35536e4..ffe7456e7713 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -274,7 +274,7 @@ snd_sof_dsp_set_power_state(struct snd_sof_dev *sdev, } /* debug */ -void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, u32 flags); +void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, const char *msg, u32 flags); static inline int snd_sof_debugfs_add_region_item(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type, u32 offset, size_t size, -- cgit v1.2.3 From 4e1f86482189ddbef73f7be8c6e62e8e3730e6b9 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 23 Dec 2021 13:36:15 +0200 Subject: ASoC: SOF: Introduce new firmware state: SOF_FW_CRASHED The SOF_FW_CRASHED state is meant to indicate the unfortunate case when the firmware has crashed after a successful boot. IPC tx timeout is not treated as indication of a firmware crash as it tends to happen regularly while the firmware is operational. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Reviewed-by: Kai Vehmanen Reviewed-by: Paul Olaru Link: https://lore.kernel.org/r/20211223113628.18582-8-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/debug.c | 1 + sound/soc/sof/ipc.c | 2 +- sound/soc/sof/ops.c | 2 ++ sound/soc/sof/pm.c | 7 +++++++ sound/soc/sof/sof-priv.h | 1 + 5 files changed, 12 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index cf7d95c33afe..5941316751dd 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -939,6 +939,7 @@ static const struct soc_fw_state_info { {SOF_FW_BOOT_FAILED, "SOF_FW_BOOT_FAILED"}, {SOF_FW_BOOT_READY_FAILED, "SOF_FW_BOOT_READY_FAILED"}, {SOF_FW_BOOT_COMPLETE, "SOF_FW_BOOT_COMPLETE"}, + {SOF_FW_CRASHED, "SOF_FW_CRASHED"}, }; static void snd_sof_dbg_print_fw_state(struct snd_sof_dev *sdev) diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 12860da1d373..898f261e8603 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -302,7 +302,7 @@ static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc, u32 header, struct snd_sof_ipc_msg *msg; int ret; - if (ipc->disable_ipc_tx) + if (ipc->disable_ipc_tx || sdev->fw_state == SOF_FW_CRASHED) return -ENODEV; /* diff --git a/sound/soc/sof/ops.c b/sound/soc/sof/ops.c index edfd080a3e4f..ed46f33ce72b 100644 --- a/sound/soc/sof/ops.c +++ b/sound/soc/sof/ops.c @@ -176,6 +176,8 @@ void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset, bool non_recoverabl snd_sof_dsp_dbg_dump(sdev, "DSP panic!", SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); + if (non_recoverable) + sof_set_fw_state(sdev, SOF_FW_CRASHED); snd_sof_trace_notify_for_error(sdev); } } diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index ac8ae6e422a7..f22b5ee23478 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -312,6 +312,13 @@ int snd_sof_prepare(struct device *dev) /* will suspend to S3 by default */ sdev->system_suspend_target = SOF_SUSPEND_S3; + /* + * if the firmware is crashed then we try to aim for S3 to reboot the + * firmware + */ + if (sdev->fw_state == SOF_FW_CRASHED) + return 0; + if (!desc->use_acpi_target_states) return 0; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 0f849cdbfbc8..9bb30b2a516f 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -382,6 +382,7 @@ enum snd_sof_fw_state { SOF_FW_BOOT_FAILED, SOF_FW_BOOT_READY_FAILED, /* firmware booted but fw_ready op failed */ SOF_FW_BOOT_COMPLETE, + SOF_FW_CRASHED, }; /* -- cgit v1.2.3 From b2e9eb3adb9a498b997b18852773e75d7af3b60d Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 23 Dec 2021 13:36:16 +0200 Subject: ASoC: SOF: Introduce new firmware state: SOF_FW_BOOT_READY_OK The SOF_FW_BOOT_READY_OK fw_state indicates that the boot ready message has been received and there were no errors found. The SOF_FW_BOOT_COMPLETE state will be reached after the snd_sof_dsp_post_fw_run() completes without error. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Reviewed-by: Kai Vehmanen Reviewed-by: Paul Olaru Link: https://lore.kernel.org/r/20211223113628.18582-9-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/debug.c | 1 + sound/soc/sof/ipc.c | 2 +- sound/soc/sof/loader.c | 7 ++++--- sound/soc/sof/sof-priv.h | 1 + 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index 5941316751dd..75aaf0da87a0 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -938,6 +938,7 @@ static const struct soc_fw_state_info { {SOF_FW_BOOT_IN_PROGRESS, "SOF_FW_BOOT_IN_PROGRESS"}, {SOF_FW_BOOT_FAILED, "SOF_FW_BOOT_FAILED"}, {SOF_FW_BOOT_READY_FAILED, "SOF_FW_BOOT_READY_FAILED"}, + {SOF_FW_BOOT_READY_OK, "SOF_FW_BOOT_READY_OK"}, {SOF_FW_BOOT_COMPLETE, "SOF_FW_BOOT_COMPLETE"}, {SOF_FW_CRASHED, "SOF_FW_CRASHED"}, }; diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 898f261e8603..bbd539071ac5 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -536,7 +536,7 @@ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) if (err < 0) sof_set_fw_state(sdev, SOF_FW_BOOT_READY_FAILED); else - sof_set_fw_state(sdev, SOF_FW_BOOT_COMPLETE); + sof_set_fw_state(sdev, SOF_FW_BOOT_READY_OK); /* wake up firmware loader */ wake_up(&sdev->boot_wait); diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index 8977a65b5704..f81f24732799 100644 --- a/sound/soc/sof/loader.c +++ b/sound/soc/sof/loader.c @@ -842,9 +842,7 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev) return -EIO; } - if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) - dev_dbg(sdev->dev, "firmware boot complete\n"); - else + if (sdev->fw_state == SOF_FW_BOOT_READY_FAILED) return -EIO; /* FW boots but fw_ready op failed */ /* perform post fw run operations */ @@ -854,6 +852,9 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev) return ret; } + dev_dbg(sdev->dev, "firmware boot complete\n"); + sof_set_fw_state(sdev, SOF_FW_BOOT_COMPLETE); + return 0; } EXPORT_SYMBOL(snd_sof_run_firmware); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 9bb30b2a516f..c92103a028ff 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -381,6 +381,7 @@ enum snd_sof_fw_state { SOF_FW_BOOT_IN_PROGRESS, SOF_FW_BOOT_FAILED, SOF_FW_BOOT_READY_FAILED, /* firmware booted but fw_ready op failed */ + SOF_FW_BOOT_READY_OK, SOF_FW_BOOT_COMPLETE, SOF_FW_CRASHED, }; -- cgit v1.2.3 From fc179420fde3821c4d191e81b4f7b05c1dab87e2 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 23 Dec 2021 13:36:17 +0200 Subject: ASoC: SOF: Move the definition of enum snd_sof_fw_state to global header Move the enum snd_sof_fw_state to include/sound/sof.h to be accessible outside of the core SOF stack. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Reviewed-by: Kai Vehmanen Reviewed-by: Paul Olaru Link: https://lore.kernel.org/r/20211223113628.18582-10-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/sof.h | 22 ++++++++++++++++++++++ sound/soc/sof/sof-priv.h | 11 ----------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/include/sound/sof.h b/include/sound/sof.h index 23b374311d16..b9131c01eefd 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -17,6 +17,28 @@ struct snd_sof_dsp_ops; +/** + * enum snd_sof_fw_state - DSP firmware state definitions + * @SOF_FW_BOOT_NOT_STARTED: firmware boot is not yet started + * @SOF_FW_BOOT_PREPARE: preparing for boot (firmware loading for exaqmple) + * @SOF_FW_BOOT_IN_PROGRESS: firmware boot is in progress + * @SOF_FW_BOOT_FAILED: firmware boot failed + * @SOF_FW_BOOT_READY_FAILED: firmware booted but fw_ready op failed + * @SOF_FW_BOOT_READY_OK: firmware booted and fw_ready op passed + * @SOF_FW_BOOT_COMPLETE: firmware is booted up and functional + * @SOF_FW_CRASHED: firmware crashed after successful boot + */ +enum snd_sof_fw_state { + SOF_FW_BOOT_NOT_STARTED = 0, + SOF_FW_BOOT_PREPARE, + SOF_FW_BOOT_IN_PROGRESS, + SOF_FW_BOOT_FAILED, + SOF_FW_BOOT_READY_FAILED, + SOF_FW_BOOT_READY_OK, + SOF_FW_BOOT_COMPLETE, + SOF_FW_CRASHED, +}; + /* * SOF Platform data. */ diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index c92103a028ff..a1ebc89b216a 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -375,17 +375,6 @@ struct snd_sof_ipc_msg { bool ipc_complete; }; -enum snd_sof_fw_state { - SOF_FW_BOOT_NOT_STARTED = 0, - SOF_FW_BOOT_PREPARE, - SOF_FW_BOOT_IN_PROGRESS, - SOF_FW_BOOT_FAILED, - SOF_FW_BOOT_READY_FAILED, /* firmware booted but fw_ready op failed */ - SOF_FW_BOOT_READY_OK, - SOF_FW_BOOT_COMPLETE, - SOF_FW_CRASHED, -}; - /* * SOF Device Level. */ -- cgit v1.2.3 From d41607d37c1385da799f9a2ddb10c460e573687e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 23 Dec 2021 13:36:18 +0200 Subject: ASoC: SOF: Rename 'enum snd_sof_fw_state' to 'enum sof_fw_state' Since there is nothing SND about the firmware state, rename the enum from `snd_sof_fw_state` to simply `sof_fw_state` Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Reviewed-by: Kai Vehmanen Reviewed-by: Paul Olaru Link: https://lore.kernel.org/r/20211223113628.18582-11-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/sof.h | 4 ++-- sound/soc/sof/debug.c | 2 +- sound/soc/sof/sof-priv.h | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/sound/sof.h b/include/sound/sof.h index b9131c01eefd..813680ab9aad 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -18,7 +18,7 @@ struct snd_sof_dsp_ops; /** - * enum snd_sof_fw_state - DSP firmware state definitions + * enum sof_fw_state - DSP firmware state definitions * @SOF_FW_BOOT_NOT_STARTED: firmware boot is not yet started * @SOF_FW_BOOT_PREPARE: preparing for boot (firmware loading for exaqmple) * @SOF_FW_BOOT_IN_PROGRESS: firmware boot is in progress @@ -28,7 +28,7 @@ struct snd_sof_dsp_ops; * @SOF_FW_BOOT_COMPLETE: firmware is booted up and functional * @SOF_FW_CRASHED: firmware crashed after successful boot */ -enum snd_sof_fw_state { +enum sof_fw_state { SOF_FW_BOOT_NOT_STARTED = 0, SOF_FW_BOOT_PREPARE, SOF_FW_BOOT_IN_PROGRESS, diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index 75aaf0da87a0..d3640ff33134 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -930,7 +930,7 @@ void snd_sof_free_debug(struct snd_sof_dev *sdev) EXPORT_SYMBOL_GPL(snd_sof_free_debug); static const struct soc_fw_state_info { - enum snd_sof_fw_state state; + enum sof_fw_state state; const char *name; } fw_state_dbg[] = { {SOF_FW_BOOT_NOT_STARTED, "SOF_FW_BOOT_NOT_STARTED"}, diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index a1ebc89b216a..44ae8d8d1333 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -399,7 +399,7 @@ struct snd_sof_dev { /* DSP firmware boot */ wait_queue_head_t boot_wait; - enum snd_sof_fw_state fw_state; + enum sof_fw_state fw_state; bool first_boot; /* work queue in case the probe is implemented in two steps */ @@ -591,7 +591,7 @@ extern const struct dsp_arch_ops sof_xtensa_arch_ops; * Firmware state tracking */ static inline void sof_set_fw_state(struct snd_sof_dev *sdev, - enum snd_sof_fw_state new_state) + enum sof_fw_state new_state) { if (sdev->fw_state == new_state) return; -- cgit v1.2.3 From 9421ff7665f66452f61ee40566c6f562d3847873 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 23 Dec 2021 13:36:19 +0200 Subject: ASoC: SOF: ipc: Only allow sending of an IPC in SOF_FW_BOOT_COMPLETE state If the state of the firmware is not BOOT_COMPLETE, it means that the firmware is not functioning, thus it is not capable of handling IPC messages. Do not try to send IPC if the state is not BOOT_COMPLETE Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20211223113628.18582-12-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index bbd539071ac5..5bcf906d90af 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -302,7 +302,7 @@ static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc, u32 header, struct snd_sof_ipc_msg *msg; int ret; - if (ipc->disable_ipc_tx || sdev->fw_state == SOF_FW_CRASHED) + if (ipc->disable_ipc_tx || sdev->fw_state != SOF_FW_BOOT_COMPLETE) return -ENODEV; /* -- cgit v1.2.3 From e2406275be2b6b15d985f33aec921e6555e4f87a Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 23 Dec 2021 13:36:20 +0200 Subject: ASoC: SOF: Set SOF_FW_BOOT_FAILED in case we have failure during boot Change the fw_state to SOF_FW_BOOT_FAILED if we encountered an error during booting the firmware. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20211223113628.18582-13-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/core.c | 2 ++ sound/soc/sof/loader.c | 1 - sound/soc/sof/pm.c | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 00f8ffee2866..aa7a721f34e4 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -202,6 +202,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) if (ret < 0) { dev_err(sdev->dev, "error: failed to load DSP firmware %d\n", ret); + sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED); goto fw_load_err; } @@ -215,6 +216,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) if (ret < 0) { dev_err(sdev->dev, "error: failed to boot DSP firmware %d\n", ret); + sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED); goto fw_run_err; } diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index f81f24732799..697f03565a70 100644 --- a/sound/soc/sof/loader.c +++ b/sound/soc/sof/loader.c @@ -838,7 +838,6 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev) snd_sof_dsp_dbg_dump(sdev, "Firmware boot failure due to timeout", SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_TEXT | SOF_DBG_DUMP_PCI); - sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED); return -EIO; } diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index f22b5ee23478..022b19669735 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -130,6 +130,7 @@ static int sof_resume(struct device *dev, bool runtime_resume) dev_err(sdev->dev, "error: failed to load DSP firmware after resume %d\n", ret); + sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED); return ret; } @@ -144,6 +145,7 @@ static int sof_resume(struct device *dev, bool runtime_resume) dev_err(sdev->dev, "error: failed to boot DSP firmware after resume %d\n", ret); + sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED); return ret; } -- cgit v1.2.3 From b54b3a4e08bc0210768a1839af2ff888376cae4c Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 23 Dec 2021 13:36:21 +0200 Subject: ASoC: SOF: pm: Force DSP off on suspend in BOOT_FAILED state also Try to force the DSP to be turned off next time if the fw_state is either CRASHED or BOOT_FAILED when a suspend happens in order to attempt a clean boot to recover. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20211223113628.18582-14-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/pm.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 022b19669735..197a88695fef 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -315,10 +315,11 @@ int snd_sof_prepare(struct device *dev) sdev->system_suspend_target = SOF_SUSPEND_S3; /* - * if the firmware is crashed then we try to aim for S3 to reboot the - * firmware + * if the firmware is crashed or boot failed then we try to aim for S3 + * to reboot the firmware */ - if (sdev->fw_state == SOF_FW_CRASHED) + if (sdev->fw_state == SOF_FW_CRASHED || + sdev->fw_state == SOF_FW_BOOT_FAILED) return 0; if (!desc->use_acpi_target_states) -- cgit v1.2.3 From 9f89a988d5c222f2fba495bbc861a476bdf1bd30 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 23 Dec 2021 13:36:22 +0200 Subject: ASoc: SOF: core: Update the FW boot state transition diagram Update the state flow diagram to reflect the current implementation. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20211223113628.18582-15-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/core.c | 52 ++++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index aa7a721f34e4..bc3d7192bdda 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -114,32 +114,32 @@ EXPORT_SYMBOL(snd_sof_get_status); /* * FW Boot State Transition Diagram * - * +-----------------------------------------------------------------------+ - * | | - * ------------------ ------------------ | - * | | | | | - * | BOOT_FAILED | | READY_FAILED |-------------------------+ | - * | | | | | | - * ------------------ ------------------ | | - * ^ ^ | | - * | | | | - * (FW Boot Timeout) (FW_READY FAIL) | | - * | | | | - * | | | | - * ------------------ | ------------------ | | - * | | | | | | | - * | IN_PROGRESS |---------------+------------->| COMPLETE | | | - * | | (FW Boot OK) (FW_READY OK) | | | | - * ------------------ ------------------ | | - * ^ | | | - * | | | | - * (FW Loading OK) (System Suspend/Runtime Suspend) - * | | | | - * | | | | - * ------------------ ------------------ | | | - * | | | |<-----+ | | - * | PREPARE | | NOT_STARTED |<---------------------+ | - * | | | |<---------------------------+ + * +----------------------------------------------------------------------+ + * | | + * ------------------ ------------------ | + * | | | | | + * | BOOT_FAILED |<-------| READY_FAILED | | + * | |<--+ | | ------------------ | + * ------------------ | ------------------ | | | + * ^ | ^ | CRASHED |---+ | + * | | | | | | | + * (FW Boot Timeout) | (FW_READY FAIL) ------------------ | | + * | | | ^ | | + * | | | |(DSP Panic) | | + * ------------------ | | ------------------ | | + * | | | | | | | | + * | IN_PROGRESS |---------------+------------->| COMPLETE | | | + * | | (FW Boot OK) (FW_READY OK) | | | | + * ------------------ | ------------------ | | + * ^ | | | | + * | | | | | + * (FW Loading OK) | (System Suspend/Runtime Suspend) + * | | | | | + * | (FW Loading Fail) | | | + * ------------------ | ------------------ | | | + * | | | | |<-----+ | | + * | PREPARE |---+ | NOT_STARTED |<---------------------+ | + * | | | |<--------------------------+ * ------------------ ------------------ * | ^ | ^ * | | | | -- cgit v1.2.3 From fdc573b1c26a8859996de6fbae2d436511b74e00 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 23 Dec 2021 13:36:23 +0200 Subject: ASoC: SOF: ops: Always print DSP Panic message but use different message Never suppress the DSP panic dump as it is always originates from an assert() or panic() call within the firmware. Use different message for DSP panics when there will be recovery attempt going to be done compared to a definitive DSP panic. Suggested-by: Chao Song Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Chao Song Reviewed-by: Kai Vehmanen Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20211223113628.18582-16-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ops.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/sound/soc/sof/ops.c b/sound/soc/sof/ops.c index ed46f33ce72b..235e2ef72178 100644 --- a/sound/soc/sof/ops.c +++ b/sound/soc/sof/ops.c @@ -167,18 +167,21 @@ void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset, bool non_recoverabl __func__, sdev->dsp_oops_offset, offset); /* - * Only print the panic information if we have non recoverable panic or - * if all dumps should be printed + * Set the fw_state to crashed only in case of non recoverable DSP panic + * event. + * Use different message within the snd_sof_dsp_dbg_dump() depending on + * the non_recoverable flag. */ - if (non_recoverable || sof_debug_check_flag(SOF_DBG_PRINT_ALL_DUMPS)) { - /* We want to see the DSP panic! */ - sdev->dbg_dump_printed = false; - + sdev->dbg_dump_printed = false; + if (non_recoverable) { snd_sof_dsp_dbg_dump(sdev, "DSP panic!", SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); - if (non_recoverable) - sof_set_fw_state(sdev, SOF_FW_CRASHED); + sof_set_fw_state(sdev, SOF_FW_CRASHED); snd_sof_trace_notify_for_error(sdev); + } else { + snd_sof_dsp_dbg_dump(sdev, + "DSP panic (recovery will be attempted)", + SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); } } EXPORT_SYMBOL(snd_sof_dsp_panic); -- cgit v1.2.3 From b9f0bfd16d8b390b35dbec67c3ed74e74a0ade24 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 23 Dec 2021 13:36:24 +0200 Subject: ASoC: SOF: dsp_arch_ops: add kernel log level parameter for oops and stack To allow custom log level to be used for the DSP oops and stack print, add a kernel log level parameter to the two ops. Modify the xtensa oops and stack functions tom use this new log level parameter. Pass KER_ERR from snd_sof_get_status() to make sure that there is no functional change with this new parameter. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Chao Song Reviewed-by: Kai Vehmanen Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20211223113628.18582-17-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/core.c | 4 ++-- sound/soc/sof/sof-priv.h | 15 ++++++++------- sound/soc/sof/xtensa/core.c | 44 +++++++++++++++++++++++++------------------- 3 files changed, 35 insertions(+), 28 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index bc3d7192bdda..c3630ecc7d89 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -106,8 +106,8 @@ void snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, out: dev_err(sdev->dev, "panic at %s:%d\n", panic_info->filename, panic_info->linenum); - sof_oops(sdev, oops); - sof_stack(sdev, oops, stack, stack_words); + sof_oops(sdev, KERN_ERR, oops); + sof_stack(sdev, KERN_ERR, oops, stack, stack_words); } EXPORT_SYMBOL(snd_sof_get_status); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 44ae8d8d1333..598f858f0e1b 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -309,8 +309,8 @@ struct snd_sof_dsp_ops { /* DSP architecture specific callbacks for oops and stack dumps */ struct dsp_arch_ops { - void (*dsp_oops)(struct snd_sof_dev *sdev, void *oops); - void (*dsp_stack)(struct snd_sof_dev *sdev, void *oops, + void (*dsp_oops)(struct snd_sof_dev *sdev, const char *level, void *oops); + void (*dsp_stack)(struct snd_sof_dev *sdev, const char *level, void *oops, u32 *stack, u32 stack_words); }; @@ -573,16 +573,17 @@ int snd_sof_debugfs_add_region_item_iomem(struct snd_sof_dev *sdev, /* * DSP Architectures. */ -static inline void sof_stack(struct snd_sof_dev *sdev, void *oops, u32 *stack, - u32 stack_words) +static inline void sof_stack(struct snd_sof_dev *sdev, const char *level, + void *oops, u32 *stack, u32 stack_words) { - sof_dsp_arch_ops(sdev)->dsp_stack(sdev, oops, stack, stack_words); + sof_dsp_arch_ops(sdev)->dsp_stack(sdev, level, oops, stack, + stack_words); } -static inline void sof_oops(struct snd_sof_dev *sdev, void *oops) +static inline void sof_oops(struct snd_sof_dev *sdev, const char *level, void *oops) { if (sof_dsp_arch_ops(sdev)->dsp_oops) - sof_dsp_arch_ops(sdev)->dsp_oops(sdev, oops); + sof_dsp_arch_ops(sdev)->dsp_oops(sdev, level, oops); } extern const struct dsp_arch_ops sof_xtensa_arch_ops; diff --git a/sound/soc/sof/xtensa/core.c b/sound/soc/sof/xtensa/core.c index bd09c3825caf..bebbe3a2865c 100644 --- a/sound/soc/sof/xtensa/core.c +++ b/sound/soc/sof/xtensa/core.c @@ -81,33 +81,39 @@ static const struct xtensa_exception_cause xtensa_exception_causes[] = { }; /* only need xtensa atm */ -static void xtensa_dsp_oops(struct snd_sof_dev *sdev, void *oops) +static void xtensa_dsp_oops(struct snd_sof_dev *sdev, const char *level, void *oops) { struct sof_ipc_dsp_oops_xtensa *xoops = oops; int i; - dev_err(sdev->dev, "error: DSP Firmware Oops\n"); + dev_printk(level, sdev->dev, "error: DSP Firmware Oops\n"); for (i = 0; i < ARRAY_SIZE(xtensa_exception_causes); i++) { if (xtensa_exception_causes[i].id == xoops->exccause) { - dev_err(sdev->dev, "error: Exception Cause: %s, %s\n", - xtensa_exception_causes[i].msg, - xtensa_exception_causes[i].description); + dev_printk(level, sdev->dev, + "error: Exception Cause: %s, %s\n", + xtensa_exception_causes[i].msg, + xtensa_exception_causes[i].description); } } - dev_err(sdev->dev, "EXCCAUSE 0x%8.8x EXCVADDR 0x%8.8x PS 0x%8.8x SAR 0x%8.8x\n", - xoops->exccause, xoops->excvaddr, xoops->ps, xoops->sar); - dev_err(sdev->dev, "EPC1 0x%8.8x EPC2 0x%8.8x EPC3 0x%8.8x EPC4 0x%8.8x", - xoops->epc1, xoops->epc2, xoops->epc3, xoops->epc4); - dev_err(sdev->dev, "EPC5 0x%8.8x EPC6 0x%8.8x EPC7 0x%8.8x DEPC 0x%8.8x", - xoops->epc5, xoops->epc6, xoops->epc7, xoops->depc); - dev_err(sdev->dev, "EPS2 0x%8.8x EPS3 0x%8.8x EPS4 0x%8.8x EPS5 0x%8.8x", - xoops->eps2, xoops->eps3, xoops->eps4, xoops->eps5); - dev_err(sdev->dev, "EPS6 0x%8.8x EPS7 0x%8.8x INTENABL 0x%8.8x INTERRU 0x%8.8x", - xoops->eps6, xoops->eps7, xoops->intenable, xoops->interrupt); + dev_printk(level, sdev->dev, + "EXCCAUSE 0x%8.8x EXCVADDR 0x%8.8x PS 0x%8.8x SAR 0x%8.8x\n", + xoops->exccause, xoops->excvaddr, xoops->ps, xoops->sar); + dev_printk(level, sdev->dev, + "EPC1 0x%8.8x EPC2 0x%8.8x EPC3 0x%8.8x EPC4 0x%8.8x", + xoops->epc1, xoops->epc2, xoops->epc3, xoops->epc4); + dev_printk(level, sdev->dev, + "EPC5 0x%8.8x EPC6 0x%8.8x EPC7 0x%8.8x DEPC 0x%8.8x", + xoops->epc5, xoops->epc6, xoops->epc7, xoops->depc); + dev_printk(level, sdev->dev, + "EPS2 0x%8.8x EPS3 0x%8.8x EPS4 0x%8.8x EPS5 0x%8.8x", + xoops->eps2, xoops->eps3, xoops->eps4, xoops->eps5); + dev_printk(level, sdev->dev, + "EPS6 0x%8.8x EPS7 0x%8.8x INTENABL 0x%8.8x INTERRU 0x%8.8x", + xoops->eps6, xoops->eps7, xoops->intenable, xoops->interrupt); } -static void xtensa_stack(struct snd_sof_dev *sdev, void *oops, u32 *stack, - u32 stack_words) +static void xtensa_stack(struct snd_sof_dev *sdev, const char *level, void *oops, + u32 *stack, u32 stack_words) { struct sof_ipc_dsp_oops_xtensa *xoops = oops; u32 stack_ptr = xoops->plat_hdr.stackptr; @@ -115,7 +121,7 @@ static void xtensa_stack(struct snd_sof_dev *sdev, void *oops, u32 *stack, unsigned char buf[4 * 8 + 3 + 1]; int i; - dev_err(sdev->dev, "stack dump from 0x%8.8x\n", stack_ptr); + dev_printk(level, sdev->dev, "stack dump from 0x%8.8x\n", stack_ptr); /* * example output: @@ -124,7 +130,7 @@ static void xtensa_stack(struct snd_sof_dev *sdev, void *oops, u32 *stack, for (i = 0; i < stack_words; i += 4) { hex_dump_to_buffer(stack + i, 16, 16, 4, buf, sizeof(buf), false); - dev_err(sdev->dev, "0x%08x: %s\n", stack_ptr + i * 4, buf); + dev_printk(level, sdev->dev, "0x%08x: %s\n", stack_ptr + i * 4, buf); } } -- cgit v1.2.3 From 4995ffce2ce2164fa507a5dbaf1aa38bab679cca Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 23 Dec 2021 13:36:25 +0200 Subject: ASoC: SOF: Rename snd_sof_get_status() and add kernel log level parameter The snd_sof_get_status() is not the best name for a function which in fact is tasked to print out DSP oops and stack. Rename it to sof_print_oops_and_stack(). At the same time add a new parameter to specify the desired kernel log level to be used for the prints. When updating the users of the function, pass KERN_ERR for now to make sure that there is no functional change happens. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Chao Song Reviewed-by: Kai Vehmanen Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20211223113628.18582-18-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/core.c | 45 ++++++++++++++++++++++++++---------------- sound/soc/sof/imx/imx-common.c | 4 ++-- sound/soc/sof/intel/atom.c | 4 ++-- sound/soc/sof/intel/bdw.c | 4 ++-- sound/soc/sof/intel/hda.c | 4 ++-- sound/soc/sof/sof-priv.h | 8 ++++---- 6 files changed, 40 insertions(+), 29 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index c3630ecc7d89..8f32b5b12b3e 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -68,23 +68,33 @@ static const struct sof_panic_msg panic_msg[] = { {SOF_IPC_PANIC_ASSERT, "assertion failed"}, }; -/* +/** + * sof_print_oops_and_stack - Handle the printing of DSP oops and stack trace + * @sdev: Pointer to the device's sdev + * @level: prink log level to use for the printing + * @panic_code: the panic code + * @tracep_code: tracepoint code + * @oops: Pointer to DSP specific oops data + * @panic_info: Pointer to the received panic information message + * @stack: Pointer to the call stack data + * @stack_words: Number of words in the stack data + * * helper to be called from .dbg_dump callbacks. No error code is * provided, it's left as an exercise for the caller of .dbg_dump * (typically IPC or loader) */ -void snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, - u32 tracep_code, void *oops, - struct sof_ipc_panic_info *panic_info, - void *stack, size_t stack_words) +void sof_print_oops_and_stack(struct snd_sof_dev *sdev, const char *level, + u32 panic_code, u32 tracep_code, void *oops, + struct sof_ipc_panic_info *panic_info, + void *stack, size_t stack_words) { u32 code; int i; /* is firmware dead ? */ if ((panic_code & SOF_IPC_PANIC_MAGIC_MASK) != SOF_IPC_PANIC_MAGIC) { - dev_err(sdev->dev, "unexpected fault %#010x trace %#010x\n", - panic_code, tracep_code); + dev_printk(level, sdev->dev, "unexpected fault %#010x trace %#010x\n", + panic_code, tracep_code); return; /* no fault ? */ } @@ -92,24 +102,25 @@ void snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, for (i = 0; i < ARRAY_SIZE(panic_msg); i++) { if (panic_msg[i].id == code) { - dev_err(sdev->dev, "reason: %s (%#x)\n", panic_msg[i].msg, - code & SOF_IPC_PANIC_CODE_MASK); - dev_err(sdev->dev, "trace point: %#010x\n", tracep_code); + dev_printk(level, sdev->dev, "reason: %s (%#x)\n", + panic_msg[i].msg, code & SOF_IPC_PANIC_CODE_MASK); + dev_printk(level, sdev->dev, "trace point: %#010x\n", tracep_code); goto out; } } /* unknown error */ - dev_err(sdev->dev, "unknown panic code: %#x\n", code & SOF_IPC_PANIC_CODE_MASK); - dev_err(sdev->dev, "trace point: %#010x\n", tracep_code); + dev_printk(level, sdev->dev, "unknown panic code: %#x\n", + code & SOF_IPC_PANIC_CODE_MASK); + dev_printk(level, sdev->dev, "trace point: %#010x\n", tracep_code); out: - dev_err(sdev->dev, "panic at %s:%d\n", panic_info->filename, - panic_info->linenum); - sof_oops(sdev, KERN_ERR, oops); - sof_stack(sdev, KERN_ERR, oops, stack, stack_words); + dev_printk(level, sdev->dev, "panic at %s:%d\n", panic_info->filename, + panic_info->linenum); + sof_oops(sdev, level, oops); + sof_stack(sdev, level, oops, stack, stack_words); } -EXPORT_SYMBOL(snd_sof_get_status); +EXPORT_SYMBOL(sof_print_oops_and_stack); /* * FW Boot State Transition Diagram diff --git a/sound/soc/sof/imx/imx-common.c b/sound/soc/sof/imx/imx-common.c index 9371e9062cb1..36e3d414a18f 100644 --- a/sound/soc/sof/imx/imx-common.c +++ b/sound/soc/sof/imx/imx-common.c @@ -69,8 +69,8 @@ void imx8_dump(struct snd_sof_dev *sdev, u32 flags) IMX8_STACK_DUMP_SIZE); /* Print the information to the console */ - snd_sof_get_status(sdev, status, status, &xoops, &panic_info, stack, - IMX8_STACK_DUMP_SIZE); + sof_print_oops_and_stack(sdev, KERN_ERR, status, status, &xoops, + &panic_info, stack, IMX8_STACK_DUMP_SIZE); } EXPORT_SYMBOL(imx8_dump); diff --git a/sound/soc/sof/intel/atom.c b/sound/soc/sof/intel/atom.c index bcb2eb2acf2e..ff5900b155dc 100644 --- a/sound/soc/sof/intel/atom.c +++ b/sound/soc/sof/intel/atom.c @@ -70,8 +70,8 @@ void atom_dump(struct snd_sof_dev *sdev, u32 flags) panic = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCX); atom_get_registers(sdev, &xoops, &panic_info, stack, STACK_DUMP_SIZE); - snd_sof_get_status(sdev, status, panic, &xoops, &panic_info, stack, - STACK_DUMP_SIZE); + sof_print_oops_and_stack(sdev, KERN_ERR, status, panic, &xoops, + &panic_info, stack, STACK_DUMP_SIZE); /* provide some context for firmware debug */ imrx = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IMRX); diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c index 10c9a0b39371..d627b7498d5e 100644 --- a/sound/soc/sof/intel/bdw.c +++ b/sound/soc/sof/intel/bdw.c @@ -258,8 +258,8 @@ static void bdw_dump(struct snd_sof_dev *sdev, u32 flags) panic = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IPCX); bdw_get_registers(sdev, &xoops, &panic_info, stack, BDW_STACK_DUMP_SIZE); - snd_sof_get_status(sdev, status, panic, &xoops, &panic_info, stack, - BDW_STACK_DUMP_SIZE); + sof_print_oops_and_stack(sdev, KERN_ERR, status, panic, &xoops, + &panic_info, stack, BDW_STACK_DUMP_SIZE); /* provide some context for firmware debug */ imrx = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IMRX); diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 21100d2e6644..97027530ecef 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -554,8 +554,8 @@ void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags) hda_dsp_get_registers(sdev, &xoops, &panic_info, stack, HDA_DSP_STACK_DUMP_SIZE); - snd_sof_get_status(sdev, status, panic, &xoops, &panic_info, - stack, HDA_DSP_STACK_DUMP_SIZE); + sof_print_oops_and_stack(sdev, KERN_ERR, status, panic, &xoops, + &panic_info, stack, HDA_DSP_STACK_DUMP_SIZE); } else { hda_dsp_dump_ext_rom_status(sdev, flags); } diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 598f858f0e1b..5fbd4f29321a 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -559,10 +559,10 @@ int snd_sof_debugfs_buf_item(struct snd_sof_dev *sdev, int snd_sof_trace_update_pos(struct snd_sof_dev *sdev, struct sof_ipc_dma_trace_posn *posn); void snd_sof_trace_notify_for_error(struct snd_sof_dev *sdev); -void snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, - u32 tracep_code, void *oops, - struct sof_ipc_panic_info *panic_info, - void *stack, size_t stack_words); +void sof_print_oops_and_stack(struct snd_sof_dev *sdev, const char *level, + u32 panic_code, u32 tracep_code, void *oops, + struct sof_ipc_panic_info *panic_info, + void *stack, size_t stack_words); int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev); void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev); int snd_sof_dbg_memory_info_init(struct snd_sof_dev *sdev); -- cgit v1.2.3 From beb6ade168177bf6c43abe78b3c9512b260b8068 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 23 Dec 2021 13:36:26 +0200 Subject: ASoC: SOF: Add clarifying comments for sof_core_debug and DSP dump flags Update the comment for the global SOF level debug flags and add one for the flags used to control the DSP dump functionality. Document the expected behavior when the SOF_DBG_DUMP_OPTIONAL is passed for the DSP dump: Only print the dump if SOF_DBG_PRINT_ALL_DUMPS is set Print must use KERN_DEBUG log level Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Chao Song Reviewed-by: Kai Vehmanen Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20211223113628.18582-19-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-priv.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 5fbd4f29321a..087935192ce8 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -20,7 +20,7 @@ #include #include -/* debug flags */ +/* Flag definitions used in sof_core_debug (sof_debug module parameter) */ #define SOF_DBG_ENABLE_TRACE BIT(0) #define SOF_DBG_RETAIN_CTX BIT(1) /* prevent DSP D3 on FW exception */ #define SOF_DBG_VERIFY_TPLG BIT(2) /* verify topology during load */ @@ -35,11 +35,13 @@ */ #define SOF_DBG_PRINT_ALL_DUMPS BIT(6) /* Print all ipc and dsp dumps */ +/* Flag definitions used for controlling the DSP dump behavior */ #define SOF_DBG_DUMP_REGS BIT(0) #define SOF_DBG_DUMP_MBOX BIT(1) #define SOF_DBG_DUMP_TEXT BIT(2) #define SOF_DBG_DUMP_PCI BIT(3) -#define SOF_DBG_DUMP_OPTIONAL BIT(4) /* only dump if SOF_DBG_PRINT_ALL_DUMPS is set */ +/* Output this dump (at the DEBUG level) only when SOF_DBG_PRINT_ALL_DUMPS is set */ +#define SOF_DBG_DUMP_OPTIONAL BIT(4) /* global debug state set by SOF_DBG_ flags */ bool sof_debug_check_flag(int mask); -- cgit v1.2.3 From 0152b8a2f0831b03bb7483159ef28167dcd33ab0 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 23 Dec 2021 13:36:27 +0200 Subject: ASoC: SOF: debug: Use DEBUG log level for optional prints If the user requested to see all dumps (even the optional ones) then use KERN_DEBUG level for the optional dumps as they are only for debugging purposes. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Chao Song Reviewed-by: Kai Vehmanen Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20211223113628.18582-20-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/debug.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index d3640ff33134..6d6757075f7c 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -943,38 +943,42 @@ static const struct soc_fw_state_info { {SOF_FW_CRASHED, "SOF_FW_CRASHED"}, }; -static void snd_sof_dbg_print_fw_state(struct snd_sof_dev *sdev) +static void snd_sof_dbg_print_fw_state(struct snd_sof_dev *sdev, const char *level) { int i; for (i = 0; i < ARRAY_SIZE(fw_state_dbg); i++) { if (sdev->fw_state == fw_state_dbg[i].state) { - dev_err(sdev->dev, "fw_state: %s (%d)\n", fw_state_dbg[i].name, i); + dev_printk(level, sdev->dev, "fw_state: %s (%d)\n", + fw_state_dbg[i].name, i); return; } } - dev_err(sdev->dev, "fw_state: UNKNOWN (%d)\n", sdev->fw_state); + dev_printk(level, sdev->dev, "fw_state: UNKNOWN (%d)\n", sdev->fw_state); } void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, const char *msg, u32 flags) { + char *level = flags & SOF_DBG_DUMP_OPTIONAL ? KERN_DEBUG : KERN_ERR; bool print_all = sof_debug_check_flag(SOF_DBG_PRINT_ALL_DUMPS); if (flags & SOF_DBG_DUMP_OPTIONAL && !print_all) return; if (sof_ops(sdev)->dbg_dump && !sdev->dbg_dump_printed) { - dev_err(sdev->dev, "------------[ DSP dump start ]------------\n"); + dev_printk(level, sdev->dev, + "------------[ DSP dump start ]------------\n"); if (msg) - dev_err(sdev->dev, "%s\n", msg); - snd_sof_dbg_print_fw_state(sdev); + dev_printk(level, sdev->dev, "%s\n", msg); + snd_sof_dbg_print_fw_state(sdev, level); sof_ops(sdev)->dbg_dump(sdev, flags); - dev_err(sdev->dev, "------------[ DSP dump end ]------------\n"); + dev_printk(level, sdev->dev, + "------------[ DSP dump end ]------------\n"); if (!print_all) sdev->dbg_dump_printed = true; } else if (msg) { - dev_err(sdev->dev, "%s\n", msg); + dev_printk(level, sdev->dev, "%s\n", msg); } } EXPORT_SYMBOL(snd_sof_dsp_dbg_dump); -- cgit v1.2.3 From 34bfba9a63ece79c683591e757899e61fbcaa753 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 23 Dec 2021 13:36:28 +0200 Subject: ASoC: SOF: Intel: hda: Use DEBUG log level for optional prints If the user requested to see all dumps (even the optional ones) then use KERN_DEBUG level for the optional dumps as they are only for debugging purposes. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Chao Song Reviewed-by: Kai Vehmanen Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20211223113628.18582-21-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 97027530ecef..18abbd13d593 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -474,7 +474,7 @@ static const struct hda_dsp_msg_code hda_dsp_rom_msg[] = { {HDA_DSP_ROM_NULL_FW_ENTRY, "error: null FW entry point"}, }; -static void hda_dsp_get_status(struct snd_sof_dev *sdev) +static void hda_dsp_get_status(struct snd_sof_dev *sdev, const char *level) { u32 status; int i; @@ -484,8 +484,8 @@ static void hda_dsp_get_status(struct snd_sof_dev *sdev) for (i = 0; i < ARRAY_SIZE(hda_dsp_rom_msg); i++) { if (status == hda_dsp_rom_msg[i].code) { - dev_err(sdev->dev, "%s - code %8.8x\n", - hda_dsp_rom_msg[i].msg, status); + dev_printk(level, sdev->dev, "%s - code %8.8x\n", + hda_dsp_rom_msg[i].msg, status); return; } } @@ -523,7 +523,8 @@ static void hda_dsp_get_registers(struct snd_sof_dev *sdev, } /* dump the first 8 dwords representing the extended ROM status */ -static void hda_dsp_dump_ext_rom_status(struct snd_sof_dev *sdev, u32 flags) +static void hda_dsp_dump_ext_rom_status(struct snd_sof_dev *sdev, const char *level, + u32 flags) { char msg[128]; int len = 0; @@ -535,18 +536,19 @@ static void hda_dsp_dump_ext_rom_status(struct snd_sof_dev *sdev, u32 flags) len += snprintf(msg + len, sizeof(msg) - len, " 0x%x", value); } - dev_err(sdev->dev, "extended rom status: %s", msg); + dev_printk(level, sdev->dev, "extended rom status: %s", msg); } void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags) { + char *level = flags & SOF_DBG_DUMP_OPTIONAL ? KERN_DEBUG : KERN_ERR; struct sof_ipc_dsp_oops_xtensa xoops; struct sof_ipc_panic_info panic_info; u32 stack[HDA_DSP_STACK_DUMP_SIZE]; /* print ROM/FW status */ - hda_dsp_get_status(sdev); + hda_dsp_get_status(sdev, level); if (flags & SOF_DBG_DUMP_REGS) { u32 status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_SRAM_REG_FW_STATUS); @@ -554,10 +556,10 @@ void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags) hda_dsp_get_registers(sdev, &xoops, &panic_info, stack, HDA_DSP_STACK_DUMP_SIZE); - sof_print_oops_and_stack(sdev, KERN_ERR, status, panic, &xoops, + sof_print_oops_and_stack(sdev, level, status, panic, &xoops, &panic_info, stack, HDA_DSP_STACK_DUMP_SIZE); } else { - hda_dsp_dump_ext_rom_status(sdev, flags); + hda_dsp_dump_ext_rom_status(sdev, level, flags); } } -- cgit v1.2.3 From c6cef35bf723ef0152258d15179c725c5f8cbeba Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Thu, 23 Dec 2021 13:24:33 +0100 Subject: ASoC: dt-bindings: spdif-dit: add missing sound-name-prefix property This is used in meson-axg, meson-g12 and meson-gx. Add the property to the binding. This fixes the dtschema warning: audio-codec-0: 'sound-name-prefix' does not match any of the regexes: 'pinctrl-[0-9]+' Signed-off-by: Alexander Stein Link: https://lore.kernel.org/r/20211223122434.39378-3-alexander.stein@mailbox.org Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/linux,spdif-dit.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/linux,spdif-dit.yaml b/Documentation/devicetree/bindings/sound/linux,spdif-dit.yaml index c6b070e1d014..a4f9257e313d 100644 --- a/Documentation/devicetree/bindings/sound/linux,spdif-dit.yaml +++ b/Documentation/devicetree/bindings/sound/linux,spdif-dit.yaml @@ -9,6 +9,9 @@ title: Dummy SPDIF Transmitter Device Tree Bindings maintainers: - Mark Brown +allOf: + - $ref: name-prefix.yaml# + properties: compatible: const: linux,spdif-dit @@ -16,6 +19,8 @@ properties: "#sound-dai-cells": const: 0 + sound-name-prefix: true + required: - "#sound-dai-cells" - compatible -- cgit v1.2.3 From 559ec82aa47d7c9ce39f3c769f1ba5f3237f6869 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Thu, 23 Dec 2021 13:24:34 +0100 Subject: ASoC: dt-bindings: aiu: spdif-dit: add missing sound-name-prefix property This is used in meson-gx. Add the property to the binding. This fixes the dtschema warning: audio-controller@5400: 'sound-name-prefix' does not match any of the regexes: 'pinctrl-[0-9]+' Signed-off-by: Alexander Stein Link: https://lore.kernel.org/r/20211223122434.39378-4-alexander.stein@mailbox.org Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/amlogic,aiu.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/amlogic,aiu.yaml b/Documentation/devicetree/bindings/sound/amlogic,aiu.yaml index f50558ed914f..0705f91199a0 100644 --- a/Documentation/devicetree/bindings/sound/amlogic,aiu.yaml +++ b/Documentation/devicetree/bindings/sound/amlogic,aiu.yaml @@ -9,6 +9,9 @@ title: Amlogic AIU audio output controller maintainers: - Jerome Brunet +allOf: + - $ref: name-prefix.yaml# + properties: $nodename: pattern: "^audio-controller@.*" @@ -65,6 +68,8 @@ properties: resets: maxItems: 1 + sound-name-prefix: true + required: - "#sound-dai-cells" - compatible -- cgit v1.2.3 From 8a2d8e4fed6d5829ec3681af313d63e02bc22dad Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Thu, 23 Dec 2021 16:22:12 +0800 Subject: ASoC: codec: tlv320adc3xxx: Fix missing clk_disable_unprepare() on error in adc3xxx_i2c_probe() Fix the missing clk_disable_unprepare() before return from adc3xxx_i2c_probe() in the error handling case. Fixes: e9a3b57efd28 ("ASoC: codec: tlv320adc3xxx: New codec driver") Reported-by: Hulk Robot Signed-off-by: Yang Yingliang Link: https://lore.kernel.org/r/20211223082212.3342184-1-yangyingliang@huawei.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320adc3xxx.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/tlv320adc3xxx.c b/sound/soc/codecs/tlv320adc3xxx.c index a683bda7eb36..4baf3d881633 100644 --- a/sound/soc/codecs/tlv320adc3xxx.c +++ b/sound/soc/codecs/tlv320adc3xxx.c @@ -1232,21 +1232,21 @@ static int adc3xxx_i2c_probe(struct i2c_client *i2c, ret = adc3xxx_parse_dt_gpio(adc3xxx, "ti,dmdin-gpio1", &adc3xxx->gpio_cfg[0]); if (ret < 0) - return ret; + goto err_unprepare_mclk; ret = adc3xxx_parse_dt_gpio(adc3xxx, "ti,dmclk-gpio2", &adc3xxx->gpio_cfg[1]); if (ret < 0) - return ret; + goto err_unprepare_mclk; ret = adc3xxx_parse_dt_micbias(adc3xxx, "ti,micbias1-vg", &adc3xxx->micbias_vg[0]); if (ret < 0) - return ret; + goto err_unprepare_mclk; ret = adc3xxx_parse_dt_micbias(adc3xxx, "ti,micbias2-vg", &adc3xxx->micbias_vg[1]); if (ret < 0) - return ret; + goto err_unprepare_mclk; adc3xxx->regmap = devm_regmap_init_i2c(i2c, &adc3xxx_regmap); if (IS_ERR(adc3xxx->regmap)) { ret = PTR_ERR(adc3xxx->regmap); - return ret; + goto err_unprepare_mclk; } i2c_set_clientdata(i2c, adc3xxx); @@ -1263,9 +1263,15 @@ static int adc3xxx_i2c_probe(struct i2c_client *i2c, ret = snd_soc_register_component(dev, &soc_component_dev_adc3xxx, &adc3xxx_dai, 1); - if (ret < 0) + if (ret < 0) { dev_err(dev, "Failed to register codec: %d\n", ret); + goto err_unprepare_mclk; + } + + return 0; +err_unprepare_mclk: + clk_disable_unprepare(adc3xxx->mclk); return ret; } -- cgit v1.2.3 From 4e28491a7a198c668437f2be8a91a76aa52f20eb Mon Sep 17 00:00:00 2001 From: Tzung-Bi Shih Date: Tue, 14 Dec 2021 12:00:28 +0800 Subject: ASoC: mediatek: mt8192-mt6359: fix device_node leak The of_parse_phandle() document: >>> Use of_node_put() on it when done. The driver didn't call of_node_put(). Fixes the leak. Signed-off-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20211214040028.2992627-1-tzungbi@google.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c index 1d16939f80e3..2552f30d8fe4 100644 --- a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c +++ b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c @@ -1172,7 +1172,11 @@ static int mt8192_mt6359_dev_probe(struct platform_device *pdev) return ret; } - return devm_snd_soc_register_card(&pdev->dev, card); + ret = devm_snd_soc_register_card(&pdev->dev, card); + + of_node_put(platform_node); + of_node_put(hdmi_codec); + return ret; } #ifdef CONFIG_OF -- cgit v1.2.3 From 3d4641a42ccf1593b3f3a474ee7541727acbb8e0 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Tue, 14 Dec 2021 15:20:46 +0100 Subject: ASoC: core: Add snd_soc_of_parse_pin_switches() from simple-card-utils The ASoC core already has several helpers to parse card properties from the device tree. Move the parsing code for "pin-switches" from simple-card-utils to a shared snd_soc_of_parse_pin_switches() function so other drivers can also use it to set up pin switches configured in the device tree. Cc: Paul Cercueil Signed-off-by: Stephan Gerhold Link: https://lore.kernel.org/r/20211214142049.20422-2-stephan@gerhold.net Signed-off-by: Mark Brown --- include/sound/soc.h | 1 + sound/soc/generic/simple-card-utils.c | 45 +------------------------------ sound/soc/soc-core.c | 50 +++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 44 deletions(-) diff --git a/include/sound/soc.h b/include/sound/soc.h index 5872a8864f3b..7a1650b303f1 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1211,6 +1211,7 @@ int snd_soc_of_parse_card_name(struct snd_soc_card *card, const char *propname); int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card, const char *propname); +int snd_soc_of_parse_pin_switches(struct snd_soc_card *card, const char *prop); int snd_soc_of_get_slot_mask(struct device_node *np, const char *prop_name, unsigned int *mask); diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 850e968677f1..a81323d1691d 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -499,57 +499,14 @@ EXPORT_SYMBOL_GPL(asoc_simple_parse_widgets); int asoc_simple_parse_pin_switches(struct snd_soc_card *card, char *prefix) { - const unsigned int nb_controls_max = 16; - const char **strings, *control_name; - struct snd_kcontrol_new *controls; - struct device *dev = card->dev; - unsigned int i, nb_controls; char prop[128]; - int ret; if (!prefix) prefix = ""; snprintf(prop, sizeof(prop), "%s%s", prefix, "pin-switches"); - if (!of_property_read_bool(dev->of_node, prop)) - return 0; - - strings = devm_kcalloc(dev, nb_controls_max, - sizeof(*strings), GFP_KERNEL); - if (!strings) - return -ENOMEM; - - ret = of_property_read_string_array(dev->of_node, prop, - strings, nb_controls_max); - if (ret < 0) - return ret; - - nb_controls = (unsigned int)ret; - - controls = devm_kcalloc(dev, nb_controls, - sizeof(*controls), GFP_KERNEL); - if (!controls) - return -ENOMEM; - - for (i = 0; i < nb_controls; i++) { - control_name = devm_kasprintf(dev, GFP_KERNEL, - "%s Switch", strings[i]); - if (!control_name) - return -ENOMEM; - - controls[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; - controls[i].name = control_name; - controls[i].info = snd_soc_dapm_info_pin_switch; - controls[i].get = snd_soc_dapm_get_pin_switch; - controls[i].put = snd_soc_dapm_put_pin_switch; - controls[i].private_value = (unsigned long)strings[i]; - } - - card->controls = controls; - card->num_controls = nb_controls; - - return 0; + return snd_soc_of_parse_pin_switches(card, prop); } EXPORT_SYMBOL_GPL(asoc_simple_parse_pin_switches); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 1d62160f96b1..434e61b46983 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2823,6 +2823,56 @@ int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_simple_widgets); +int snd_soc_of_parse_pin_switches(struct snd_soc_card *card, const char *prop) +{ + const unsigned int nb_controls_max = 16; + const char **strings, *control_name; + struct snd_kcontrol_new *controls; + struct device *dev = card->dev; + unsigned int i, nb_controls; + int ret; + + if (!of_property_read_bool(dev->of_node, prop)) + return 0; + + strings = devm_kcalloc(dev, nb_controls_max, + sizeof(*strings), GFP_KERNEL); + if (!strings) + return -ENOMEM; + + ret = of_property_read_string_array(dev->of_node, prop, + strings, nb_controls_max); + if (ret < 0) + return ret; + + nb_controls = (unsigned int)ret; + + controls = devm_kcalloc(dev, nb_controls, + sizeof(*controls), GFP_KERNEL); + if (!controls) + return -ENOMEM; + + for (i = 0; i < nb_controls; i++) { + control_name = devm_kasprintf(dev, GFP_KERNEL, + "%s Switch", strings[i]); + if (!control_name) + return -ENOMEM; + + controls[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + controls[i].name = control_name; + controls[i].info = snd_soc_dapm_info_pin_switch; + controls[i].get = snd_soc_dapm_get_pin_switch; + controls[i].put = snd_soc_dapm_put_pin_switch; + controls[i].private_value = (unsigned long)strings[i]; + } + + card->controls = controls; + card->num_controls = nb_controls; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_of_parse_pin_switches); + int snd_soc_of_get_slot_mask(struct device_node *np, const char *prop_name, unsigned int *mask) -- cgit v1.2.3 From 37a49da9a7d5ac1f7128000de42ff222da46ba7a Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Tue, 14 Dec 2021 15:20:47 +0100 Subject: ASoC: dt-bindings: qcom: sm8250: Document "pin-switches" and "widgets" Some sound card setups might require extra pin switches to allow turning off certain audio components. There are two real examples for this in smartphones/tablets based on MSM8916: 1. Analog speaker amplifiers connected to headphone outputs. The MSM8916 analog codec does not have a separate "Line Out" port so some devices have an analog speaker amplifier connected to one of the headphone outputs. A pin switch is necessary to allow playback on headphones without also activating the speaker. 2. External speaker codec also used as earpiece. Some smartphones have two front-facing (stereo) speakers that can be also configured to act as an earpiece during voice calls. A pin switch is needed to allow disabling the second speaker during voice calls. There are existing bindings that allow setting up such pin switches in simple-card.yaml. Document the same for Qcom sound cards. One variant of example 1 above is added to the examples in the DT schema: There is an analog speaker amplifier connected to the HPH_R (right headphone channel) output. Adding a "Speaker" pin switch and widget allows turning off the speaker when audio should be only played via the connected headphones. Cc: Srinivas Kandagatla Signed-off-by: Stephan Gerhold Acked-by: Rob Herring Link: https://lore.kernel.org/r/20211214142049.20422-3-stephan@gerhold.net Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/qcom,sm8250.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml index e50964c54bb9..4bfda04b4608 100644 --- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml @@ -39,6 +39,14 @@ properties: $ref: /schemas/types.yaml#/definitions/string description: User visible long sound card name + pin-switches: + description: List of widget names for which pin switches should be created. + $ref: /schemas/types.yaml#/definitions/string-array + + widgets: + description: User specified audio sound widgets. + $ref: /schemas/types.yaml#/definitions/non-unique-string-array + # Only valid for some compatibles (see allOf if below) reg: true reg-names: true @@ -251,7 +259,15 @@ examples: reg-names = "mic-iomux", "spkr-iomux"; model = "msm8916"; + widgets = + "Speaker", "Speaker", + "Headphone", "Headphones"; + pin-switches = "Speaker"; audio-routing = + "Speaker", "Speaker Amp OUT", + "Speaker Amp IN", "HPH_R", + "Headphones", "HPH_L", + "Headphones", "HPH_R", "AMIC1", "MIC BIAS Internal1", "AMIC2", "MIC BIAS Internal2", "AMIC3", "MIC BIAS Internal3"; -- cgit v1.2.3 From 2623e66de125ba153e41be6a0b8af24cae8aa436 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Tue, 14 Dec 2021 15:20:48 +0100 Subject: ASoC: qcom: common: Parse "pin-switches" and "widgets" from DT Use the DT helpers in the ASoC core to parse the "pin-switches" and "widgets" properties from the device tree. This allows adding extra mixers to disable e.g. an extra speaker amplifier that would be normally powered on automatically because it is connected to a shared output pin. Cc: Srinivas Kandagatla Signed-off-by: Stephan Gerhold Link: https://lore.kernel.org/r/20211214142049.20422-4-stephan@gerhold.net Signed-off-by: Mark Brown --- sound/soc/qcom/common.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c index e1bf04d00625..c407684ce1a2 100644 --- a/sound/soc/qcom/common.c +++ b/sound/soc/qcom/common.c @@ -26,6 +26,12 @@ int qcom_snd_parse_of(struct snd_soc_card *card) return ret; } + if (of_property_read_bool(dev->of_node, "widgets")) { + ret = snd_soc_of_parse_audio_simple_widgets(card, "widgets"); + if (ret) + return ret; + } + /* DAPM routes */ if (of_property_read_bool(dev->of_node, "audio-routing")) { ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); @@ -39,6 +45,10 @@ int qcom_snd_parse_of(struct snd_soc_card *card) return ret; } + ret = snd_soc_of_parse_pin_switches(card, "pin-switches"); + if (ret) + return ret; + ret = snd_soc_of_parse_aux_devs(card, "aux-devs"); if (ret) return ret; -- cgit v1.2.3 From 319a05330f4ff3f951f9c42094958c6cdef393b3 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Tue, 14 Dec 2021 15:20:49 +0100 Subject: ASoC: msm8916-wcd-analog: Use separate outputs for HPH_L/HPH_R The analog codec has separate output paths for the left headphone channel (HPH_L) and the right headphone channel (HPH_R). While they are usually used together for actual headphones output, some devices also have an analog speaker amplifier connected to one of the headphone channels. To allow modelling that properly (and to avoid powering on the unneeded output path), HPH_L and HPH_R should be represented by separate outputs rather than a shared HEADPHONE output that always activates both paths. Cc: Srinivas Kandagatla Signed-off-by: Stephan Gerhold Link: https://lore.kernel.org/r/20211214142049.20422-5-stephan@gerhold.net Signed-off-by: Mark Brown --- sound/soc/codecs/msm8916-wcd-analog.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c index 3ddd822240e3..485cda46dbb9 100644 --- a/sound/soc/codecs/msm8916-wcd-analog.c +++ b/sound/soc/codecs/msm8916-wcd-analog.c @@ -822,8 +822,8 @@ static const struct snd_soc_dapm_route pm8916_wcd_analog_audio_map[] = { {"EAR PA", NULL, "EAR CP"}, /* Headset (RX MIX1 and RX MIX2) */ - {"HEADPHONE", NULL, "HPHL PA"}, - {"HEADPHONE", NULL, "HPHR PA"}, + {"HPH_L", NULL, "HPHL PA"}, + {"HPH_R", NULL, "HPHR PA"}, {"HPHL DAC", NULL, "EAR_HPHL_CLK"}, {"HPHR DAC", NULL, "EAR_HPHR_CLK"}, @@ -870,7 +870,8 @@ static const struct snd_soc_dapm_widget pm8916_wcd_analog_dapm_widgets[] = { SND_SOC_DAPM_INPUT("AMIC3"), SND_SOC_DAPM_INPUT("AMIC2"), SND_SOC_DAPM_OUTPUT("EAR"), - SND_SOC_DAPM_OUTPUT("HEADPHONE"), + SND_SOC_DAPM_OUTPUT("HPH_L"), + SND_SOC_DAPM_OUTPUT("HPH_R"), /* RX stuff */ SND_SOC_DAPM_SUPPLY("INT_LDO_H", SND_SOC_NOPM, 1, 0, NULL, 0), -- cgit v1.2.3 From da893a93eaf8eb2bce03862e00b9998463eeaecf Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 24 Dec 2021 10:10:28 +0800 Subject: ASOC: SOF: Intel: use snd_soc_dai_get_widget() We have a helper, use it to simplify widget lookup Suggested-by: Peter Ujfalusi Signed-off-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20211224021034.26635-2-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 18abbd13d593..99255028d3fe 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -189,10 +189,7 @@ static int sdw_params_stream(struct device *dev, struct snd_soc_dai *d = params_data->dai; struct snd_soc_dapm_widget *w; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - w = d->playback_widget; - else - w = d->capture_widget; + w = snd_soc_dai_get_widget(d, substream->stream); return sdw_dai_config_ipc(sdev, w, params_data->link_id, params_data->alh_stream_id, d->id, true); @@ -206,10 +203,7 @@ static int sdw_free_stream(struct device *dev, struct snd_soc_dai *d = free_data->dai; struct snd_soc_dapm_widget *w; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - w = d->playback_widget; - else - w = d->capture_widget; + w = snd_soc_dai_get_widget(d, substream->stream); /* send invalid stream_id */ return sdw_dai_config_ipc(sdev, w, free_data->link_id, 0xFFFF, d->id, false); -- cgit v1.2.3 From b86947b52f0d0e5b6e6f0510933ca13aad266e47 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 24 Dec 2021 10:10:29 +0800 Subject: ASoC/soundwire: intel: simplify callbacks for params/hw_free We don't really need to pass a substream to the callback, we only need the direction. No functionality change, only simplification to enable improve suspend with paused streams. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Signed-off-by: Bard Liao Acked-By: Vinod Koul Link: https://lore.kernel.org/r/20211224021034.26635-3-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- drivers/soundwire/intel.c | 14 +++++++------- include/linux/soundwire/sdw_intel.h | 4 ++-- sound/soc/sof/intel/hda.c | 6 ++---- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 78037ffdb09b..25c5f5b9f058 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -711,7 +711,7 @@ intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) } static int intel_params_stream(struct sdw_intel *sdw, - struct snd_pcm_substream *substream, + int stream, struct snd_soc_dai *dai, struct snd_pcm_hw_params *hw_params, int link_id, int alh_stream_id) @@ -719,7 +719,7 @@ static int intel_params_stream(struct sdw_intel *sdw, struct sdw_intel_link_res *res = sdw->link_res; struct sdw_intel_stream_params_data params_data; - params_data.substream = substream; + params_data.stream = stream; /* direction */ params_data.dai = dai; params_data.hw_params = hw_params; params_data.link_id = link_id; @@ -732,14 +732,14 @@ static int intel_params_stream(struct sdw_intel *sdw, } static int intel_free_stream(struct sdw_intel *sdw, - struct snd_pcm_substream *substream, + int stream, struct snd_soc_dai *dai, int link_id) { struct sdw_intel_link_res *res = sdw->link_res; struct sdw_intel_stream_free_data free_data; - free_data.substream = substream; + free_data.stream = stream; /* direction */ free_data.dai = dai; free_data.link_id = link_id; @@ -876,7 +876,7 @@ static int intel_hw_params(struct snd_pcm_substream *substream, dma->hw_params = params; /* Inform DSP about PDI stream number */ - ret = intel_params_stream(sdw, substream, dai, params, + ret = intel_params_stream(sdw, substream->stream, dai, params, sdw->instance, pdi->intel_alh_id); if (ret) @@ -953,7 +953,7 @@ static int intel_prepare(struct snd_pcm_substream *substream, sdw_cdns_config_stream(cdns, ch, dir, dma->pdi); /* Inform DSP about PDI stream number */ - ret = intel_params_stream(sdw, substream, dai, + ret = intel_params_stream(sdw, substream->stream, dai, dma->hw_params, sdw->instance, dma->pdi->intel_alh_id); @@ -987,7 +987,7 @@ intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) return ret; } - ret = intel_free_stream(sdw, substream, dai, sdw->instance); + ret = intel_free_stream(sdw, substream->stream, dai, sdw->instance); if (ret < 0) { dev_err(dai->dev, "intel_free_stream: failed %d\n", ret); return ret; diff --git a/include/linux/soundwire/sdw_intel.h b/include/linux/soundwire/sdw_intel.h index 8a463b8fc12a..67e0d3e750b5 100644 --- a/include/linux/soundwire/sdw_intel.h +++ b/include/linux/soundwire/sdw_intel.h @@ -92,7 +92,7 @@ * firmware. */ struct sdw_intel_stream_params_data { - struct snd_pcm_substream *substream; + int stream; struct snd_soc_dai *dai; struct snd_pcm_hw_params *hw_params; int link_id; @@ -105,7 +105,7 @@ struct sdw_intel_stream_params_data { * firmware. */ struct sdw_intel_stream_free_data { - struct snd_pcm_substream *substream; + int stream; struct snd_soc_dai *dai; int link_id; }; diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 99255028d3fe..c8fb082209ce 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -184,12 +184,11 @@ static int sdw_dai_config_ipc(struct snd_sof_dev *sdev, static int sdw_params_stream(struct device *dev, struct sdw_intel_stream_params_data *params_data) { - struct snd_pcm_substream *substream = params_data->substream; struct snd_sof_dev *sdev = dev_get_drvdata(dev); struct snd_soc_dai *d = params_data->dai; struct snd_soc_dapm_widget *w; - w = snd_soc_dai_get_widget(d, substream->stream); + w = snd_soc_dai_get_widget(d, params_data->stream); return sdw_dai_config_ipc(sdev, w, params_data->link_id, params_data->alh_stream_id, d->id, true); @@ -198,12 +197,11 @@ static int sdw_params_stream(struct device *dev, static int sdw_free_stream(struct device *dev, struct sdw_intel_stream_free_data *free_data) { - struct snd_pcm_substream *substream = free_data->substream; struct snd_sof_dev *sdev = dev_get_drvdata(dev); struct snd_soc_dai *d = free_data->dai; struct snd_soc_dapm_widget *w; - w = snd_soc_dai_get_widget(d, substream->stream); + w = snd_soc_dai_get_widget(d, free_data->stream); /* send invalid stream_id */ return sdw_dai_config_ipc(sdev, w, free_data->link_id, 0xFFFF, d->id, false); -- cgit v1.2.3 From 8ddeafb957a9a6dd33b2c80309d726d3141df08f Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Fri, 24 Dec 2021 10:10:30 +0800 Subject: soundwire: intel: improve suspend flows This patch provides both a simplification of the suspend flows and a better balanced operation during suspend/resume transition, as part of the transition of Sound Open Firmware (SOF) to dynamic pipelines: the DSP resources are only enabled when required instead of enabled on startup. The exiting code relies on a convoluted way of dealing with suspend signals. Since there is no .suspend DAI callback, we used the component .suspend and marked all the component DAI dmas as 'suspended'. The information was used in the .prepare stage to differentiate resume operations from xrun handling, and only reinitialize SHIM registers and DMA in the former case. While this solution has been working reliably for about 2 years, there is a much better solution consisting in trapping the TRIGGER_SUSPEND in the .trigger DAI ops. The DMA is still marked in the same way for the .prepare op to run, but in addition the callbacks sent to DSP firmware are now balanced. Normal operation: hw_params -> intel_params_stream hw_free -> intel_free_stream suspend -> intel_free_stream prepare -> intel_params_stream This balanced operation was not required with existing SOF firmware relying on static pipelines instantiated at every boot. With the on-going transition to dynamic pipelines, it's however a requirement to keep the use count for the DAI widget balanced across all transitions. The component suspend is not removed but instead modified to deal with a corner case: when a substream is PAUSED, the ALSA core does not throw the TRIGGER_SUSPEND. This is problematic since the refcount for all pipelines and widgets is not balanced, leading to issues on resume. The trigger callback keeps track of the 'paused' state with a new flag, which is tested during the component suspend called later to release the remaining DSP resources. These resources will be re-enabled in the .prepare step. The IPC used in the TRIGGER_SUSPEND to release DSP resources is not a problem since the BE dailink is already marked as non-atomic. Co-developed-by: Pierre-Louis Bossart Signed-off-by: Pierre-Louis Bossart Signed-off-by: Ranjani Sridharan Signed-off-by: Bard Liao Acked-By: Vinod Koul Link: https://lore.kernel.org/r/20211224021034.26635-4-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- drivers/soundwire/cadence_master.h | 2 + drivers/soundwire/intel.c | 110 +++++++++++++++++++++++++++++-------- 2 files changed, 89 insertions(+), 23 deletions(-) diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index e587aede63bf..aa4b9b0eb2a8 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -86,6 +86,7 @@ struct sdw_cdns_stream_config { * @link_id: Master link id * @hw_params: hw_params to be applied in .prepare step * @suspended: status set when suspended, to be used in .prepare + * @paused: status set in .trigger, to be used in suspend */ struct sdw_cdns_dma_data { char *name; @@ -96,6 +97,7 @@ struct sdw_cdns_dma_data { int link_id; struct snd_pcm_hw_params *hw_params; bool suspended; + bool paused; }; /** diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 25c5f5b9f058..3d29f02ad5a6 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -871,6 +871,7 @@ static int intel_hw_params(struct snd_pcm_substream *substream, sdw_cdns_config_stream(cdns, ch, dir, pdi); /* store pdi and hw_params, may be needed in prepare step */ + dma->paused = false; dma->suspended = false; dma->pdi = pdi; dma->hw_params = params; @@ -1008,29 +1009,6 @@ static void intel_shutdown(struct snd_pcm_substream *substream, pm_runtime_put_autosuspend(cdns->dev); } -static int intel_component_dais_suspend(struct snd_soc_component *component) -{ - struct sdw_cdns_dma_data *dma; - struct snd_soc_dai *dai; - - for_each_component_dais(component, dai) { - /* - * we don't have a .suspend dai_ops, and we don't have access - * to the substream, so let's mark both capture and playback - * DMA contexts as suspended - */ - dma = dai->playback_dma_data; - if (dma) - dma->suspended = true; - - dma = dai->capture_dma_data; - if (dma) - dma->suspended = true; - } - - return 0; -} - static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai, void *stream, int direction) { @@ -1059,11 +1037,97 @@ static void *intel_get_sdw_stream(struct snd_soc_dai *dai, return dma->stream; } +static int intel_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) +{ + struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); + struct sdw_intel *sdw = cdns_to_intel(cdns); + struct sdw_cdns_dma_data *dma; + int ret = 0; + + dma = snd_soc_dai_get_dma_data(dai, substream); + if (!dma) { + dev_err(dai->dev, "failed to get dma data in %s\n", + __func__); + return -EIO; + } + + switch (cmd) { + case SNDRV_PCM_TRIGGER_SUSPEND: + + /* + * The .prepare callback is used to deal with xruns and resume operations. + * In the case of xruns, the DMAs and SHIM registers cannot be touched, + * but for resume operations the DMAs and SHIM registers need to be initialized. + * the .trigger callback is used to track the suspend case only. + */ + + dma->suspended = true; + + ret = intel_free_stream(sdw, substream->stream, dai, sdw->instance); + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + dma->paused = true; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + dma->paused = false; + break; + default: + break; + } + + return ret; +} + +static int intel_component_dais_suspend(struct snd_soc_component *component) +{ + struct snd_soc_dai *dai; + + /* + * In the corner case where a SUSPEND happens during a PAUSE, the ALSA core + * does not throw the TRIGGER_SUSPEND. This leaves the DAIs in an unbalanced state. + * Since the component suspend is called last, we can trap this corner case + * and force the DAIs to release their resources. + */ + for_each_component_dais(component, dai) { + struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); + struct sdw_intel *sdw = cdns_to_intel(cdns); + struct sdw_cdns_dma_data *dma; + int stream; + int ret; + + dma = dai->playback_dma_data; + stream = SNDRV_PCM_STREAM_PLAYBACK; + if (!dma) { + dma = dai->capture_dma_data; + stream = SNDRV_PCM_STREAM_CAPTURE; + } + + if (!dma) + continue; + + if (dma->suspended) + continue; + + if (dma->paused) { + dma->suspended = true; + + ret = intel_free_stream(sdw, stream, dai, sdw->instance); + if (ret < 0) + return ret; + } + } + + return 0; +} + static const struct snd_soc_dai_ops intel_pcm_dai_ops = { .startup = intel_startup, .hw_params = intel_hw_params, .prepare = intel_prepare, .hw_free = intel_hw_free, + .trigger = intel_trigger, .shutdown = intel_shutdown, .set_sdw_stream = intel_pcm_set_sdw_stream, .get_sdw_stream = intel_get_sdw_stream, -- cgit v1.2.3 From e8444560b4d9302a511f0996f4cfdf85b628f4ca Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 24 Dec 2021 10:10:31 +0800 Subject: ASoC/SoundWire: dai: expand 'stream' concept beyond SoundWire The HDAudio ASoC support relies on the set_tdm_slots() helper to store the HDaudio stream tag in the tx_mask. This only works because of the pre-existing order in soc-pcm.c, where the hw_params() is handled for codec_dais *before* cpu_dais. When the order is reversed, the stream_tag is used as a mask in the codec fixup functions: /* fixup params based on TDM slot masks */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && codec_dai->tx_mask) soc_pcm_codec_params_fixup(&codec_params, codec_dai->tx_mask); As a result of this confusion, the codec_params_fixup() ends-up generating bad channel masks, depending on what stream_tag was allocated. We could add a flag to state that the tx_mask is really not a mask, but it would be quite ugly to persist in overloading concepts. Instead, this patch suggests a more generic get/set 'stream' API based on the existing model for SoundWire. We can expand the concept to store 'stream' opaque information that is specific to different DAI types. In the case of HDAudio DAIs, we only need to store a stream tag as an unsigned char pointer. The TDM rx_ and tx_masks should really only be used to store masks. Rename get_sdw_stream/set_sdw_stream callbacks and helpers as get_stream/set_stream. No functionality change beyond the rename. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Reviewed-by: Ranjani Sridharan Signed-off-by: Bard Liao Acked-By: Vinod Koul Link: https://lore.kernel.org/r/20211224021034.26635-5-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- drivers/soundwire/intel.c | 8 ++++---- drivers/soundwire/qcom.c | 8 ++++---- drivers/soundwire/stream.c | 4 ++-- include/sound/soc-dai.h | 32 ++++++++++++++++---------------- sound/soc/codecs/max98373-sdw.c | 2 +- sound/soc/codecs/rt1308-sdw.c | 2 +- sound/soc/codecs/rt1316-sdw.c | 2 +- sound/soc/codecs/rt5682-sdw.c | 2 +- sound/soc/codecs/rt700.c | 2 +- sound/soc/codecs/rt711-sdca.c | 2 +- sound/soc/codecs/rt711.c | 2 +- sound/soc/codecs/rt715-sdca.c | 2 +- sound/soc/codecs/rt715.c | 2 +- sound/soc/codecs/sdw-mockup.c | 2 +- sound/soc/codecs/wcd938x.c | 2 +- sound/soc/codecs/wsa881x.c | 2 +- sound/soc/intel/boards/sof_sdw.c | 6 +++--- sound/soc/qcom/sdm845.c | 4 ++-- sound/soc/qcom/sm8250.c | 4 ++-- 19 files changed, 45 insertions(+), 45 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 3d29f02ad5a6..70d164372a2b 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1129,8 +1129,8 @@ static const struct snd_soc_dai_ops intel_pcm_dai_ops = { .hw_free = intel_hw_free, .trigger = intel_trigger, .shutdown = intel_shutdown, - .set_sdw_stream = intel_pcm_set_sdw_stream, - .get_sdw_stream = intel_get_sdw_stream, + .set_stream = intel_pcm_set_sdw_stream, + .get_stream = intel_get_sdw_stream, }; static const struct snd_soc_dai_ops intel_pdm_dai_ops = { @@ -1139,8 +1139,8 @@ static const struct snd_soc_dai_ops intel_pdm_dai_ops = { .prepare = intel_prepare, .hw_free = intel_hw_free, .shutdown = intel_shutdown, - .set_sdw_stream = intel_pdm_set_sdw_stream, - .get_sdw_stream = intel_get_sdw_stream, + .set_stream = intel_pdm_set_sdw_stream, + .get_stream = intel_get_sdw_stream, }; static const struct snd_soc_component_driver dai_component = { diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index a317bea2d42d..9d42891ac3d6 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -1024,8 +1024,8 @@ static int qcom_swrm_startup(struct snd_pcm_substream *substream, ctrl->sruntime[dai->id] = sruntime; for_each_rtd_codec_dais(rtd, i, codec_dai) { - ret = snd_soc_dai_set_sdw_stream(codec_dai, sruntime, - substream->stream); + ret = snd_soc_dai_set_stream(codec_dai, sruntime, + substream->stream); if (ret < 0 && ret != -ENOTSUPP) { dev_err(dai->dev, "Failed to set sdw stream on %s\n", codec_dai->name); @@ -1051,8 +1051,8 @@ static const struct snd_soc_dai_ops qcom_swrm_pdm_dai_ops = { .hw_free = qcom_swrm_hw_free, .startup = qcom_swrm_startup, .shutdown = qcom_swrm_shutdown, - .set_sdw_stream = qcom_swrm_set_sdw_stream, - .get_sdw_stream = qcom_swrm_get_sdw_stream, + .set_stream = qcom_swrm_set_sdw_stream, + .get_stream = qcom_swrm_get_sdw_stream, }; static const struct snd_soc_component_driver qcom_swrm_dai_component = { diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 5d4f6b308ef7..980f26d49b66 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1863,7 +1863,7 @@ static int set_stream(struct snd_pcm_substream *substream, /* Set stream pointer on all DAIs */ for_each_rtd_dais(rtd, i, dai) { - ret = snd_soc_dai_set_sdw_stream(dai, sdw_stream, substream->stream); + ret = snd_soc_dai_set_stream(dai, sdw_stream, substream->stream); if (ret < 0) { dev_err(rtd->dev, "failed to set stream pointer on dai %s\n", dai->name); break; @@ -1934,7 +1934,7 @@ void sdw_shutdown_stream(void *sdw_substream) /* Find stream from first CPU DAI */ dai = asoc_rtd_to_cpu(rtd, 0); - sdw_stream = snd_soc_dai_get_sdw_stream(dai, substream->stream); + sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); if (IS_ERR(sdw_stream)) { dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name); diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 5d4dd7c5450b..bbd821d2df9c 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -295,9 +295,9 @@ struct snd_soc_dai_ops { unsigned int *rx_num, unsigned int *rx_slot); int (*set_tristate)(struct snd_soc_dai *dai, int tristate); - int (*set_sdw_stream)(struct snd_soc_dai *dai, - void *stream, int direction); - void *(*get_sdw_stream)(struct snd_soc_dai *dai, int direction); + int (*set_stream)(struct snd_soc_dai *dai, + void *stream, int direction); + void *(*get_stream)(struct snd_soc_dai *dai, int direction); /* * DAI digital mute - optional. @@ -515,42 +515,42 @@ static inline void *snd_soc_dai_get_drvdata(struct snd_soc_dai *dai) } /** - * snd_soc_dai_set_sdw_stream() - Configures a DAI for SDW stream operation + * snd_soc_dai_set_stream() - Configures a DAI for stream operation * @dai: DAI - * @stream: STREAM + * @stream: STREAM (opaque structure depending on DAI type) * @direction: Stream direction(Playback/Capture) - * SoundWire subsystem doesn't have a notion of direction and we reuse + * Some subsystems, such as SoundWire, don't have a notion of direction and we reuse * the ASoC stream direction to configure sink/source ports. * Playback maps to source ports and Capture for sink ports. * * This should be invoked with NULL to clear the stream set previously. * Returns 0 on success, a negative error code otherwise. */ -static inline int snd_soc_dai_set_sdw_stream(struct snd_soc_dai *dai, - void *stream, int direction) +static inline int snd_soc_dai_set_stream(struct snd_soc_dai *dai, + void *stream, int direction) { - if (dai->driver->ops->set_sdw_stream) - return dai->driver->ops->set_sdw_stream(dai, stream, direction); + if (dai->driver->ops->set_stream) + return dai->driver->ops->set_stream(dai, stream, direction); else return -ENOTSUPP; } /** - * snd_soc_dai_get_sdw_stream() - Retrieves SDW stream from DAI + * snd_soc_dai_get_stream() - Retrieves stream from DAI * @dai: DAI * @direction: Stream direction(Playback/Capture) * * This routine only retrieves that was previously configured - * with snd_soc_dai_get_sdw_stream() + * with snd_soc_dai_get_stream() * * Returns pointer to stream or an ERR_PTR value, e.g. * ERR_PTR(-ENOTSUPP) if callback is not supported; */ -static inline void *snd_soc_dai_get_sdw_stream(struct snd_soc_dai *dai, - int direction) +static inline void *snd_soc_dai_get_stream(struct snd_soc_dai *dai, + int direction) { - if (dai->driver->ops->get_sdw_stream) - return dai->driver->ops->get_sdw_stream(dai, direction); + if (dai->driver->ops->get_stream) + return dai->driver->ops->get_stream(dai, direction); else return ERR_PTR(-ENOTSUPP); } diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c index dc520effc61c..f47e956d4f55 100644 --- a/sound/soc/codecs/max98373-sdw.c +++ b/sound/soc/codecs/max98373-sdw.c @@ -741,7 +741,7 @@ static int max98373_sdw_set_tdm_slot(struct snd_soc_dai *dai, static const struct snd_soc_dai_ops max98373_dai_sdw_ops = { .hw_params = max98373_sdw_dai_hw_params, .hw_free = max98373_pcm_hw_free, - .set_sdw_stream = max98373_set_sdw_stream, + .set_stream = max98373_set_sdw_stream, .shutdown = max98373_shutdown, .set_tdm_slot = max98373_sdw_set_tdm_slot, }; diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c index f716668de640..149a76075c76 100644 --- a/sound/soc/codecs/rt1308-sdw.c +++ b/sound/soc/codecs/rt1308-sdw.c @@ -613,7 +613,7 @@ static const struct snd_soc_component_driver soc_component_sdw_rt1308 = { static const struct snd_soc_dai_ops rt1308_aif_dai_ops = { .hw_params = rt1308_sdw_hw_params, .hw_free = rt1308_sdw_pcm_hw_free, - .set_sdw_stream = rt1308_set_sdw_stream, + .set_stream = rt1308_set_sdw_stream, .shutdown = rt1308_sdw_shutdown, .set_tdm_slot = rt1308_sdw_set_tdm_slot, }; diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c index 09b4914bba1b..c66d7b20cb4d 100644 --- a/sound/soc/codecs/rt1316-sdw.c +++ b/sound/soc/codecs/rt1316-sdw.c @@ -602,7 +602,7 @@ static const struct snd_soc_component_driver soc_component_sdw_rt1316 = { static const struct snd_soc_dai_ops rt1316_aif_dai_ops = { .hw_params = rt1316_sdw_hw_params, .hw_free = rt1316_sdw_pcm_hw_free, - .set_sdw_stream = rt1316_set_sdw_stream, + .set_stream = rt1316_set_sdw_stream, .shutdown = rt1316_sdw_shutdown, }; diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c index 31a4f286043e..248257a2e4e0 100644 --- a/sound/soc/codecs/rt5682-sdw.c +++ b/sound/soc/codecs/rt5682-sdw.c @@ -272,7 +272,7 @@ static int rt5682_sdw_hw_free(struct snd_pcm_substream *substream, static const struct snd_soc_dai_ops rt5682_sdw_ops = { .hw_params = rt5682_sdw_hw_params, .hw_free = rt5682_sdw_hw_free, - .set_sdw_stream = rt5682_set_sdw_stream, + .set_stream = rt5682_set_sdw_stream, .shutdown = rt5682_sdw_shutdown, }; diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index 921382724f9c..e61a8257bf64 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -1005,7 +1005,7 @@ static int rt700_pcm_hw_free(struct snd_pcm_substream *substream, static const struct snd_soc_dai_ops rt700_ops = { .hw_params = rt700_pcm_hw_params, .hw_free = rt700_pcm_hw_free, - .set_sdw_stream = rt700_set_sdw_stream, + .set_stream = rt700_set_sdw_stream, .shutdown = rt700_shutdown, }; diff --git a/sound/soc/codecs/rt711-sdca.c b/sound/soc/codecs/rt711-sdca.c index 2e992589f1e4..bdb1375f0338 100644 --- a/sound/soc/codecs/rt711-sdca.c +++ b/sound/soc/codecs/rt711-sdca.c @@ -1358,7 +1358,7 @@ static int rt711_sdca_pcm_hw_free(struct snd_pcm_substream *substream, static const struct snd_soc_dai_ops rt711_sdca_ops = { .hw_params = rt711_sdca_pcm_hw_params, .hw_free = rt711_sdca_pcm_hw_free, - .set_sdw_stream = rt711_sdca_set_sdw_stream, + .set_stream = rt711_sdca_set_sdw_stream, .shutdown = rt711_sdca_shutdown, }; diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c index a7c5608a0ef8..6770825d037a 100644 --- a/sound/soc/codecs/rt711.c +++ b/sound/soc/codecs/rt711.c @@ -1089,7 +1089,7 @@ static int rt711_pcm_hw_free(struct snd_pcm_substream *substream, static const struct snd_soc_dai_ops rt711_ops = { .hw_params = rt711_pcm_hw_params, .hw_free = rt711_pcm_hw_free, - .set_sdw_stream = rt711_set_sdw_stream, + .set_stream = rt711_set_sdw_stream, .shutdown = rt711_shutdown, }; diff --git a/sound/soc/codecs/rt715-sdca.c b/sound/soc/codecs/rt715-sdca.c index 66e166568c50..bfa536bd7196 100644 --- a/sound/soc/codecs/rt715-sdca.c +++ b/sound/soc/codecs/rt715-sdca.c @@ -938,7 +938,7 @@ static int rt715_sdca_pcm_hw_free(struct snd_pcm_substream *substream, static const struct snd_soc_dai_ops rt715_sdca_ops = { .hw_params = rt715_sdca_pcm_hw_params, .hw_free = rt715_sdca_pcm_hw_free, - .set_sdw_stream = rt715_sdca_set_sdw_stream, + .set_stream = rt715_sdca_set_sdw_stream, .shutdown = rt715_sdca_shutdown, }; diff --git a/sound/soc/codecs/rt715.c b/sound/soc/codecs/rt715.c index 1352869cc086..a64d11a74751 100644 --- a/sound/soc/codecs/rt715.c +++ b/sound/soc/codecs/rt715.c @@ -909,7 +909,7 @@ static int rt715_pcm_hw_free(struct snd_pcm_substream *substream, static const struct snd_soc_dai_ops rt715_ops = { .hw_params = rt715_pcm_hw_params, .hw_free = rt715_pcm_hw_free, - .set_sdw_stream = rt715_set_sdw_stream, + .set_stream = rt715_set_sdw_stream, .shutdown = rt715_shutdown, }; diff --git a/sound/soc/codecs/sdw-mockup.c b/sound/soc/codecs/sdw-mockup.c index 8ea13cfa9f8e..7c612aaf31c7 100644 --- a/sound/soc/codecs/sdw-mockup.c +++ b/sound/soc/codecs/sdw-mockup.c @@ -138,7 +138,7 @@ static int sdw_mockup_pcm_hw_free(struct snd_pcm_substream *substream, static const struct snd_soc_dai_ops sdw_mockup_ops = { .hw_params = sdw_mockup_pcm_hw_params, .hw_free = sdw_mockup_pcm_hw_free, - .set_sdw_stream = sdw_mockup_set_sdw_stream, + .set_stream = sdw_mockup_set_sdw_stream, .shutdown = sdw_mockup_shutdown, }; diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c index 67151c7770c6..0b5266c56a3f 100644 --- a/sound/soc/codecs/wcd938x.c +++ b/sound/soc/codecs/wcd938x.c @@ -4287,7 +4287,7 @@ static int wcd938x_codec_set_sdw_stream(struct snd_soc_dai *dai, static const struct snd_soc_dai_ops wcd938x_sdw_dai_ops = { .hw_params = wcd938x_codec_hw_params, .hw_free = wcd938x_codec_free, - .set_sdw_stream = wcd938x_codec_set_sdw_stream, + .set_stream = wcd938x_codec_set_sdw_stream, }; static struct snd_soc_dai_driver wcd938x_dais[] = { diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c index 2da4a5fa7a18..ffc025e01bce 100644 --- a/sound/soc/codecs/wsa881x.c +++ b/sound/soc/codecs/wsa881x.c @@ -1018,7 +1018,7 @@ static const struct snd_soc_dai_ops wsa881x_dai_ops = { .hw_params = wsa881x_hw_params, .hw_free = wsa881x_hw_free, .mute_stream = wsa881x_digital_mute, - .set_sdw_stream = wsa881x_set_sdw_stream, + .set_stream = wsa881x_set_sdw_stream, }; static struct snd_soc_dai_driver wsa881x_dais[] = { diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index de303a980879..da515eb1ddbe 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -347,7 +347,7 @@ int sdw_prepare(struct snd_pcm_substream *substream) /* Find stream from first CPU DAI */ dai = asoc_rtd_to_cpu(rtd, 0); - sdw_stream = snd_soc_dai_get_sdw_stream(dai, substream->stream); + sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); if (IS_ERR(sdw_stream)) { dev_err(rtd->dev, "no stream found for DAI %s", dai->name); @@ -367,7 +367,7 @@ int sdw_trigger(struct snd_pcm_substream *substream, int cmd) /* Find stream from first CPU DAI */ dai = asoc_rtd_to_cpu(rtd, 0); - sdw_stream = snd_soc_dai_get_sdw_stream(dai, substream->stream); + sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); if (IS_ERR(sdw_stream)) { dev_err(rtd->dev, "no stream found for DAI %s", dai->name); @@ -406,7 +406,7 @@ int sdw_hw_free(struct snd_pcm_substream *substream) /* Find stream from first CPU DAI */ dai = asoc_rtd_to_cpu(rtd, 0); - sdw_stream = snd_soc_dai_get_sdw_stream(dai, substream->stream); + sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); if (IS_ERR(sdw_stream)) { dev_err(rtd->dev, "no stream found for DAI %s", dai->name); diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c index 10d724bd1d67..5c1d13eccbee 100644 --- a/sound/soc/qcom/sdm845.c +++ b/sound/soc/qcom/sdm845.c @@ -57,8 +57,8 @@ static int sdm845_slim_snd_hw_params(struct snd_pcm_substream *substream, int ret = 0, i; for_each_rtd_codec_dais(rtd, i, codec_dai) { - sruntime = snd_soc_dai_get_sdw_stream(codec_dai, - substream->stream); + sruntime = snd_soc_dai_get_stream(codec_dai, + substream->stream); if (sruntime != ERR_PTR(-ENOTSUPP)) pdata->sruntime[cpu_dai->id] = sruntime; diff --git a/sound/soc/qcom/sm8250.c b/sound/soc/qcom/sm8250.c index b2ca2579810b..114a29e01c0f 100644 --- a/sound/soc/qcom/sm8250.c +++ b/sound/soc/qcom/sm8250.c @@ -136,8 +136,8 @@ static int sm8250_snd_hw_params(struct snd_pcm_substream *substream, case TX_CODEC_DMA_TX_2: case TX_CODEC_DMA_TX_3: for_each_rtd_codec_dais(rtd, i, codec_dai) { - sruntime = snd_soc_dai_get_sdw_stream(codec_dai, - substream->stream); + sruntime = snd_soc_dai_get_stream(codec_dai, + substream->stream); if (sruntime != ERR_PTR(-ENOTSUPP)) pdata->sruntime[cpu_dai->id] = sruntime; } -- cgit v1.2.3 From 636110411ca726f19ef8e87b0be51bb9a4cdef06 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 24 Dec 2021 10:10:32 +0800 Subject: ASoC: Intel/SOF: use set_stream() instead of set_tdm_slots() for HDAudio Overloading the tx_mask with a linear value is asking for trouble and only works because the codec_dai hw_params() is called before the cpu_dai hw_params(). Move to the more generic set_stream() API to pass the hdac_stream information. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Reviewed-by: Ranjani Sridharan Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20211224021034.26635-6-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hda.c | 22 +++++++++++----------- sound/soc/intel/skylake/skl-pcm.c | 7 ++----- sound/soc/sof/intel/hda-dai.c | 7 ++----- 3 files changed, 15 insertions(+), 21 deletions(-) diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c index 390dd6c7f6a5..de5955db0a5f 100644 --- a/sound/soc/codecs/hdac_hda.c +++ b/sound/soc/codecs/hdac_hda.c @@ -46,9 +46,8 @@ static int hdac_hda_dai_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai); static int hdac_hda_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai); -static int hdac_hda_dai_set_tdm_slot(struct snd_soc_dai *dai, - unsigned int tx_mask, unsigned int rx_mask, - int slots, int slot_width); +static int hdac_hda_dai_set_stream(struct snd_soc_dai *dai, void *stream, + int direction); static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt, struct snd_soc_dai *dai); @@ -58,7 +57,7 @@ static const struct snd_soc_dai_ops hdac_hda_dai_ops = { .prepare = hdac_hda_dai_prepare, .hw_params = hdac_hda_dai_hw_params, .hw_free = hdac_hda_dai_hw_free, - .set_tdm_slot = hdac_hda_dai_set_tdm_slot, + .set_stream = hdac_hda_dai_set_stream, }; static struct snd_soc_dai_driver hdac_hda_dais[] = { @@ -180,21 +179,22 @@ static struct snd_soc_dai_driver hdac_hda_dais[] = { }; -static int hdac_hda_dai_set_tdm_slot(struct snd_soc_dai *dai, - unsigned int tx_mask, unsigned int rx_mask, - int slots, int slot_width) +static int hdac_hda_dai_set_stream(struct snd_soc_dai *dai, + void *stream, int direction) { struct snd_soc_component *component = dai->component; struct hdac_hda_priv *hda_pvt; struct hdac_hda_pcm *pcm; + struct hdac_stream *hstream; + + if (!stream) + return -EINVAL; hda_pvt = snd_soc_component_get_drvdata(component); pcm = &hda_pvt->pcm[dai->id]; + hstream = (struct hdac_stream *)stream; - if (tx_mask) - pcm->stream_tag[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask; - else - pcm->stream_tag[SNDRV_PCM_STREAM_CAPTURE] = rx_mask; + pcm->stream_tag[direction] = hstream->stream_tag; return 0; } diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 9ecaf6a1e847..8378c187959f 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -562,11 +562,8 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream, stream_tag = hdac_stream(link_dev)->stream_tag; - /* set the stream tag in the codec dai dma params */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - snd_soc_dai_set_tdm_slot(codec_dai, stream_tag, 0, 0, 0); - else - snd_soc_dai_set_tdm_slot(codec_dai, 0, stream_tag, 0, 0); + /* set the hdac_stream in the codec dai */ + snd_soc_dai_set_stream(codec_dai, hdac_stream(link_dev), substream->stream); p_params.s_fmt = snd_pcm_format_width(params_format(params)); p_params.ch = params_channels(params); diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 6381f2b227f0..cd12589355ef 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -243,11 +243,8 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream, if (!link) return -EINVAL; - /* set the stream tag in the codec dai dma params */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - snd_soc_dai_set_tdm_slot(codec_dai, stream_tag, 0, 0, 0); - else - snd_soc_dai_set_tdm_slot(codec_dai, 0, stream_tag, 0, 0); + /* set the hdac_stream in the codec dai */ + snd_soc_dai_set_stream(codec_dai, hdac_stream(link_dev), substream->stream); p_params.s_fmt = snd_pcm_format_width(params_format(params)); p_params.ch = params_channels(params); -- cgit v1.2.3 From 9283b6f923f3bdd92bdeaf259c6b7a5e9dac6900 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 24 Dec 2021 10:10:33 +0800 Subject: soundwire: intel: remove unnecessary init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cppcheck warning: drivers/soundwire/intel.c:1487:10: style: Variable 'ret' is assigned a value that is never used. [unreadVariable] int ret = 0; ^ Signed-off-by: Pierre-Louis Bossart Reviewed-by: Péter Ujfalusi Reviewed-by: Rander Wang Signed-off-by: Bard Liao Acked-By: Vinod Koul Link: https://lore.kernel.org/r/20211224021034.26635-7-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- drivers/soundwire/intel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 70d164372a2b..0d1c86e6ac2e 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1613,7 +1613,7 @@ static int __maybe_unused intel_pm_prepare(struct device *dev) struct sdw_intel *sdw = cdns_to_intel(cdns); struct sdw_bus *bus = &cdns->bus; u32 clock_stop_quirks; - int ret = 0; + int ret; if (bus->prop.hw_disabled || !sdw->startup_done) { dev_dbg(dev, "SoundWire master %d is disabled or not-started, ignoring\n", -- cgit v1.2.3 From 63a6aa963dd01b66019b7834cc84d032e145bb00 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 24 Dec 2021 10:10:34 +0800 Subject: soundwire: intel: remove PDM support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While the hardware supports PDM streams, this capability has never been tested or enabled on any product, so this is dead-code. Let's remove all this. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Péter Ujfalusi Reviewed-by: Rander Wang Signed-off-by: Bard Liao Acked-By: Vinod Koul Link: https://lore.kernel.org/r/20211224021034.26635-8-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- drivers/soundwire/cadence_master.c | 36 +---------- drivers/soundwire/cadence_master.h | 12 +--- drivers/soundwire/intel.c | 123 ++++++++----------------------------- 3 files changed, 31 insertions(+), 140 deletions(-) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 4fcc3ba93004..558390af44b6 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -1178,9 +1178,6 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns, cdns->pcm.num_bd = config.pcm_bd; cdns->pcm.num_in = config.pcm_in; cdns->pcm.num_out = config.pcm_out; - cdns->pdm.num_bd = config.pdm_bd; - cdns->pdm.num_in = config.pdm_in; - cdns->pdm.num_out = config.pdm_out; /* Allocate PDIs for PCMs */ stream = &cdns->pcm; @@ -1211,32 +1208,6 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns, stream->num_pdi = stream->num_bd + stream->num_in + stream->num_out; cdns->num_ports = stream->num_pdi; - /* Allocate PDIs for PDMs */ - stream = &cdns->pdm; - ret = cdns_allocate_pdi(cdns, &stream->bd, - stream->num_bd, offset); - if (ret) - return ret; - - offset += stream->num_bd; - - ret = cdns_allocate_pdi(cdns, &stream->in, - stream->num_in, offset); - if (ret) - return ret; - - offset += stream->num_in; - - ret = cdns_allocate_pdi(cdns, &stream->out, - stream->num_out, offset); - - if (ret) - return ret; - - /* Update total number of PDM PDIs */ - stream->num_pdi = stream->num_bd + stream->num_in + stream->num_out; - cdns->num_ports += stream->num_pdi; - return 0; } EXPORT_SYMBOL(sdw_cdns_pdi_init); @@ -1681,7 +1652,7 @@ int sdw_cdns_probe(struct sdw_cdns *cdns) EXPORT_SYMBOL(sdw_cdns_probe); int cdns_set_sdw_stream(struct snd_soc_dai *dai, - void *stream, bool pcm, int direction) + void *stream, int direction) { struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); struct sdw_cdns_dma_data *dma; @@ -1705,10 +1676,7 @@ int cdns_set_sdw_stream(struct snd_soc_dai *dai, if (!dma) return -ENOMEM; - if (pcm) - dma->stream_type = SDW_STREAM_PCM; - else - dma->stream_type = SDW_STREAM_PDM; + dma->stream_type = SDW_STREAM_PCM; dma->bus = &cdns->bus; dma->link_id = cdns->instance; diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index aa4b9b0eb2a8..595d72c15d97 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -17,7 +17,7 @@ * @h_ch_num: high channel for PDI * @ch_count: total channel count for PDI * @dir: data direction - * @type: stream type, PDM or PCM + * @type: stream type, (only PCM supported) */ struct sdw_cdns_pdi { int num; @@ -62,17 +62,11 @@ struct sdw_cdns_streams { * @pcm_bd: number of bidirectional PCM streams supported * @pcm_in: number of input PCM streams supported * @pcm_out: number of output PCM streams supported - * @pdm_bd: number of bidirectional PDM streams supported - * @pdm_in: number of input PDM streams supported - * @pdm_out: number of output PDM streams supported */ struct sdw_cdns_stream_config { unsigned int pcm_bd; unsigned int pcm_in; unsigned int pcm_out; - unsigned int pdm_bd; - unsigned int pdm_in; - unsigned int pdm_out; }; /** @@ -111,7 +105,6 @@ struct sdw_cdns_dma_data { * @ports: Data ports * @num_ports: Total number of data ports * @pcm: PCM streams - * @pdm: PDM streams * @registers: Cadence registers * @link_up: Link status * @msg_count: Messages sent on bus @@ -129,7 +122,6 @@ struct sdw_cdns { int num_ports; struct sdw_cdns_streams pcm; - struct sdw_cdns_streams pdm; int pdi_loopback_source; int pdi_loopback_target; @@ -188,7 +180,7 @@ cdns_xfer_msg_defer(struct sdw_bus *bus, int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params); int cdns_set_sdw_stream(struct snd_soc_dai *dai, - void *stream, bool pcm, int direction); + void *stream, int direction); void sdw_cdns_check_self_clearing_bits(struct sdw_cdns *cdns, const char *string, bool initial_delay, int reset_iterations); diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 0d1c86e6ac2e..79ba0e3f6dac 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -564,7 +564,7 @@ static void intel_pdi_init(struct sdw_intel *sdw, { void __iomem *shim = sdw->link_res->shim; unsigned int link_id = sdw->instance; - int pcm_cap, pdm_cap; + int pcm_cap; /* PCM Stream Capability */ pcm_cap = intel_readw(shim, SDW_SHIM_PCMSCAP(link_id)); @@ -575,41 +575,25 @@ static void intel_pdi_init(struct sdw_intel *sdw, dev_dbg(sdw->cdns.dev, "PCM cap bd:%d in:%d out:%d\n", config->pcm_bd, config->pcm_in, config->pcm_out); - - /* PDM Stream Capability */ - pdm_cap = intel_readw(shim, SDW_SHIM_PDMSCAP(link_id)); - - config->pdm_bd = FIELD_GET(SDW_SHIM_PDMSCAP_BSS, pdm_cap); - config->pdm_in = FIELD_GET(SDW_SHIM_PDMSCAP_ISS, pdm_cap); - config->pdm_out = FIELD_GET(SDW_SHIM_PDMSCAP_OSS, pdm_cap); - - dev_dbg(sdw->cdns.dev, "PDM cap bd:%d in:%d out:%d\n", - config->pdm_bd, config->pdm_in, config->pdm_out); } static int -intel_pdi_get_ch_cap(struct sdw_intel *sdw, unsigned int pdi_num, bool pcm) +intel_pdi_get_ch_cap(struct sdw_intel *sdw, unsigned int pdi_num) { void __iomem *shim = sdw->link_res->shim; unsigned int link_id = sdw->instance; int count; - if (pcm) { - count = intel_readw(shim, SDW_SHIM_PCMSYCHC(link_id, pdi_num)); + count = intel_readw(shim, SDW_SHIM_PCMSYCHC(link_id, pdi_num)); - /* - * WORKAROUND: on all existing Intel controllers, pdi - * number 2 reports channel count as 1 even though it - * supports 8 channels. Performing hardcoding for pdi - * number 2. - */ - if (pdi_num == 2) - count = 7; - - } else { - count = intel_readw(shim, SDW_SHIM_PDMSCAP(link_id)); - count = FIELD_GET(SDW_SHIM_PDMSCAP_CPSS, count); - } + /* + * WORKAROUND: on all existing Intel controllers, pdi + * number 2 reports channel count as 1 even though it + * supports 8 channels. Performing hardcoding for pdi + * number 2. + */ + if (pdi_num == 2) + count = 7; /* zero based values for channel count in register */ count++; @@ -620,12 +604,12 @@ intel_pdi_get_ch_cap(struct sdw_intel *sdw, unsigned int pdi_num, bool pcm) static int intel_pdi_get_ch_update(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi, unsigned int num_pdi, - unsigned int *num_ch, bool pcm) + unsigned int *num_ch) { int i, ch_count = 0; for (i = 0; i < num_pdi; i++) { - pdi->ch_count = intel_pdi_get_ch_cap(sdw, pdi->num, pcm); + pdi->ch_count = intel_pdi_get_ch_cap(sdw, pdi->num); ch_count += pdi->ch_count; pdi++; } @@ -635,25 +619,23 @@ static int intel_pdi_get_ch_update(struct sdw_intel *sdw, } static int intel_pdi_stream_ch_update(struct sdw_intel *sdw, - struct sdw_cdns_streams *stream, bool pcm) + struct sdw_cdns_streams *stream) { intel_pdi_get_ch_update(sdw, stream->bd, stream->num_bd, - &stream->num_ch_bd, pcm); + &stream->num_ch_bd); intel_pdi_get_ch_update(sdw, stream->in, stream->num_in, - &stream->num_ch_in, pcm); + &stream->num_ch_in); intel_pdi_get_ch_update(sdw, stream->out, stream->num_out, - &stream->num_ch_out, pcm); + &stream->num_ch_out); return 0; } static int intel_pdi_ch_update(struct sdw_intel *sdw) { - /* First update PCM streams followed by PDM streams */ - intel_pdi_stream_ch_update(sdw, &sdw->cdns.pcm, true); - intel_pdi_stream_ch_update(sdw, &sdw->cdns.pdm, false); + intel_pdi_stream_ch_update(sdw, &sdw->cdns.pcm); return 0; } @@ -840,7 +822,6 @@ static int intel_hw_params(struct snd_pcm_substream *substream, struct sdw_port_config *pconfig; int ch, dir; int ret; - bool pcm = true; dma = snd_soc_dai_get_dma_data(dai, substream); if (!dma) @@ -852,13 +833,7 @@ static int intel_hw_params(struct snd_pcm_substream *substream, else dir = SDW_DATA_DIR_TX; - if (dma->stream_type == SDW_STREAM_PDM) - pcm = false; - - if (pcm) - pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pcm, ch, dir, dai->id); - else - pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pdm, ch, dir, dai->id); + pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pcm, ch, dir, dai->id); if (!pdi) { ret = -EINVAL; @@ -888,12 +863,7 @@ static int intel_hw_params(struct snd_pcm_substream *substream, sconfig.frame_rate = params_rate(params); sconfig.type = dma->stream_type; - if (dma->stream_type == SDW_STREAM_PDM) { - sconfig.frame_rate *= 50; - sconfig.bps = 1; - } else { - sconfig.bps = snd_pcm_format_width(params_format(params)); - } + sconfig.bps = snd_pcm_format_width(params_format(params)); /* Port configuration */ pconfig = kzalloc(sizeof(*pconfig), GFP_KERNEL); @@ -1012,13 +982,7 @@ static void intel_shutdown(struct snd_pcm_substream *substream, static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai, void *stream, int direction) { - return cdns_set_sdw_stream(dai, stream, true, direction); -} - -static int intel_pdm_set_sdw_stream(struct snd_soc_dai *dai, - void *stream, int direction) -{ - return cdns_set_sdw_stream(dai, stream, false, direction); + return cdns_set_sdw_stream(dai, stream, direction); } static void *intel_get_sdw_stream(struct snd_soc_dai *dai, @@ -1133,16 +1097,6 @@ static const struct snd_soc_dai_ops intel_pcm_dai_ops = { .get_stream = intel_get_sdw_stream, }; -static const struct snd_soc_dai_ops intel_pdm_dai_ops = { - .startup = intel_startup, - .hw_params = intel_hw_params, - .prepare = intel_prepare, - .hw_free = intel_hw_free, - .shutdown = intel_shutdown, - .set_stream = intel_pdm_set_sdw_stream, - .get_stream = intel_get_sdw_stream, -}; - static const struct snd_soc_component_driver dai_component = { .name = "soundwire", .suspend = intel_component_dais_suspend @@ -1151,7 +1105,7 @@ static const struct snd_soc_component_driver dai_component = { static int intel_create_dai(struct sdw_cdns *cdns, struct snd_soc_dai_driver *dais, enum intel_pdi_type type, - u32 num, u32 off, u32 max_ch, bool pcm) + u32 num, u32 off, u32 max_ch) { int i; @@ -1180,10 +1134,7 @@ static int intel_create_dai(struct sdw_cdns *cdns, dais[i].capture.formats = SNDRV_PCM_FMTBIT_S16_LE; } - if (pcm) - dais[i].ops = &intel_pcm_dai_ops; - else - dais[i].ops = &intel_pdm_dai_ops; + dais[i].ops = &intel_pcm_dai_ops; } return 0; @@ -1197,7 +1148,7 @@ static int intel_register_dai(struct sdw_intel *sdw) int num_dai, ret, off = 0; /* DAIs are created based on total number of PDIs supported */ - num_dai = cdns->pcm.num_pdi + cdns->pdm.num_pdi; + num_dai = cdns->pcm.num_pdi; dais = devm_kcalloc(cdns->dev, num_dai, sizeof(*dais), GFP_KERNEL); if (!dais) @@ -1207,39 +1158,19 @@ static int intel_register_dai(struct sdw_intel *sdw) stream = &cdns->pcm; ret = intel_create_dai(cdns, dais, INTEL_PDI_IN, cdns->pcm.num_in, - off, stream->num_ch_in, true); + off, stream->num_ch_in); if (ret) return ret; off += cdns->pcm.num_in; ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT, cdns->pcm.num_out, - off, stream->num_ch_out, true); + off, stream->num_ch_out); if (ret) return ret; off += cdns->pcm.num_out; ret = intel_create_dai(cdns, dais, INTEL_PDI_BD, cdns->pcm.num_bd, - off, stream->num_ch_bd, true); - if (ret) - return ret; - - /* Create PDM DAIs */ - stream = &cdns->pdm; - off += cdns->pcm.num_bd; - ret = intel_create_dai(cdns, dais, INTEL_PDI_IN, cdns->pdm.num_in, - off, stream->num_ch_in, false); - if (ret) - return ret; - - off += cdns->pdm.num_in; - ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT, cdns->pdm.num_out, - off, stream->num_ch_out, false); - if (ret) - return ret; - - off += cdns->pdm.num_out; - ret = intel_create_dai(cdns, dais, INTEL_PDI_BD, cdns->pdm.num_bd, - off, stream->num_ch_bd, false); + off, stream->num_ch_bd); if (ret) return ret; -- cgit v1.2.3 From 493433785df0075afc0c106ab65f10a605d0b35d Mon Sep 17 00:00:00 2001 From: Tzung-Bi Shih Date: Fri, 24 Dec 2021 14:47:16 +0800 Subject: ASoC: mediatek: mt8173: fix device_node leak Fixes the device_node leak. Signed-off-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20211224064719.2031210-2-tzungbi@google.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8173/mt8173-max98090.c | 3 +++ sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c | 2 ++ sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c | 2 ++ sound/soc/mediatek/mt8173/mt8173-rt5650.c | 2 ++ 4 files changed, 9 insertions(+) diff --git a/sound/soc/mediatek/mt8173/mt8173-max98090.c b/sound/soc/mediatek/mt8173/mt8173-max98090.c index 2408c9d3d9b3..65941dd2295d 100644 --- a/sound/soc/mediatek/mt8173/mt8173-max98090.c +++ b/sound/soc/mediatek/mt8173/mt8173-max98090.c @@ -180,6 +180,9 @@ static int mt8173_max98090_dev_probe(struct platform_device *pdev) if (ret) dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", __func__, ret); + + of_node_put(codec_node); + of_node_put(platform_node); return ret; } diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c index e6e824f3d24a..227b0b72c66e 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c @@ -218,6 +218,8 @@ static int mt8173_rt5650_rt5514_dev_probe(struct platform_device *pdev) if (ret) dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", __func__, ret); + + of_node_put(platform_node); return ret; } diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c index ba6fe3d90bfc..8df2ff2f2aab 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c @@ -285,6 +285,8 @@ static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev) if (ret) dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", __func__, ret); + + of_node_put(platform_node); return ret; } diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c index 9b933cce0b20..16ddf9f8bbcb 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c @@ -323,6 +323,8 @@ static int mt8173_rt5650_dev_probe(struct platform_device *pdev) if (ret) dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", __func__, ret); + + of_node_put(platform_node); return ret; } -- cgit v1.2.3 From cb006006fe6221f092fadaffd3f219288304c9ad Mon Sep 17 00:00:00 2001 From: Tzung-Bi Shih Date: Fri, 24 Dec 2021 14:47:17 +0800 Subject: ASoC: mediatek: mt8183: fix device_node leak Fixes the device_node leak. Signed-off-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20211224064719.2031210-3-tzungbi@google.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c | 6 +++++- sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c index f8a72a5102ad..ca893feab7eb 100644 --- a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c @@ -781,7 +781,11 @@ static int mt8183_da7219_max98357_dev_probe(struct platform_device *pdev) return ret; } - return devm_snd_soc_register_card(&pdev->dev, card); + ret = devm_snd_soc_register_card(&pdev->dev, card); + + of_node_put(platform_node); + of_node_put(hdmi_codec); + return ret; } #ifdef CONFIG_OF diff --git a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c index d5fc86132b49..19f8aead775d 100644 --- a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c @@ -780,7 +780,12 @@ mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev) __func__, ret); } - return devm_snd_soc_register_card(&pdev->dev, card); + ret = devm_snd_soc_register_card(&pdev->dev, card); + + of_node_put(platform_node); + of_node_put(ec_codec); + of_node_put(hdmi_codec); + return ret; } #ifdef CONFIG_OF -- cgit v1.2.3 From db3f5abe68ea5ea39d84e6af4f0f2ce5d5e2daf4 Mon Sep 17 00:00:00 2001 From: Tzung-Bi Shih Date: Fri, 24 Dec 2021 14:47:18 +0800 Subject: ASoC: mediatek: mt8173: reduce log verbosity in probe() Eliminates error messages if snd_soc_register_card() failed. Kernel emits messages if device probe error anyway. Signed-off-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20211224064719.2031210-4-tzungbi@google.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8173/mt8173-max98090.c | 3 --- sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c | 3 --- sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c | 3 --- sound/soc/mediatek/mt8173/mt8173-rt5650.c | 3 --- 4 files changed, 12 deletions(-) diff --git a/sound/soc/mediatek/mt8173/mt8173-max98090.c b/sound/soc/mediatek/mt8173/mt8173-max98090.c index 65941dd2295d..4cb90da89262 100644 --- a/sound/soc/mediatek/mt8173/mt8173-max98090.c +++ b/sound/soc/mediatek/mt8173/mt8173-max98090.c @@ -177,9 +177,6 @@ static int mt8173_max98090_dev_probe(struct platform_device *pdev) card->dev = &pdev->dev; ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) - dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", - __func__, ret); of_node_put(codec_node); of_node_put(platform_node); diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c index 227b0b72c66e..b55122b99f07 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c @@ -215,9 +215,6 @@ static int mt8173_rt5650_rt5514_dev_probe(struct platform_device *pdev) card->dev = &pdev->dev; ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) - dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", - __func__, ret); of_node_put(platform_node); return ret; diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c index 8df2ff2f2aab..5716d9299066 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c @@ -282,9 +282,6 @@ static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev) card->dev = &pdev->dev; ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) - dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", - __func__, ret); of_node_put(platform_node); return ret; diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c index 16ddf9f8bbcb..fc164f4f95f8 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c @@ -320,9 +320,6 @@ static int mt8173_rt5650_dev_probe(struct platform_device *pdev) card->dev = &pdev->dev; ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) - dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", - __func__, ret); of_node_put(platform_node); return ret; -- cgit v1.2.3 From 082482a5022780d42180a394fe6843e71fe963d8 Mon Sep 17 00:00:00 2001 From: Tzung-Bi Shih Date: Fri, 24 Dec 2021 14:47:19 +0800 Subject: ASoC: mediatek: mt8195: release device_node after snd_soc_register_card Device nodes can be released after components have bound. Shortens the lifecycle of the device nodes. Releases the reference counts after snd_soc_register_card. Signed-off-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20211224064719.2031210-5-tzungbi@google.com Signed-off-by: Mark Brown --- .../mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c | 53 ++++++------------ .../mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c | 64 +++++++--------------- 2 files changed, 36 insertions(+), 81 deletions(-) diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c index 9cf907c49ea8..ce8dace6527c 100644 --- a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c +++ b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c @@ -31,9 +31,6 @@ #define RT5682S_DEV0_NAME "rt5682s.2-001a" struct mt8195_mt6359_rt1011_rt5682_priv { - struct device_node *platform_node; - struct device_node *hdmi_node; - struct device_node *dp_node; struct snd_soc_jack headset_jack; struct snd_soc_jack dp_jack; struct snd_soc_jack hdmi_jack; @@ -1047,6 +1044,7 @@ static int mt8195_mt6359_rt1011_rt5682_dev_probe(struct platform_device *pdev) struct snd_soc_card *card = &mt8195_mt6359_rt1011_rt5682_soc_card; struct snd_soc_dai_link *dai_link; struct mt8195_mt6359_rt1011_rt5682_priv *priv; + struct device_node *platform_node, *dp_node, *hdmi_node; int is5682s = 0; int ret, i; @@ -1065,38 +1063,35 @@ static int mt8195_mt6359_rt1011_rt5682_dev_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - priv->platform_node = of_parse_phandle(pdev->dev.of_node, - "mediatek,platform", 0); - if (!priv->platform_node) { + platform_node = of_parse_phandle(pdev->dev.of_node, + "mediatek,platform", 0); + if (!platform_node) { dev_dbg(&pdev->dev, "Property 'platform' missing or invalid\n"); return -EINVAL; } + dp_node = of_parse_phandle(pdev->dev.of_node, "mediatek,dptx-codec", 0); + hdmi_node = of_parse_phandle(pdev->dev.of_node, + "mediatek,hdmi-codec", 0); + for_each_card_prelinks(card, i, dai_link) { if (!dai_link->platforms->name) - dai_link->platforms->of_node = priv->platform_node; + dai_link->platforms->of_node = platform_node; if (strcmp(dai_link->name, "DPTX_BE") == 0) { - priv->dp_node = - of_parse_phandle(pdev->dev.of_node, - "mediatek,dptx-codec", 0); - - if (!priv->dp_node) { + if (!dp_node) { dev_dbg(&pdev->dev, "No property 'dptx-codec'\n"); } else { - dai_link->codecs->of_node = priv->dp_node; + dai_link->codecs->of_node = dp_node; dai_link->codecs->name = NULL; dai_link->codecs->dai_name = "i2s-hifi"; dai_link->init = mt8195_dptx_codec_init; } } else if (strcmp(dai_link->name, "ETDM3_OUT_BE") == 0) { - priv->hdmi_node = - of_parse_phandle(pdev->dev.of_node, - "mediatek,hdmi-codec", 0); - if (!priv->hdmi_node) { + if (!hdmi_node) { dev_dbg(&pdev->dev, "No property 'hdmi-codec'\n"); } else { - dai_link->codecs->of_node = priv->hdmi_node; + dai_link->codecs->of_node = hdmi_node; dai_link->codecs->name = NULL; dai_link->codecs->dai_name = "i2s-hifi"; dai_link->init = mt8195_hdmi_codec_init; @@ -1113,28 +1108,13 @@ static int mt8195_mt6359_rt1011_rt5682_dev_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(card, priv); ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) { - of_node_put(priv->hdmi_node); - of_node_put(priv->dp_node); - of_node_put(priv->platform_node); - } + of_node_put(platform_node); + of_node_put(dp_node); + of_node_put(hdmi_node); return ret; } -static int mt8195_mt6359_rt1011_rt5682_dev_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - struct mt8195_mt6359_rt1011_rt5682_priv *priv = - snd_soc_card_get_drvdata(card); - - of_node_put(priv->hdmi_node); - of_node_put(priv->dp_node); - of_node_put(priv->platform_node); - - return 0; -} - #ifdef CONFIG_OF static const struct of_device_id mt8195_mt6359_rt1011_rt5682_dt_match[] = { {.compatible = "mediatek,mt8195_mt6359_rt1011_rt5682",}, @@ -1156,7 +1136,6 @@ static struct platform_driver mt8195_mt6359_rt1011_rt5682_driver = { .pm = &mt8195_mt6359_rt1011_rt5682_pm_ops, }, .probe = mt8195_mt6359_rt1011_rt5682_dev_probe, - .remove = mt8195_mt6359_rt1011_rt5682_dev_remove, }; module_platform_driver(mt8195_mt6359_rt1011_rt5682_driver); diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c index fdd444138728..c15c58170e9d 100644 --- a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c +++ b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c @@ -43,10 +43,6 @@ struct sof_conn_stream { }; struct mt8195_mt6359_rt1019_rt5682_priv { - struct device_node *platform_node; - struct device_node *adsp_node; - struct device_node *hdmi_node; - struct device_node *dp_node; struct snd_soc_jack headset_jack; struct snd_soc_jack dp_jack; struct snd_soc_jack hdmi_jack; @@ -1257,6 +1253,7 @@ static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev) struct snd_soc_card *card = &mt8195_mt6359_rt1019_rt5682_soc_card; struct snd_soc_dai_link *dai_link; struct mt8195_mt6359_rt1019_rt5682_priv *priv; + struct device_node *platform_node, *adsp_node, *dp_node, *hdmi_node; int is5682s = 0; int init6359 = 0; int sof_on = 0; @@ -1278,19 +1275,21 @@ static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - priv->platform_node = of_parse_phandle(pdev->dev.of_node, - "mediatek,platform", 0); - if (!priv->platform_node) { + platform_node = of_parse_phandle(pdev->dev.of_node, + "mediatek,platform", 0); + if (!platform_node) { dev_dbg(&pdev->dev, "Property 'platform' missing or invalid\n"); return -EINVAL; } - /* dai link */ - priv->adsp_node = of_parse_phandle(pdev->dev.of_node, - "mediatek,adsp", 0); - if (priv->adsp_node) + adsp_node = of_parse_phandle(pdev->dev.of_node, "mediatek,adsp", 0); + if (adsp_node) sof_on = 1; + dp_node = of_parse_phandle(pdev->dev.of_node, "mediatek,dptx-codec", 0); + hdmi_node = of_parse_phandle(pdev->dev.of_node, + "mediatek,hdmi-codec", 0); + if (of_property_read_bool(pdev->dev.of_node, "mediatek,dai-link")) { ret = mt8195_dailink_parse_of(card, pdev->dev.of_node, "mediatek,dai-link"); @@ -1306,31 +1305,25 @@ static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev) for_each_card_prelinks(card, i, dai_link) { if (!dai_link->platforms->name) { if (!strncmp(dai_link->name, "AFE_SOF", strlen("AFE_SOF")) && sof_on) - dai_link->platforms->of_node = priv->adsp_node; + dai_link->platforms->of_node = adsp_node; else - dai_link->platforms->of_node = priv->platform_node; + dai_link->platforms->of_node = platform_node; } if (strcmp(dai_link->name, "DPTX_BE") == 0) { - priv->dp_node = - of_parse_phandle(pdev->dev.of_node, - "mediatek,dptx-codec", 0); - if (!priv->dp_node) { + if (!dp_node) { dev_dbg(&pdev->dev, "No property 'dptx-codec'\n"); } else { - dai_link->codecs->of_node = priv->dp_node; + dai_link->codecs->of_node = dp_node; dai_link->codecs->name = NULL; dai_link->codecs->dai_name = "i2s-hifi"; dai_link->init = mt8195_dptx_codec_init; } } else if (strcmp(dai_link->name, "ETDM3_OUT_BE") == 0) { - priv->hdmi_node = - of_parse_phandle(pdev->dev.of_node, - "mediatek,hdmi-codec", 0); - if (!priv->hdmi_node) { + if (!hdmi_node) { dev_dbg(&pdev->dev, "No property 'hdmi-codec'\n"); } else { - dai_link->codecs->of_node = priv->hdmi_node; + dai_link->codecs->of_node = hdmi_node; dai_link->codecs->name = NULL; dai_link->codecs->dai_name = "i2s-hifi"; dai_link->init = mt8195_hdmi_codec_init; @@ -1357,30 +1350,14 @@ static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(card, priv); ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) { - of_node_put(priv->hdmi_node); - of_node_put(priv->dp_node); - of_node_put(priv->platform_node); - of_node_put(priv->adsp_node); - } + of_node_put(platform_node); + of_node_put(adsp_node); + of_node_put(dp_node); + of_node_put(hdmi_node); return ret; } -static int mt8195_mt6359_rt1019_rt5682_dev_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - struct mt8195_mt6359_rt1019_rt5682_priv *priv = - snd_soc_card_get_drvdata(card); - - of_node_put(priv->hdmi_node); - of_node_put(priv->dp_node); - of_node_put(priv->platform_node); - of_node_put(priv->adsp_node); - - return 0; -} - #ifdef CONFIG_OF static const struct of_device_id mt8195_mt6359_rt1019_rt5682_dt_match[] = { {.compatible = "mediatek,mt8195_mt6359_rt1019_rt5682",}, @@ -1402,7 +1379,6 @@ static struct platform_driver mt8195_mt6359_rt1019_rt5682_driver = { .pm = &mt8195_mt6359_rt1019_rt5682_pm_ops, }, .probe = mt8195_mt6359_rt1019_rt5682_dev_probe, - .remove = mt8195_mt6359_rt1019_rt5682_dev_remove, }; module_platform_driver(mt8195_mt6359_rt1019_rt5682_driver); -- cgit v1.2.3 From 9de2b9286a6dd16966959b3cb34fc2ddfd39213e Mon Sep 17 00:00:00 2001 From: Jiasheng Jiang Date: Wed, 22 Dec 2021 09:51:57 +0800 Subject: ASoC: mediatek: Check for error clk pointer Yes, you are right and now the return code depending on the init_clks(). Fixes: 6078c651947a ("soc: mediatek: Refine scpsys to support multiple platform") Signed-off-by: Jiasheng Jiang Link: https://lore.kernel.org/r/20211222015157.1025853-1-jiasheng@iscas.ac.cn Signed-off-by: Mark Brown --- drivers/soc/mediatek/mtk-scpsys.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/soc/mediatek/mtk-scpsys.c b/drivers/soc/mediatek/mtk-scpsys.c index ca75b14931ec..670cc82d17dc 100644 --- a/drivers/soc/mediatek/mtk-scpsys.c +++ b/drivers/soc/mediatek/mtk-scpsys.c @@ -411,12 +411,17 @@ out: return ret; } -static void init_clks(struct platform_device *pdev, struct clk **clk) +static int init_clks(struct platform_device *pdev, struct clk **clk) { int i; - for (i = CLK_NONE + 1; i < CLK_MAX; i++) + for (i = CLK_NONE + 1; i < CLK_MAX; i++) { clk[i] = devm_clk_get(&pdev->dev, clk_names[i]); + if (IS_ERR(clk[i])) + return PTR_ERR(clk[i]); + } + + return 0; } static struct scp *init_scp(struct platform_device *pdev, @@ -426,7 +431,7 @@ static struct scp *init_scp(struct platform_device *pdev, { struct genpd_onecell_data *pd_data; struct resource *res; - int i, j; + int i, j, ret; struct scp *scp; struct clk *clk[CLK_MAX]; @@ -481,7 +486,9 @@ static struct scp *init_scp(struct platform_device *pdev, pd_data->num_domains = num; - init_clks(pdev, clk); + ret = init_clks(pdev, clk); + if (ret) + return ERR_PTR(ret); for (i = 0; i < num; i++) { struct scp_domain *scpd = &scp->domains[i]; -- cgit v1.2.3 From b2fde4deff854ca7d49ec735a8252d944418b64d Mon Sep 17 00:00:00 2001 From: chiminghao Date: Thu, 9 Dec 2021 01:57:07 +0000 Subject: ASoC: remove unneeded variable return value form directly instead of taking this in another redundant variable. Reported-by: Zeal Robot Signed-off-by: chiminghao Link: https://lore.kernel.org/r/20211209015707.409870-1-chi.minghao@zte.com.cn Signed-off-by: Mark Brown --- sound/soc/codecs/mt6660.c | 5 ++--- sound/soc/codecs/wcd938x.c | 6 ++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/mt6660.c b/sound/soc/codecs/mt6660.c index 358c500377df..3a881523c30f 100644 --- a/sound/soc/codecs/mt6660.c +++ b/sound/soc/codecs/mt6660.c @@ -47,13 +47,12 @@ static int mt6660_reg_write(void *context, unsigned int reg, unsigned int val) struct mt6660_chip *chip = context; int size = mt6660_get_reg_size(reg); u8 reg_data[4]; - int i, ret; + int i; for (i = 0; i < size; i++) reg_data[size - i - 1] = (val >> (8 * i)) & 0xff; - ret = i2c_smbus_write_i2c_block_data(chip->i2c, reg, size, reg_data); - return ret; + return i2c_smbus_write_i2c_block_data(chip->i2c, reg, size, reg_data); } static int mt6660_reg_read(void *context, unsigned int reg, unsigned int *val) diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c index 67151c7770c6..54152b57eeeb 100644 --- a/sound/soc/codecs/wcd938x.c +++ b/sound/soc/codecs/wcd938x.c @@ -3086,7 +3086,7 @@ static int wcd938x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *compon int micb_num, bool req_en) { struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); - int rc, micb_mv; + int micb_mv; if (micb_num != MIC_BIAS_2) return -EINVAL; @@ -3100,9 +3100,7 @@ static int wcd938x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *compon micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : wcd938x->micb2_mv; - rc = wcd938x_mbhc_micb_adjust_voltage(component, micb_mv, MIC_BIAS_2); - - return rc; + return wcd938x_mbhc_micb_adjust_voltage(component, micb_mv, MIC_BIAS_2); } static inline void wcd938x_mbhc_get_result_params(struct wcd938x_priv *wcd938x, -- cgit v1.2.3 From 5c5f08f7fc0bee9a1bc3fbdcb7a21cfd0648ab14 Mon Sep 17 00:00:00 2001 From: V sujith kumar Reddy Date: Fri, 24 Dec 2021 20:30:43 +0530 Subject: ASoC: amd: acp: Power on/off the speaker enable gpio pin based on DAPM callback. Configure the speaker gpio pin based on power sequence of the DAPM speaker events. Enable speaker after widget power up and Disable before widget powerdown. Signed-off-by: V sujith kumar Reddy Link: https://lore.kernel.org/r/20211224150058.2444776-1-vsujithkumar.reddy@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/Kconfig | 6 +++--- sound/soc/amd/acp/acp-legacy-mach.c | 19 ++++++++++++++++--- sound/soc/amd/acp/acp-mach-common.c | 25 +++++++++++++++++++++++++ sound/soc/amd/acp/acp-mach.h | 10 +++++++++- sound/soc/amd/acp/acp-sof-mach.c | 21 ++++++++++++++++++--- 5 files changed, 71 insertions(+), 10 deletions(-) diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig index 154be5e70821..d5838df3064b 100644 --- a/sound/soc/amd/acp/Kconfig +++ b/sound/soc/amd/acp/Kconfig @@ -32,7 +32,7 @@ config SND_AMD_ASOC_RENOIR config SND_SOC_AMD_MACH_COMMON tristate - depends on X86 && PCI && I2C + depends on X86 && PCI && I2C && GPIOLIB select CLK_FIXED_FCH select SND_SOC_RT5682_I2C select SND_SOC_DMIC @@ -44,14 +44,14 @@ config SND_SOC_AMD_MACH_COMMON config SND_SOC_AMD_LEGACY_MACH tristate "AMD Legacy Machine Driver Support" - depends on X86 && PCI && I2C + depends on X86 && PCI && I2C && GPIOLIB select SND_SOC_AMD_MACH_COMMON help This option enables legacy sound card support for ACP audio. config SND_SOC_AMD_SOF_MACH tristate "AMD SOF Machine Driver Support" - depends on X86 && PCI && I2C + depends on X86 && PCI && I2C && GPIOLIB select SND_SOC_AMD_MACH_COMMON help This option enables SOF sound card support for ACP audio. diff --git a/sound/soc/amd/acp/acp-legacy-mach.c b/sound/soc/amd/acp/acp-legacy-mach.c index de0f8024e2fb..0ad1cf41b308 100644 --- a/sound/soc/amd/acp/acp-legacy-mach.c +++ b/sound/soc/amd/acp/acp-legacy-mach.c @@ -27,6 +27,7 @@ static struct acp_card_drvdata rt5682_rt1019_data = { .hs_codec_id = RT5682, .amp_codec_id = RT1019, .dmic_codec_id = NONE, + .gpio_spkr_en = EN_SPKR_GPIO_GB, }; static const struct snd_kcontrol_new acp_controls[] = { @@ -41,15 +42,16 @@ static const struct snd_kcontrol_new acp_controls[] = { static const struct snd_soc_dapm_widget acp_widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), - SND_SOC_DAPM_SPK("Spk", NULL), - SND_SOC_DAPM_SPK("Left Spk", NULL), - SND_SOC_DAPM_SPK("Right Spk", NULL), + SND_SOC_DAPM_SPK("Spk", event_spkr_handler), + SND_SOC_DAPM_SPK("Left Spk", event_spkr_handler), + SND_SOC_DAPM_SPK("Right Spk", event_spkr_handler), }; static int acp_asoc_probe(struct platform_device *pdev) { struct snd_soc_card *card = NULL; struct device *dev = &pdev->dev; + unsigned int spkr_gpio; int ret; if (!pdev->id_entry) @@ -67,9 +69,20 @@ static int acp_asoc_probe(struct platform_device *pdev) card->controls = acp_controls; card->num_controls = ARRAY_SIZE(acp_controls); card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data; + spkr_gpio = ((struct acp_card_drvdata *)(card->drvdata))->gpio_spkr_en; acp_legacy_dai_links_create(card); + if (gpio_is_valid(spkr_gpio)) { + ret = devm_gpio_request(dev, spkr_gpio, "spkren"); + if (ret) { + dev_err(dev, "(%s) gpio request failed: %d\n", + __func__, ret); + return ret; + } + gpio_direction_output(spkr_gpio, 0); + } + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { dev_err(&pdev->dev, diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c index 7785f12aa006..03d8d1af14b3 100644 --- a/sound/soc/amd/acp/acp-mach-common.c +++ b/sound/soc/amd/acp/acp-mach-common.c @@ -71,6 +71,31 @@ static const struct snd_soc_dapm_route rt5682_map[] = { { "IN1P", NULL, "Headset Mic" }, }; +int event_spkr_handler(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct acp_card_drvdata *drvdata = snd_soc_card_get_drvdata(card); + + if (!gpio_is_valid(drvdata->gpio_spkr_en)) + return 0; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + gpio_set_value(drvdata->gpio_spkr_en, 1); + break; + case SND_SOC_DAPM_PRE_PMD: + gpio_set_value(drvdata->gpio_spkr_en, 0); + break; + default: + dev_warn(card->dev, "%s invalid setting\n", __func__); + break; + } + return 0; +} +EXPORT_SYMBOL_NS_GPL(event_spkr_handler, SND_SOC_AMD_MACH); + /* Define card ops for RT5682 CODEC */ static int acp_card_rt5682_init(struct snd_soc_pcm_runtime *rtd) { diff --git a/sound/soc/amd/acp/acp-mach.h b/sound/soc/amd/acp/acp-mach.h index 5dc47cfbff10..fd6299844ebe 100644 --- a/sound/soc/amd/acp/acp-mach.h +++ b/sound/soc/amd/acp/acp-mach.h @@ -17,6 +17,12 @@ #include #include #include +#include +#include + +#define EN_SPKR_GPIO_GB 0x11F +#define EN_SPKR_GPIO_NK 0x146 +#define EN_SPKR_GPIO_NONE -EINVAL enum be_id { HEADSET_BE_ID = 0, @@ -49,9 +55,11 @@ struct acp_card_drvdata { unsigned int dai_fmt; struct clk *wclk; struct clk *bclk; + unsigned int gpio_spkr_en; }; int acp_sofdsp_dai_links_create(struct snd_soc_card *card); int acp_legacy_dai_links_create(struct snd_soc_card *card); - +int event_spkr_handler(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event); #endif diff --git a/sound/soc/amd/acp/acp-sof-mach.c b/sound/soc/amd/acp/acp-sof-mach.c index 854eb7214cea..07de46142655 100644 --- a/sound/soc/amd/acp/acp-sof-mach.c +++ b/sound/soc/amd/acp/acp-sof-mach.c @@ -27,6 +27,7 @@ static struct acp_card_drvdata sof_rt5682_rt1019_data = { .hs_codec_id = RT5682, .amp_codec_id = RT1019, .dmic_codec_id = DMIC, + .gpio_spkr_en = EN_SPKR_GPIO_GB, }; static struct acp_card_drvdata sof_rt5682_max_data = { @@ -36,6 +37,7 @@ static struct acp_card_drvdata sof_rt5682_max_data = { .hs_codec_id = RT5682, .amp_codec_id = MAX98360A, .dmic_codec_id = DMIC, + .gpio_spkr_en = EN_SPKR_GPIO_NK, }; static struct acp_card_drvdata sof_rt5682s_max_data = { @@ -45,6 +47,7 @@ static struct acp_card_drvdata sof_rt5682s_max_data = { .hs_codec_id = RT5682S, .amp_codec_id = MAX98360A, .dmic_codec_id = DMIC, + .gpio_spkr_en = EN_SPKR_GPIO_NK, }; static const struct snd_kcontrol_new acp_controls[] = { @@ -58,15 +61,16 @@ static const struct snd_kcontrol_new acp_controls[] = { static const struct snd_soc_dapm_widget acp_widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), - SND_SOC_DAPM_SPK("Spk", NULL), - SND_SOC_DAPM_SPK("Left Spk", NULL), - SND_SOC_DAPM_SPK("Right Spk", NULL), + SND_SOC_DAPM_SPK("Spk", event_spkr_handler), + SND_SOC_DAPM_SPK("Left Spk", event_spkr_handler), + SND_SOC_DAPM_SPK("Right Spk", event_spkr_handler), }; static int acp_sof_probe(struct platform_device *pdev) { struct snd_soc_card *card = NULL; struct device *dev = &pdev->dev; + unsigned int spkr_gpio; int ret; if (!pdev->id_entry) @@ -84,9 +88,20 @@ static int acp_sof_probe(struct platform_device *pdev) card->controls = acp_controls; card->num_controls = ARRAY_SIZE(acp_controls); card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data; + spkr_gpio = ((struct acp_card_drvdata *)(card->drvdata))->gpio_spkr_en; acp_sofdsp_dai_links_create(card); + if (gpio_is_valid(spkr_gpio)) { + ret = devm_gpio_request(dev, spkr_gpio, "spkren"); + if (ret) { + dev_err(dev, "(%s) gpio request failed: %d\n", + __func__, ret); + return ret; + } + gpio_direction_output(spkr_gpio, 0); + } + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { dev_err(&pdev->dev, -- cgit v1.2.3 From 12054f0ce8be7d2003ec068ab27c9eb608397b98 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 16 Dec 2021 17:11:27 -0600 Subject: ALSA/ASoC: hda: move/rename snd_hdac_ext_stop_streams to hdac_stream.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit snd_hdac_ext_stop_streams() has really nothing to do with the extension, it just loops over the bus streams. Move it to the hdac_stream layer and rename to remove the 'ext' prefix and add the precision that the chip will also be stopped. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Kai Vehmanen Reviewed-by: Péter Ujfalusi Reviewed-by: Ranjani Sridharan Reviewed-by: Cezary Rojewski Link: https://lore.kernel.org/r/20211216231128.344321-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Takashi Iwai --- include/sound/hdaudio.h | 1 + include/sound/hdaudio_ext.h | 1 - sound/hda/ext/hdac_ext_stream.c | 17 ----------------- sound/hda/hdac_stream.c | 16 ++++++++++++++++ sound/soc/intel/skylake/skl.c | 4 ++-- 5 files changed, 19 insertions(+), 20 deletions(-) diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 22af68b01426..6a90ce405e60 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -558,6 +558,7 @@ int snd_hdac_stream_set_params(struct hdac_stream *azx_dev, void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start); void snd_hdac_stream_clear(struct hdac_stream *azx_dev); void snd_hdac_stream_stop(struct hdac_stream *azx_dev); +void snd_hdac_stop_streams_and_chip(struct hdac_bus *bus); void snd_hdac_stream_reset(struct hdac_stream *azx_dev); void snd_hdac_stream_sync_trigger(struct hdac_stream *azx_dev, bool set, unsigned int streams, unsigned int reg); diff --git a/include/sound/hdaudio_ext.h b/include/sound/hdaudio_ext.h index d4e31ea16aba..56ea5cde5e63 100644 --- a/include/sound/hdaudio_ext.h +++ b/include/sound/hdaudio_ext.h @@ -92,7 +92,6 @@ void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus, struct hdac_ext_stream *azx_dev, bool decouple); void snd_hdac_ext_stream_decouple(struct hdac_bus *bus, struct hdac_ext_stream *azx_dev, bool decouple); -void snd_hdac_ext_stop_streams(struct hdac_bus *bus); int snd_hdac_ext_stream_set_spib(struct hdac_bus *bus, struct hdac_ext_stream *stream, u32 value); diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c index 37154ed43bd5..c09652da43ff 100644 --- a/sound/hda/ext/hdac_ext_stream.c +++ b/sound/hda/ext/hdac_ext_stream.c @@ -475,23 +475,6 @@ int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_bus *bus, } EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_get_spbmaxfifo); - -/** - * snd_hdac_ext_stop_streams - stop all stream if running - * @bus: HD-audio core bus - */ -void snd_hdac_ext_stop_streams(struct hdac_bus *bus) -{ - struct hdac_stream *stream; - - if (bus->chip_init) { - list_for_each_entry(stream, &bus->stream_list, list) - snd_hdac_stream_stop(stream); - snd_hdac_bus_stop_chip(bus); - } -} -EXPORT_SYMBOL_GPL(snd_hdac_ext_stop_streams); - /** * snd_hdac_ext_stream_drsm_enable - enable DMA resume for a stream * @bus: HD-audio core bus diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index aa7955fdf68a..f3582012d22f 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -142,6 +142,22 @@ void snd_hdac_stream_stop(struct hdac_stream *azx_dev) } EXPORT_SYMBOL_GPL(snd_hdac_stream_stop); +/** + * snd_hdac_stop_streams_and_chip - stop all streams and chip if running + * @bus: HD-audio core bus + */ +void snd_hdac_stop_streams_and_chip(struct hdac_bus *bus) +{ + struct hdac_stream *stream; + + if (bus->chip_init) { + list_for_each_entry(stream, &bus->stream_list, list) + snd_hdac_stream_stop(stream); + snd_hdac_bus_stop_chip(bus); + } +} +EXPORT_SYMBOL_GPL(snd_hdac_stop_streams_and_chip); + /** * snd_hdac_stream_reset - reset a stream * @azx_dev: HD-audio core stream to reset diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 5b1a15e39912..148ddf4cace0 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -439,7 +439,7 @@ static int skl_free(struct hdac_bus *bus) skl->init_done = 0; /* to be sure */ - snd_hdac_ext_stop_streams(bus); + snd_hdac_stop_streams_and_chip(bus); if (bus->irq >= 0) free_irq(bus->irq, (void *)bus); @@ -1096,7 +1096,7 @@ static void skl_shutdown(struct pci_dev *pci) if (!skl->init_done) return; - snd_hdac_ext_stop_streams(bus); + snd_hdac_stop_streams_and_chip(bus); list_for_each_entry(s, &bus->stream_list, list) { stream = stream_to_hdac_ext_stream(s); snd_hdac_ext_stream_decouple(bus, stream, false); -- cgit v1.2.3 From 0f7e5ee62f4c24ca9db58351c86653cc3ee0bd0e Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 16 Dec 2021 17:11:28 -0600 Subject: ALSA: HDA: hdac_ext_stream: use consistent prefixes for variables The existing code maximizes confusion by using 'stream' and 'hstream' variables of different types. Examples: struct hdac_stream *stream; struct hdac_ext_stream *stream; struct hdac_stream *hstream; struct hdac_ext_stream *hstream; with some additional copy/paste remains: struct hdac_ext_stream *azx_dev; This patch suggests a consistent naming across all 'hdac_ext_stream' functions. The convention is: struct hdac_stream *hstream; struct hdac_ext_stream *hext_stream; No functionality change - just renaming of variables and more consistent indentation. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Kai Vehmanen Reviewed-by: Rander Wang Link: https://lore.kernel.org/r/20211216231128.344321-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Takashi Iwai --- include/sound/hdaudio_ext.h | 26 +++--- sound/hda/ext/hdac_ext_stream.c | 199 ++++++++++++++++++++-------------------- 2 files changed, 113 insertions(+), 112 deletions(-) diff --git a/include/sound/hdaudio_ext.h b/include/sound/hdaudio_ext.h index 56ea5cde5e63..77123c3e4095 100644 --- a/include/sound/hdaudio_ext.h +++ b/include/sound/hdaudio_ext.h @@ -78,35 +78,35 @@ struct hdac_ext_stream { container_of(s, struct hdac_ext_stream, hstream) void snd_hdac_ext_stream_init(struct hdac_bus *bus, - struct hdac_ext_stream *stream, int idx, - int direction, int tag); + struct hdac_ext_stream *hext_stream, int idx, + int direction, int tag); int snd_hdac_ext_stream_init_all(struct hdac_bus *bus, int start_idx, - int num_stream, int dir); + int num_stream, int dir); void snd_hdac_stream_free_all(struct hdac_bus *bus); void snd_hdac_link_free_all(struct hdac_bus *bus); struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream, int type); -void snd_hdac_ext_stream_release(struct hdac_ext_stream *azx_dev, int type); +void snd_hdac_ext_stream_release(struct hdac_ext_stream *hext_stream, int type); void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus, - struct hdac_ext_stream *azx_dev, bool decouple); + struct hdac_ext_stream *hext_stream, bool decouple); void snd_hdac_ext_stream_decouple(struct hdac_bus *bus, struct hdac_ext_stream *azx_dev, bool decouple); int snd_hdac_ext_stream_set_spib(struct hdac_bus *bus, - struct hdac_ext_stream *stream, u32 value); + struct hdac_ext_stream *hext_stream, u32 value); int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_bus *bus, - struct hdac_ext_stream *stream); + struct hdac_ext_stream *hext_stream); void snd_hdac_ext_stream_drsm_enable(struct hdac_bus *bus, bool enable, int index); int snd_hdac_ext_stream_set_dpibr(struct hdac_bus *bus, - struct hdac_ext_stream *stream, u32 value); -int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *stream, u32 value); + struct hdac_ext_stream *hext_stream, u32 value); +int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *hext_stream, u32 value); -void snd_hdac_ext_link_stream_start(struct hdac_ext_stream *hstream); -void snd_hdac_ext_link_stream_clear(struct hdac_ext_stream *hstream); -void snd_hdac_ext_link_stream_reset(struct hdac_ext_stream *hstream); -int snd_hdac_ext_link_stream_setup(struct hdac_ext_stream *stream, int fmt); +void snd_hdac_ext_link_stream_start(struct hdac_ext_stream *hext_stream); +void snd_hdac_ext_link_stream_clear(struct hdac_ext_stream *hext_stream); +void snd_hdac_ext_link_stream_reset(struct hdac_ext_stream *hext_stream); +int snd_hdac_ext_link_stream_setup(struct hdac_ext_stream *hext_stream, int fmt); struct hdac_ext_link { struct hdac_bus *bus; diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c index c09652da43ff..d2b5724b463f 100644 --- a/sound/hda/ext/hdac_ext_stream.c +++ b/sound/hda/ext/hdac_ext_stream.c @@ -18,7 +18,7 @@ /** * snd_hdac_ext_stream_init - initialize each stream (aka device) * @bus: HD-audio core bus - * @stream: HD-audio ext core stream object to initialize + * @hext_stream: HD-audio ext core stream object to initialize * @idx: stream index number * @direction: stream direction (SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE) * @tag: the tag id to assign @@ -27,34 +27,34 @@ * invoke hdac stream initialization routine */ void snd_hdac_ext_stream_init(struct hdac_bus *bus, - struct hdac_ext_stream *stream, - int idx, int direction, int tag) + struct hdac_ext_stream *hext_stream, + int idx, int direction, int tag) { if (bus->ppcap) { - stream->pphc_addr = bus->ppcap + AZX_PPHC_BASE + + hext_stream->pphc_addr = bus->ppcap + AZX_PPHC_BASE + AZX_PPHC_INTERVAL * idx; - stream->pplc_addr = bus->ppcap + AZX_PPLC_BASE + + hext_stream->pplc_addr = bus->ppcap + AZX_PPLC_BASE + AZX_PPLC_MULTI * bus->num_streams + AZX_PPLC_INTERVAL * idx; } if (bus->spbcap) { - stream->spib_addr = bus->spbcap + AZX_SPB_BASE + + hext_stream->spib_addr = bus->spbcap + AZX_SPB_BASE + AZX_SPB_INTERVAL * idx + AZX_SPB_SPIB; - stream->fifo_addr = bus->spbcap + AZX_SPB_BASE + + hext_stream->fifo_addr = bus->spbcap + AZX_SPB_BASE + AZX_SPB_INTERVAL * idx + AZX_SPB_MAXFIFO; } if (bus->drsmcap) - stream->dpibr_addr = bus->drsmcap + AZX_DRSM_BASE + + hext_stream->dpibr_addr = bus->drsmcap + AZX_DRSM_BASE + AZX_DRSM_INTERVAL * idx; - stream->decoupled = false; - snd_hdac_stream_init(bus, &stream->hstream, idx, direction, tag); + hext_stream->decoupled = false; + snd_hdac_stream_init(bus, &hext_stream->hstream, idx, direction, tag); } EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init); @@ -67,18 +67,18 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init); * @dir: direction of streams */ int snd_hdac_ext_stream_init_all(struct hdac_bus *bus, int start_idx, - int num_stream, int dir) + int num_stream, int dir) { int stream_tag = 0; int i, tag, idx = start_idx; for (i = 0; i < num_stream; i++) { - struct hdac_ext_stream *stream = - kzalloc(sizeof(*stream), GFP_KERNEL); - if (!stream) + struct hdac_ext_stream *hext_stream = + kzalloc(sizeof(*hext_stream), GFP_KERNEL); + if (!hext_stream) return -ENOMEM; tag = ++stream_tag; - snd_hdac_ext_stream_init(bus, stream, idx, dir, tag); + snd_hdac_ext_stream_init(bus, hext_stream, idx, dir, tag); idx++; } @@ -95,22 +95,22 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init_all); void snd_hdac_stream_free_all(struct hdac_bus *bus) { struct hdac_stream *s, *_s; - struct hdac_ext_stream *stream; + struct hdac_ext_stream *hext_stream; list_for_each_entry_safe(s, _s, &bus->stream_list, list) { - stream = stream_to_hdac_ext_stream(s); - snd_hdac_ext_stream_decouple(bus, stream, false); + hext_stream = stream_to_hdac_ext_stream(s); + snd_hdac_ext_stream_decouple(bus, hext_stream, false); list_del(&s->list); - kfree(stream); + kfree(hext_stream); } } EXPORT_SYMBOL_GPL(snd_hdac_stream_free_all); void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus, - struct hdac_ext_stream *stream, + struct hdac_ext_stream *hext_stream, bool decouple) { - struct hdac_stream *hstream = &stream->hstream; + struct hdac_stream *hstream = &hext_stream->hstream; u32 val; int mask = AZX_PPCTL_PROCEN(hstream->index); @@ -121,76 +121,76 @@ void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus, else if (!decouple && val) snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, 0); - stream->decoupled = decouple; + hext_stream->decoupled = decouple; } EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple_locked); /** * snd_hdac_ext_stream_decouple - decouple the hdac stream * @bus: HD-audio core bus - * @stream: HD-audio ext core stream object to initialize + * @hext_stream: HD-audio ext core stream object to initialize * @decouple: flag to decouple */ void snd_hdac_ext_stream_decouple(struct hdac_bus *bus, - struct hdac_ext_stream *stream, bool decouple) + struct hdac_ext_stream *hext_stream, bool decouple) { spin_lock_irq(&bus->reg_lock); - snd_hdac_ext_stream_decouple_locked(bus, stream, decouple); + snd_hdac_ext_stream_decouple_locked(bus, hext_stream, decouple); spin_unlock_irq(&bus->reg_lock); } EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple); /** * snd_hdac_ext_link_stream_start - start a stream - * @stream: HD-audio ext core stream to start + * @hext_stream: HD-audio ext core stream to start */ -void snd_hdac_ext_link_stream_start(struct hdac_ext_stream *stream) +void snd_hdac_ext_link_stream_start(struct hdac_ext_stream *hext_stream) { - snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, + snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL, AZX_PPLCCTL_RUN, AZX_PPLCCTL_RUN); } EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_start); /** * snd_hdac_ext_link_stream_clear - stop a stream DMA - * @stream: HD-audio ext core stream to stop + * @hext_stream: HD-audio ext core stream to stop */ -void snd_hdac_ext_link_stream_clear(struct hdac_ext_stream *stream) +void snd_hdac_ext_link_stream_clear(struct hdac_ext_stream *hext_stream) { - snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, AZX_PPLCCTL_RUN, 0); + snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL, AZX_PPLCCTL_RUN, 0); } EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_clear); /** * snd_hdac_ext_link_stream_reset - reset a stream - * @stream: HD-audio ext core stream to reset + * @hext_stream: HD-audio ext core stream to reset */ -void snd_hdac_ext_link_stream_reset(struct hdac_ext_stream *stream) +void snd_hdac_ext_link_stream_reset(struct hdac_ext_stream *hext_stream) { unsigned char val; int timeout; - snd_hdac_ext_link_stream_clear(stream); + snd_hdac_ext_link_stream_clear(hext_stream); - snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, + snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL, AZX_PPLCCTL_STRST, AZX_PPLCCTL_STRST); udelay(3); timeout = 50; do { - val = readl(stream->pplc_addr + AZX_REG_PPLCCTL) & + val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL) & AZX_PPLCCTL_STRST; if (val) break; udelay(3); } while (--timeout); val &= ~AZX_PPLCCTL_STRST; - writel(val, stream->pplc_addr + AZX_REG_PPLCCTL); + writel(val, hext_stream->pplc_addr + AZX_REG_PPLCCTL); udelay(3); timeout = 50; /* waiting for hardware to report that the stream is out of reset */ do { - val = readl(stream->pplc_addr + AZX_REG_PPLCCTL) & AZX_PPLCCTL_STRST; + val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL) & AZX_PPLCCTL_STRST; if (!val) break; udelay(3); @@ -201,24 +201,24 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_reset); /** * snd_hdac_ext_link_stream_setup - set up the SD for streaming - * @stream: HD-audio ext core stream to set up + * @hext_stream: HD-audio ext core stream to set up * @fmt: stream format */ -int snd_hdac_ext_link_stream_setup(struct hdac_ext_stream *stream, int fmt) +int snd_hdac_ext_link_stream_setup(struct hdac_ext_stream *hext_stream, int fmt) { - struct hdac_stream *hstream = &stream->hstream; + struct hdac_stream *hstream = &hext_stream->hstream; unsigned int val; /* make sure the run bit is zero for SD */ - snd_hdac_ext_link_stream_clear(stream); + snd_hdac_ext_link_stream_clear(hext_stream); /* program the stream_tag */ - val = readl(stream->pplc_addr + AZX_REG_PPLCCTL); + val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL); val = (val & ~AZX_PPLCCTL_STRM_MASK) | (hstream->stream_tag << AZX_PPLCCTL_STRM_SHIFT); - writel(val, stream->pplc_addr + AZX_REG_PPLCCTL); + writel(val, hext_stream->pplc_addr + AZX_REG_PPLCCTL); /* program the stream format */ - writew(fmt, stream->pplc_addr + AZX_REG_PPLCFMT); + writew(fmt, hext_stream->pplc_addr + AZX_REG_PPLCFMT); return 0; } @@ -230,7 +230,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_setup); * @stream: stream id */ void snd_hdac_ext_link_set_stream_id(struct hdac_ext_link *link, - int stream) + int stream) { snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 1 << stream); } @@ -250,10 +250,10 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_link_clear_stream_id); static struct hdac_ext_stream * hdac_ext_link_stream_assign(struct hdac_bus *bus, - struct snd_pcm_substream *substream) + struct snd_pcm_substream *substream) { struct hdac_ext_stream *res = NULL; - struct hdac_stream *stream = NULL; + struct hdac_stream *hstream = NULL; if (!bus->ppcap) { dev_err(bus->dev, "stream type not supported\n"); @@ -261,22 +261,22 @@ hdac_ext_link_stream_assign(struct hdac_bus *bus, } spin_lock_irq(&bus->reg_lock); - list_for_each_entry(stream, &bus->stream_list, list) { - struct hdac_ext_stream *hstream = container_of(stream, - struct hdac_ext_stream, - hstream); - if (stream->direction != substream->stream) + list_for_each_entry(hstream, &bus->stream_list, list) { + struct hdac_ext_stream *hext_stream = container_of(hstream, + struct hdac_ext_stream, + hstream); + if (hstream->direction != substream->stream) continue; /* check if decoupled stream and not in use is available */ - if (hstream->decoupled && !hstream->link_locked) { - res = hstream; + if (hext_stream->decoupled && !hext_stream->link_locked) { + res = hext_stream; break; } - if (!hstream->link_locked) { - snd_hdac_ext_stream_decouple_locked(bus, hstream, true); - res = hstream; + if (!hext_stream->link_locked) { + snd_hdac_ext_stream_decouple_locked(bus, hext_stream, true); + res = hext_stream; break; } } @@ -290,10 +290,10 @@ hdac_ext_link_stream_assign(struct hdac_bus *bus, static struct hdac_ext_stream * hdac_ext_host_stream_assign(struct hdac_bus *bus, - struct snd_pcm_substream *substream) + struct snd_pcm_substream *substream) { struct hdac_ext_stream *res = NULL; - struct hdac_stream *stream = NULL; + struct hdac_stream *hstream = NULL; if (!bus->ppcap) { dev_err(bus->dev, "stream type not supported\n"); @@ -301,17 +301,17 @@ hdac_ext_host_stream_assign(struct hdac_bus *bus, } spin_lock_irq(&bus->reg_lock); - list_for_each_entry(stream, &bus->stream_list, list) { - struct hdac_ext_stream *hstream = container_of(stream, - struct hdac_ext_stream, - hstream); - if (stream->direction != substream->stream) + list_for_each_entry(hstream, &bus->stream_list, list) { + struct hdac_ext_stream *hext_stream = container_of(hstream, + struct hdac_ext_stream, + hstream); + if (hstream->direction != substream->stream) continue; - if (!stream->opened) { - if (!hstream->decoupled) - snd_hdac_ext_stream_decouple_locked(bus, hstream, true); - res = hstream; + if (!hstream->opened) { + if (!hext_stream->decoupled) + snd_hdac_ext_stream_decouple_locked(bus, hext_stream, true); + res = hext_stream; break; } } @@ -346,16 +346,17 @@ struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream, int type) { - struct hdac_ext_stream *hstream = NULL; - struct hdac_stream *stream = NULL; + struct hdac_ext_stream *hext_stream = NULL; + struct hdac_stream *hstream = NULL; switch (type) { case HDAC_EXT_STREAM_TYPE_COUPLED: - stream = snd_hdac_stream_assign(bus, substream); - if (stream) - hstream = container_of(stream, - struct hdac_ext_stream, hstream); - return hstream; + hstream = snd_hdac_stream_assign(bus, substream); + if (hstream) + hext_stream = container_of(hstream, + struct hdac_ext_stream, + hstream); + return hext_stream; case HDAC_EXT_STREAM_TYPE_HOST: return hdac_ext_host_stream_assign(bus, substream); @@ -371,34 +372,34 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_assign); /** * snd_hdac_ext_stream_release - release the assigned stream - * @stream: HD-audio ext core stream to release + * @hext_stream: HD-audio ext core stream to release * @type: type of stream (coupled, host or link stream) * * Release the stream that has been assigned by snd_hdac_ext_stream_assign(). */ -void snd_hdac_ext_stream_release(struct hdac_ext_stream *stream, int type) +void snd_hdac_ext_stream_release(struct hdac_ext_stream *hext_stream, int type) { - struct hdac_bus *bus = stream->hstream.bus; + struct hdac_bus *bus = hext_stream->hstream.bus; switch (type) { case HDAC_EXT_STREAM_TYPE_COUPLED: - snd_hdac_stream_release(&stream->hstream); + snd_hdac_stream_release(&hext_stream->hstream); break; case HDAC_EXT_STREAM_TYPE_HOST: spin_lock_irq(&bus->reg_lock); - if (stream->decoupled && !stream->link_locked) - snd_hdac_ext_stream_decouple_locked(bus, stream, false); + if (hext_stream->decoupled && !hext_stream->link_locked) + snd_hdac_ext_stream_decouple_locked(bus, hext_stream, false); spin_unlock_irq(&bus->reg_lock); - snd_hdac_stream_release(&stream->hstream); + snd_hdac_stream_release(&hext_stream->hstream); break; case HDAC_EXT_STREAM_TYPE_LINK: spin_lock_irq(&bus->reg_lock); - if (stream->decoupled && !stream->hstream.opened) - snd_hdac_ext_stream_decouple_locked(bus, stream, false); - stream->link_locked = 0; - stream->link_substream = NULL; + if (hext_stream->decoupled && !hext_stream->hstream.opened) + snd_hdac_ext_stream_decouple_locked(bus, hext_stream, false); + hext_stream->link_locked = 0; + hext_stream->link_substream = NULL; spin_unlock_irq(&bus->reg_lock); break; @@ -437,11 +438,11 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_spbcap_enable); /** * snd_hdac_ext_stream_set_spib - sets the spib value of a stream * @bus: HD-audio core bus - * @stream: hdac_ext_stream + * @hext_stream: hdac_ext_stream * @value: spib value to set */ int snd_hdac_ext_stream_set_spib(struct hdac_bus *bus, - struct hdac_ext_stream *stream, u32 value) + struct hdac_ext_stream *hext_stream, u32 value) { if (!bus->spbcap) { @@ -449,7 +450,7 @@ int snd_hdac_ext_stream_set_spib(struct hdac_bus *bus, return -EINVAL; } - writel(value, stream->spib_addr); + writel(value, hext_stream->spib_addr); return 0; } @@ -458,12 +459,12 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_spib); /** * snd_hdac_ext_stream_get_spbmaxfifo - gets the spib value of a stream * @bus: HD-audio core bus - * @stream: hdac_ext_stream + * @hext_stream: hdac_ext_stream * * Return maxfifo for the stream */ int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_bus *bus, - struct hdac_ext_stream *stream) + struct hdac_ext_stream *hext_stream) { if (!bus->spbcap) { @@ -471,7 +472,7 @@ int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_bus *bus, return -EINVAL; } - return readl(stream->fifo_addr); + return readl(hext_stream->fifo_addr); } EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_get_spbmaxfifo); @@ -503,11 +504,11 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_drsm_enable); /** * snd_hdac_ext_stream_set_dpibr - sets the dpibr value of a stream * @bus: HD-audio core bus - * @stream: hdac_ext_stream + * @hext_stream: hdac_ext_stream * @value: dpib value to set */ int snd_hdac_ext_stream_set_dpibr(struct hdac_bus *bus, - struct hdac_ext_stream *stream, u32 value) + struct hdac_ext_stream *hext_stream, u32 value) { if (!bus->drsmcap) { @@ -515,7 +516,7 @@ int snd_hdac_ext_stream_set_dpibr(struct hdac_bus *bus, return -EINVAL; } - writel(value, stream->dpibr_addr); + writel(value, hext_stream->dpibr_addr); return 0; } @@ -523,12 +524,12 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_dpibr); /** * snd_hdac_ext_stream_set_lpib - sets the lpib value of a stream - * @stream: hdac_ext_stream + * @hext_stream: hdac_ext_stream * @value: lpib value to set */ -int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *stream, u32 value) +int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *hext_stream, u32 value) { - snd_hdac_stream_writel(&stream->hstream, SD_LPIB, value); + snd_hdac_stream_writel(&hext_stream->hstream, SD_LPIB, value); return 0; } -- cgit v1.2.3 From 3f48b137d88e710b67b2bcc01aa3d77b4db610c4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 17 Dec 2021 13:02:12 +0000 Subject: kselftest: alsa: Factor out check that values meet constraints To simplify the code a bit and allow future reuse factor the checks that values we read are valid out of test_ctl_get_value() into a separate function which can be reused later. As part of this extend the test to check all the values for the control, not just the first one. Signed-off-by: Mark Brown Reviewed-by: Cezary Rojewski Link: https://lore.kernel.org/r/20211217130213.3893415-2-broonie@kernel.org Signed-off-by: Takashi Iwai --- tools/testing/selftests/alsa/mixer-test.c | 141 +++++++++++++++++------------- 1 file changed, 82 insertions(+), 59 deletions(-) diff --git a/tools/testing/selftests/alsa/mixer-test.c b/tools/testing/selftests/alsa/mixer-test.c index b798a76f6825..b009fc5df605 100644 --- a/tools/testing/selftests/alsa/mixer-test.c +++ b/tools/testing/selftests/alsa/mixer-test.c @@ -193,124 +193,147 @@ void find_controls(void) snd_config_delete(config); } -/* - * Check that we can read the default value and it is valid. Write - * tests use the read value to restore the default. - */ -void test_ctl_get_value(struct ctl_data *ctl) +bool ctl_value_index_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val, + int index) { - int err; long int_val; long long int64_val; - /* If the control is turned off let's be polite */ - if (snd_ctl_elem_info_is_inactive(ctl->info)) { - ksft_print_msg("%s is inactive\n", ctl->name); - ksft_test_result_skip("get_value.%d.%d\n", - ctl->card->card, ctl->elem); - return; - } - - /* Can't test reading on an unreadable control */ - if (!snd_ctl_elem_info_is_readable(ctl->info)) { - ksft_print_msg("%s is not readable\n", ctl->name); - ksft_test_result_skip("get_value.%d.%d\n", - ctl->card->card, ctl->elem); - return; - } - - err = snd_ctl_elem_read(ctl->card->handle, ctl->def_val); - if (err < 0) { - ksft_print_msg("snd_ctl_elem_read() failed: %s\n", - snd_strerror(err)); - goto out; - } - switch (snd_ctl_elem_info_get_type(ctl->info)) { case SND_CTL_ELEM_TYPE_NONE: - ksft_print_msg("%s Invalid control type NONE\n", ctl->name); - err = -1; - break; + ksft_print_msg("%s.%d Invalid control type NONE\n", + ctl->name, index); + return false; case SND_CTL_ELEM_TYPE_BOOLEAN: - int_val = snd_ctl_elem_value_get_boolean(ctl->def_val, 0); + int_val = snd_ctl_elem_value_get_boolean(val, index); switch (int_val) { case 0: case 1: break; default: - ksft_print_msg("%s Invalid boolean value %ld\n", - ctl->name, int_val); - err = -1; - break; + ksft_print_msg("%s.%d Invalid boolean value %ld\n", + ctl->name, index, int_val); + return false; } break; case SND_CTL_ELEM_TYPE_INTEGER: - int_val = snd_ctl_elem_value_get_integer(ctl->def_val, 0); + int_val = snd_ctl_elem_value_get_integer(val, index); if (int_val < snd_ctl_elem_info_get_min(ctl->info)) { - ksft_print_msg("%s value %ld less than minimum %ld\n", - ctl->name, int_val, + ksft_print_msg("%s.%d value %ld less than minimum %ld\n", + ctl->name, index, int_val, snd_ctl_elem_info_get_min(ctl->info)); - err = -1; + return false; } if (int_val > snd_ctl_elem_info_get_max(ctl->info)) { - ksft_print_msg("%s value %ld more than maximum %ld\n", - ctl->name, int_val, + ksft_print_msg("%s.%d value %ld more than maximum %ld\n", + ctl->name, index, int_val, snd_ctl_elem_info_get_max(ctl->info)); - err = -1; + return false; } /* Only check step size if there is one and we're in bounds */ - if (err >= 0 && snd_ctl_elem_info_get_step(ctl->info) && + if (snd_ctl_elem_info_get_step(ctl->info) && (int_val - snd_ctl_elem_info_get_min(ctl->info) % snd_ctl_elem_info_get_step(ctl->info))) { - ksft_print_msg("%s value %ld invalid for step %ld minimum %ld\n", - ctl->name, int_val, + ksft_print_msg("%s.%d value %ld invalid for step %ld minimum %ld\n", + ctl->name, index, int_val, snd_ctl_elem_info_get_step(ctl->info), snd_ctl_elem_info_get_min(ctl->info)); - err = -1; + return false; } break; case SND_CTL_ELEM_TYPE_INTEGER64: - int64_val = snd_ctl_elem_value_get_integer64(ctl->def_val, 0); + int64_val = snd_ctl_elem_value_get_integer64(val, index); if (int64_val < snd_ctl_elem_info_get_min64(ctl->info)) { - ksft_print_msg("%s value %lld less than minimum %lld\n", - ctl->name, int64_val, + ksft_print_msg("%s.%d value %lld less than minimum %lld\n", + ctl->name, index, int64_val, snd_ctl_elem_info_get_min64(ctl->info)); - err = -1; + return false; } if (int64_val > snd_ctl_elem_info_get_max64(ctl->info)) { - ksft_print_msg("%s value %lld more than maximum %lld\n", - ctl->name, int64_val, + ksft_print_msg("%s.%d value %lld more than maximum %lld\n", + ctl->name, index, int64_val, snd_ctl_elem_info_get_max(ctl->info)); - err = -1; + return false; } /* Only check step size if there is one and we're in bounds */ - if (err >= 0 && snd_ctl_elem_info_get_step64(ctl->info) && + if (snd_ctl_elem_info_get_step64(ctl->info) && (int64_val - snd_ctl_elem_info_get_min64(ctl->info)) % snd_ctl_elem_info_get_step64(ctl->info)) { - ksft_print_msg("%s value %lld invalid for step %lld minimum %lld\n", - ctl->name, int64_val, + ksft_print_msg("%s.%d value %lld invalid for step %lld minimum %lld\n", + ctl->name, index, int64_val, snd_ctl_elem_info_get_step64(ctl->info), snd_ctl_elem_info_get_min64(ctl->info)); - err = -1; + return false; } break; default: /* No tests for other types */ + break; + } + + return true; +} + +/* + * Check that the provided value meets the constraints for the + * provided control. + */ +bool ctl_value_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val) +{ + int i; + bool valid = true; + + for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) + if (!ctl_value_index_valid(ctl, val, i)) + valid = false; + + return valid; +} + +/* + * Check that we can read the default value and it is valid. Write + * tests use the read value to restore the default. + */ +void test_ctl_get_value(struct ctl_data *ctl) +{ + int err; + + /* If the control is turned off let's be polite */ + if (snd_ctl_elem_info_is_inactive(ctl->info)) { + ksft_print_msg("%s is inactive\n", ctl->name); + ksft_test_result_skip("get_value.%d.%d\n", + ctl->card->card, ctl->elem); + return; + } + + /* Can't test reading on an unreadable control */ + if (!snd_ctl_elem_info_is_readable(ctl->info)) { + ksft_print_msg("%s is not readable\n", ctl->name); ksft_test_result_skip("get_value.%d.%d\n", ctl->card->card, ctl->elem); return; } + err = snd_ctl_elem_read(ctl->card->handle, ctl->def_val); + if (err < 0) { + ksft_print_msg("snd_ctl_elem_read() failed: %s\n", + snd_strerror(err)); + goto out; + } + + if (!ctl_value_valid(ctl, ctl->def_val)) + err = -EINVAL; + out: ksft_test_result(err >= 0, "get_value.%d.%d\n", ctl->card->card, ctl->elem); -- cgit v1.2.3 From 10f2f194663af178f32aeb4086fc3f6687d25056 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 17 Dec 2021 13:02:13 +0000 Subject: kselftest: alsa: Validate values read from enumerations Enumerations should return a value between 0 and items-1, check that this is the case. Signed-off-by: Mark Brown Reviewed-by: Cezary Rojewski Link: https://lore.kernel.org/r/20211217130213.3893415-3-broonie@kernel.org Signed-off-by: Takashi Iwai --- tools/testing/selftests/alsa/mixer-test.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tools/testing/selftests/alsa/mixer-test.c b/tools/testing/selftests/alsa/mixer-test.c index b009fc5df605..17f158d7a767 100644 --- a/tools/testing/selftests/alsa/mixer-test.c +++ b/tools/testing/selftests/alsa/mixer-test.c @@ -276,6 +276,23 @@ bool ctl_value_index_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val, } break; + case SND_CTL_ELEM_TYPE_ENUMERATED: + int_val = snd_ctl_elem_value_get_enumerated(val, index); + + if (int_val < 0) { + ksft_print_msg("%s.%d negative value %ld for enumeration\n", + ctl->name, index, int_val); + return false; + } + + if (int_val >= snd_ctl_elem_info_get_items(ctl->info)) { + ksft_print_msg("%s.%d value %ld more than item count %ld\n", + ctl->name, index, int_val, + snd_ctl_elem_info_get_items(ctl->info)); + return false; + } + break; + default: /* No tests for other types */ break; -- cgit v1.2.3 From 5dcdc4600c3a7773a7b901d6b7eb29340be95cf6 Mon Sep 17 00:00:00 2001 From: Yang Guang Date: Sat, 18 Dec 2021 09:54:16 +0800 Subject: ALSA: hda: use swap() to make code cleaner Use the macro 'swap()' defined in 'include/linux/minmax.h' to avoid opencoding it. Reported-by: Zeal Robot Signed-off-by: David Yang Signed-off-by: Yang Guang Link: https://lore.kernel.org/r/ebc9db44b802dfc88e1538629b517e000acb27b3.1639790796.git.yang.guang5@zte.com.cn Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_auto_parser.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 4a854475a0e6..82c492b05667 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -92,14 +92,10 @@ static int compare_input_type(const void *ap, const void *bp) */ static void reorder_outputs(unsigned int nums, hda_nid_t *pins) { - hda_nid_t nid; - switch (nums) { case 3: case 4: - nid = pins[1]; - pins[1] = pins[2]; - pins[2] = nid; + swap(pins[1], pins[2]); break; } } -- cgit v1.2.3 From 6c3a0c39130c9f29d52269cca7cf29c0e1c8d966 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 22 Dec 2021 16:53:50 +0200 Subject: ALSA: hda/hdmi: Disable silent stream on GLK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The silent stream stuff recurses back into i915 audio component .get_power() from the .pin_eld_notify() hook. On GLK this will deadlock as i915 may already be holding the relevant modeset locks during .pin_eld_notify() and the GLK audio vs. CDCLK workaround will try to grab the same locks from .get_power(). Until someone comes up with a better fix just disable the silent stream support on GLK. Cc: stable@vger.kernel.org Cc: Harsha Priya Cc: Emmanuel Jillela Cc: Kai Vehmanen Cc: Takashi Iwai Closes: https://gitlab.freedesktop.org/drm/intel/-/issues/2623 Fixes: 951894cf30f4 ("ALSA: hda/hdmi: Add Intel silent stream support") Signed-off-by: Ville Syrjälä Reviewed-by: Kai Vehmanen Link: https://lore.kernel.org/r/20211222145350.24342-1-ville.syrjala@linux.intel.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 33e5f1aa24f9..4ac2a28a3167 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -2947,7 +2947,8 @@ static int parse_intel_hdmi(struct hda_codec *codec) /* Intel Haswell and onwards; audio component with eld notifier */ static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid, - const int *port_map, int port_num, int dev_num) + const int *port_map, int port_num, int dev_num, + bool send_silent_stream) { struct hdmi_spec *spec; int err; @@ -2980,7 +2981,7 @@ static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid, * Enable silent stream feature, if it is enabled via * module param or Kconfig option */ - if (enable_silent_stream) + if (send_silent_stream) spec->send_silent_stream = true; return parse_intel_hdmi(codec); @@ -2988,12 +2989,18 @@ static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid, static int patch_i915_hsw_hdmi(struct hda_codec *codec) { - return intel_hsw_common_init(codec, 0x08, NULL, 0, 3); + return intel_hsw_common_init(codec, 0x08, NULL, 0, 3, + enable_silent_stream); } static int patch_i915_glk_hdmi(struct hda_codec *codec) { - return intel_hsw_common_init(codec, 0x0b, NULL, 0, 3); + /* + * Silent stream calls audio component .get_power() from + * .pin_eld_notify(). On GLK this will deadlock in i915 due + * to the audio vs. CDCLK workaround. + */ + return intel_hsw_common_init(codec, 0x0b, NULL, 0, 3, false); } static int patch_i915_icl_hdmi(struct hda_codec *codec) @@ -3004,7 +3011,8 @@ static int patch_i915_icl_hdmi(struct hda_codec *codec) */ static const int map[] = {0x0, 0x4, 0x6, 0x8, 0xa, 0xb}; - return intel_hsw_common_init(codec, 0x02, map, ARRAY_SIZE(map), 3); + return intel_hsw_common_init(codec, 0x02, map, ARRAY_SIZE(map), 3, + enable_silent_stream); } static int patch_i915_tgl_hdmi(struct hda_codec *codec) @@ -3016,7 +3024,8 @@ static int patch_i915_tgl_hdmi(struct hda_codec *codec) static const int map[] = {0x4, 0x6, 0x8, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; int ret; - ret = intel_hsw_common_init(codec, 0x02, map, ARRAY_SIZE(map), 4); + ret = intel_hsw_common_init(codec, 0x02, map, ARRAY_SIZE(map), 4, + enable_silent_stream); if (!ret) { struct hdmi_spec *spec = codec->spec; -- cgit v1.2.3 From 4d5a628d96532607b2e01e507f951ab19a33fc12 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Thu, 23 Dec 2021 09:34:23 +0200 Subject: ALSA: hda: Add AlderLake-N PCI ID Add HD Audio PCI ID for Intel AlderLake-N. Add rules to snd_intel_dsp_find_config() to choose DSP-based SOF driver for ADL-N systems with PCH-DMIC or Soundwire codecs, and plain HDA driver for the rest (DSP not used). Signed-off-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20211223073424.1738125-1-kai.vehmanen@linux.intel.com Signed-off-by: Takashi Iwai --- sound/hda/intel-dsp-config.c | 4 ++++ sound/pci/hda/hda_intel.c | 3 +++ 2 files changed, 7 insertions(+) diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/intel-dsp-config.c index 26f8665da689..b5f9b8d00e0b 100644 --- a/sound/hda/intel-dsp-config.c +++ b/sound/hda/intel-dsp-config.c @@ -355,6 +355,10 @@ static const struct config_entry config_table[] = { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, .device = 0x51cc, }, + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, + .device = 0x54c8, + }, #endif }; diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 221afacbc7fd..4987353ee770 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2486,6 +2486,9 @@ static const struct pci_device_id azx_ids[] = { /* Alderlake-M */ { PCI_DEVICE(0x8086, 0x51cc), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + /* Alderlake-N */ + { PCI_DEVICE(0x8086, 0x54c8), + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, /* Elkhart Lake */ { PCI_DEVICE(0x8086, 0x4b55), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, -- cgit v1.2.3 From ca1ece24d9bc5bd1d5257494654bb2b73942ddea Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Thu, 23 Dec 2021 09:34:24 +0200 Subject: ALSA: hda: Add new AlderLake-P variant PCI ID Add HD Audio PCI ID for a variant of Intel AlderLake-P. Use same driver match rules as for existing AlderLake-P devices. Signed-off-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20211223073424.1738125-2-kai.vehmanen@linux.intel.com Signed-off-by: Takashi Iwai --- sound/hda/intel-dsp-config.c | 4 ++++ sound/pci/hda/hda_intel.c | 2 ++ 2 files changed, 6 insertions(+) diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/intel-dsp-config.c index b5f9b8d00e0b..8a92d661410c 100644 --- a/sound/hda/intel-dsp-config.c +++ b/sound/hda/intel-dsp-config.c @@ -355,6 +355,10 @@ static const struct config_entry config_table[] = { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, .device = 0x51cc, }, + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, + .device = 0x51cd, + }, { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, .device = 0x54c8, diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 4987353ee770..de0c2dfb8b03 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2483,6 +2483,8 @@ static const struct pci_device_id azx_ids[] = { /* Alderlake-P */ { PCI_DEVICE(0x8086, 0x51c8), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE(0x8086, 0x51cd), + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, /* Alderlake-M */ { PCI_DEVICE(0x8086, 0x51cc), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, -- cgit v1.2.3 From 6dc86976220cc904e87ee58e4be19dd90d6a36d5 Mon Sep 17 00:00:00 2001 From: Arie Geiger Date: Thu, 23 Dec 2021 15:28:57 -0800 Subject: ALSA: hda/realtek: Add speaker fixup for some Yoga 15ITL5 devices This patch adds another possible subsystem ID for the ALC287 used by the Lenovo Yoga 15ITL5. It uses the same initalization as the others. This patch has been tested and works for my device. Signed-off-by: Arie Geiger Cc: Link: https://lore.kernel.org/r/20211223232857.30741-1-arsgeiger@gmail.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 28255e752c4a..08c0529c2310 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -8927,6 +8927,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3813, "Legion 7i 15IMHG05", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3852, "Lenovo Yoga 7 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3853, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), + SND_PCI_QUIRK(0x17aa, 0x384a, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3819, "Lenovo 13s Gen2 ITL", ALC287_FIXUP_13S_GEN2_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI), SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC), -- cgit v1.2.3 From 08977fe8cfb7d9fe9337470eec4843081cf3a76d Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Fri, 24 Dec 2021 11:50:13 +0800 Subject: ALSA: hda/realtek: Use ALC285_FIXUP_HP_GPIO_LED on another HP laptop The audio mute and mic mute LEDs don't work, so use the quirk to make them work. Signed-off-by: Kai-Heng Feng Cc: Link: https://lore.kernel.org/r/20211224035015.310068-1-kai.heng.feng@canonical.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 08c0529c2310..299e2b1b2319 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -8730,6 +8730,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8896, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8898, "HP EliteBook 845 G8 Notebook PC", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x103c, 0x88d0, "HP Pavilion 15-eh1xxx (mainboard 88D0)", ALC287_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x89c3, "HP", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x89ca, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), -- cgit v1.2.3 From 8f85317292f1d99e8a70a400a46ee697d64e3326 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 22 Dec 2021 11:19:19 -0300 Subject: ASoC: cs4265: Fix part number ID error message The Chip ID - Register 01h contains the following description as per the CS4265 datasheet: "Bits 7 through 4 are the part number ID, which is 1101b (0Dh)" The current error message is incorrect as it prints CS4265_CHIP_ID, which is the register number, instead of printing the expected part number ID value. To make it clearer, also do a shift by 4, so that the error message would become: [ 4.218083] cs4265 1-004f: CS4265 Part Number ID: 0x0 Expected: 0xd Signed-off-by: Fabio Estevam Acked-by: Charles Keepax Link: https://lore.kernel.org/r/20211222141920.1482451-1-festevam@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs4265.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c index cffd6111afac..b89002189a2b 100644 --- a/sound/soc/codecs/cs4265.c +++ b/sound/soc/codecs/cs4265.c @@ -611,8 +611,8 @@ static int cs4265_i2c_probe(struct i2c_client *i2c_client, if (devid != CS4265_CHIP_ID_VAL) { ret = -ENODEV; dev_err(&i2c_client->dev, - "CS4265 Device ID (%X). Expected %X\n", - devid, CS4265_CHIP_ID); + "CS4265 Part Number ID: 0x%x Expected: 0x%x\n", + devid >> 4, CS4265_CHIP_ID_VAL >> 4); return ret; } dev_info(&i2c_client->dev, -- cgit v1.2.3 From 3667a037e50a31555276a7989435126e501f0f15 Mon Sep 17 00:00:00 2001 From: Tzung-Bi Shih Date: Mon, 27 Dec 2021 14:21:53 +0800 Subject: ASoC: mediatek: use of_device_get_match_data() Uses of_device_get_match_data() helper to clean some boilerplate code. Signed-off-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20211227062153.3887447-1-tzungbi@google.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c | 7 ++----- sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c | 7 ++----- sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c | 7 ++----- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c index ca893feab7eb..718505c75418 100644 --- a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c @@ -685,7 +685,6 @@ static int mt8183_da7219_max98357_dev_probe(struct platform_device *pdev) struct snd_soc_dai_link *dai_link; struct mt8183_da7219_max98357_priv *priv; struct pinctrl *pinctrl; - const struct of_device_id *match; int ret, i; platform_node = of_parse_phandle(pdev->dev.of_node, @@ -695,11 +694,9 @@ static int mt8183_da7219_max98357_dev_probe(struct platform_device *pdev) return -EINVAL; } - match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev); - if (!match || !match->data) + card = (struct snd_soc_card *)of_device_get_match_data(&pdev->dev); + if (!card) return -EINVAL; - - card = (struct snd_soc_card *)match->data; card->dev = &pdev->dev; hdmi_codec = of_parse_phandle(pdev->dev.of_node, diff --git a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c index 19f8aead775d..b0ec5ebd4f2d 100644 --- a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c @@ -637,7 +637,6 @@ mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev) struct device_node *platform_node, *ec_codec, *hdmi_codec; struct snd_soc_dai_link *dai_link; struct mt8183_mt6358_ts3a227_max98357_priv *priv; - const struct of_device_id *match; int ret, i; platform_node = of_parse_phandle(pdev->dev.of_node, @@ -647,11 +646,9 @@ mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev) return -EINVAL; } - match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev); - if (!match || !match->data) + card = (struct snd_soc_card *)of_device_get_match_data(&pdev->dev); + if (!card) return -EINVAL; - - card = (struct snd_soc_card *)match->data; card->dev = &pdev->dev; ec_codec = of_parse_phandle(pdev->dev.of_node, "mediatek,ec-codec", 0); diff --git a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c index 2552f30d8fe4..f7daad1bfe1e 100644 --- a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c +++ b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c @@ -1106,7 +1106,6 @@ static int mt8192_mt6359_dev_probe(struct platform_device *pdev) struct device_node *platform_node, *hdmi_codec; int ret, i; struct snd_soc_dai_link *dai_link; - const struct of_device_id *match; struct mt8192_mt6359_priv *priv; platform_node = of_parse_phandle(pdev->dev.of_node, @@ -1116,11 +1115,9 @@ static int mt8192_mt6359_dev_probe(struct platform_device *pdev) return -EINVAL; } - match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev); - if (!match || !match->data) + card = (struct snd_soc_card *)of_device_get_match_data(&pdev->dev); + if (!card) return -EINVAL; - - card = (struct snd_soc_card *)match->data; card->dev = &pdev->dev; hdmi_codec = of_parse_phandle(pdev->dev.of_node, -- cgit v1.2.3 From 3ecb46755eb85456b459a1a9f952c52986bce8ec Mon Sep 17 00:00:00 2001 From: Jiasheng Jiang Date: Tue, 28 Dec 2021 11:40:26 +0800 Subject: ASoC: samsung: idma: Check of ioremap return value Because of the potential failure of the ioremap(), the buf->area could be NULL. Therefore, we need to check it and return -ENOMEM in order to transfer the error. Fixes: f09aecd50f39 ("ASoC: SAMSUNG: Add I2S0 internal dma driver") Signed-off-by: Jiasheng Jiang Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20211228034026.1659385-1-jiasheng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/samsung/idma.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/samsung/idma.c b/sound/soc/samsung/idma.c index 66bcc2f97544..c3f1b054e238 100644 --- a/sound/soc/samsung/idma.c +++ b/sound/soc/samsung/idma.c @@ -360,6 +360,8 @@ static int preallocate_idma_buffer(struct snd_pcm *pcm, int stream) buf->addr = idma.lp_tx_addr; buf->bytes = idma_hardware.buffer_bytes_max; buf->area = (unsigned char * __force)ioremap(buf->addr, buf->bytes); + if (!buf->area) + return -ENOMEM; return 0; } -- cgit v1.2.3 From c5ab93e289ce554a4e0d47330dde120284541aa1 Mon Sep 17 00:00:00 2001 From: Trevor Wu Date: Tue, 28 Dec 2021 14:48:21 +0800 Subject: ASoC: mediatek: mt8195: update control for RT5682 series Playback pop is observed and the root cause is the reference clock provided by MT8195 is diabled before RT5682 finishes the control flow. To ensure the reference clock supplied to RT5682 is disabled after RT5682 finishes all register controls. We replace BCLK with MCLK for RT5682 reference clock, and makes use of set_bias_level_post to handle MCLK which guarantees MCLK is off after all RT5682 register access. Signed-off-by: Trevor Wu Reviewed-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20211228064821.27865-1-trevor.wu@mediatek.com Signed-off-by: Mark Brown --- .../mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c | 56 +++++++++++++++++- .../mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c | 68 ++++++++++++++++++---- 2 files changed, 110 insertions(+), 14 deletions(-) diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c index ce8dace6527c..8adbd46c4bc4 100644 --- a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c +++ b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c @@ -18,6 +18,7 @@ #include "../../codecs/rt1011.h" #include "../../codecs/rt5682.h" #include "../common/mtk-afe-platform-driver.h" +#include "mt8195-afe-clk.h" #include "mt8195-afe-common.h" #define RT1011_CODEC_DAI "rt1011-aif" @@ -34,6 +35,7 @@ struct mt8195_mt6359_rt1011_rt5682_priv { struct snd_soc_jack headset_jack; struct snd_soc_jack dp_jack; struct snd_soc_jack hdmi_jack; + struct clk *i2so1_mclk; }; static const struct snd_soc_dapm_widget @@ -84,8 +86,8 @@ static int mt8195_rt5682_etdm_hw_params(struct snd_pcm_substream *substream, return ret; } - ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL1, RT5682_PLL1_S_BCLK1, - rate * 64, rate * 512); + ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL1, RT5682_PLL1_S_MCLK, + rate * 256, rate * 512); if (ret) { dev_err(card->dev, "failed to set pll\n"); return ret; @@ -98,7 +100,7 @@ static int mt8195_rt5682_etdm_hw_params(struct snd_pcm_substream *substream, return ret; } - return snd_soc_dai_set_sysclk(cpu_dai, 0, rate * 128, + return snd_soc_dai_set_sysclk(cpu_dai, 0, rate * 256, SND_SOC_CLOCK_OUT); } @@ -327,8 +329,14 @@ static int mt8195_rt5682_init(struct snd_soc_pcm_runtime *rtd) struct mt8195_mt6359_rt1011_rt5682_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_jack *jack = &priv->headset_jack; + struct snd_soc_component *cmpnt_afe = + snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt_afe); + struct mt8195_afe_private *afe_priv = afe->platform_priv; int ret; + priv->i2so1_mclk = afe_priv->clk[MT8195_CLK_TOP_APLL12_DIV2]; + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | @@ -562,6 +570,47 @@ static const struct snd_soc_ops mt8195_capture_ops = { .startup = mt8195_capture_startup, }; +static int mt8195_set_bias_level_post(struct snd_soc_card *card, + struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level) +{ + struct snd_soc_component *component = dapm->component; + struct mt8195_mt6359_rt1011_rt5682_priv *priv = + snd_soc_card_get_drvdata(card); + int ret; + + /* + * It's required to control mclk directly in the set_bias_level_post + * function for rt5682 and rt5682s codec, or the unexpected pop happens + * at the end of playback. + */ + if (!component || + (strcmp(component->name, RT5682_DEV0_NAME) && + strcmp(component->name, RT5682S_DEV0_NAME))) + return 0; + + switch (level) { + case SND_SOC_BIAS_OFF: + if (!__clk_is_enabled(priv->i2so1_mclk)) + return 0; + + clk_disable_unprepare(priv->i2so1_mclk); + dev_dbg(card->dev, "Disable i2so1 mclk\n"); + break; + case SND_SOC_BIAS_ON: + ret = clk_prepare_enable(priv->i2so1_mclk); + if (ret) { + dev_err(card->dev, "Can't enable i2so1 mclk: %d\n", ret); + return ret; + } + dev_dbg(card->dev, "Enable i2so1 mclk\n"); + break; + default: + break; + } + + return 0; +} + enum { DAI_LINK_DL2_FE, DAI_LINK_DL3_FE, @@ -1037,6 +1086,7 @@ static struct snd_soc_card mt8195_mt6359_rt1011_rt5682_soc_card = { .num_dapm_routes = ARRAY_SIZE(mt8195_mt6359_rt1011_rt5682_routes), .codec_conf = rt1011_amp_conf, .num_configs = ARRAY_SIZE(rt1011_amp_conf), + .set_bias_level_post = mt8195_set_bias_level_post, }; static int mt8195_mt6359_rt1011_rt5682_dev_probe(struct platform_device *pdev) diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c index c15c58170e9d..20b351faeaff 100644 --- a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c +++ b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c @@ -19,6 +19,7 @@ #include "../../codecs/mt6359.h" #include "../../codecs/rt5682.h" #include "../common/mtk-afe-platform-driver.h" +#include "mt8195-afe-clk.h" #include "mt8195-afe-common.h" #define RT1019_CODEC_DAI "HiFi" @@ -46,6 +47,7 @@ struct mt8195_mt6359_rt1019_rt5682_priv { struct snd_soc_jack headset_jack; struct snd_soc_jack dp_jack; struct snd_soc_jack hdmi_jack; + struct clk *i2so1_mclk; }; static const struct snd_soc_dapm_widget @@ -92,8 +94,6 @@ static int mt8195_rt5682_etdm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); unsigned int rate = params_rate(params); - unsigned int mclk_fs_ratio = 128; - unsigned int mclk_fs = rate * mclk_fs_ratio; int bitwidth; int ret; @@ -109,25 +109,22 @@ static int mt8195_rt5682_etdm_hw_params(struct snd_pcm_substream *substream, return ret; } - ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL1, - RT5682_PLL1_S_BCLK1, - params_rate(params) * 64, - params_rate(params) * 512); + ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL1, RT5682_PLL1_S_MCLK, + rate * 256, rate * 512); if (ret) { dev_err(card->dev, "failed to set pll\n"); return ret; } - ret = snd_soc_dai_set_sysclk(codec_dai, - RT5682_SCLK_S_PLL1, - params_rate(params) * 512, - SND_SOC_CLOCK_IN); + ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1, + rate * 512, SND_SOC_CLOCK_IN); if (ret) { dev_err(card->dev, "failed to set sysclk\n"); return ret; } - return snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_fs, SND_SOC_CLOCK_OUT); + return snd_soc_dai_set_sysclk(cpu_dai, 0, rate * 256, + SND_SOC_CLOCK_OUT); } static const struct snd_soc_ops mt8195_rt5682_etdm_ops = { @@ -322,8 +319,14 @@ static int mt8195_rt5682_init(struct snd_soc_pcm_runtime *rtd) struct mt8195_mt6359_rt1019_rt5682_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_jack *jack = &priv->headset_jack; + struct snd_soc_component *cmpnt_afe = + snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt_afe); + struct mt8195_afe_private *afe_priv = afe->platform_priv; int ret; + priv->i2so1_mclk = afe_priv->clk[MT8195_CLK_TOP_APLL12_DIV2]; + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | @@ -560,6 +563,48 @@ static const struct snd_soc_ops mt8195_capture_ops = { .startup = mt8195_capture_startup, }; +static int mt8195_set_bias_level_post(struct snd_soc_card *card, + struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level) +{ + struct snd_soc_component *component = dapm->component; + struct mt8195_mt6359_rt1019_rt5682_priv *priv = + snd_soc_card_get_drvdata(card); + int ret; + + /* + * It's required to control mclk directly in the set_bias_level_post + * function for rt5682 and rt5682s codec, or the unexpected pop happens + * at the end of playback. + */ + if (!component || + (strcmp(component->name, RT5682_DEV0_NAME) && + strcmp(component->name, RT5682S_DEV0_NAME))) + return 0; + + + switch (level) { + case SND_SOC_BIAS_OFF: + if (!__clk_is_enabled(priv->i2so1_mclk)) + return 0; + + clk_disable_unprepare(priv->i2so1_mclk); + dev_dbg(card->dev, "Disable i2so1 mclk\n"); + break; + case SND_SOC_BIAS_ON: + ret = clk_prepare_enable(priv->i2so1_mclk); + if (ret) { + dev_err(card->dev, "Can't enable i2so1 mclk: %d\n", ret); + return ret; + } + dev_dbg(card->dev, "Enable i2so1 mclk\n"); + break; + default: + break; + } + + return 0; +} + enum { DAI_LINK_DL2_FE, DAI_LINK_DL3_FE, @@ -1199,6 +1244,7 @@ static struct snd_soc_card mt8195_mt6359_rt1019_rt5682_soc_card = { .num_dapm_widgets = ARRAY_SIZE(mt8195_mt6359_rt1019_rt5682_widgets), .dapm_routes = mt8195_mt6359_rt1019_rt5682_routes, .num_dapm_routes = ARRAY_SIZE(mt8195_mt6359_rt1019_rt5682_routes), + .set_bias_level_post = mt8195_set_bias_level_post, }; static int mt8195_dailink_parse_of(struct snd_soc_card *card, struct device_node *np, -- cgit v1.2.3 From cc5c9788106fb1b9e03c8c57d8d7166073a54416 Mon Sep 17 00:00:00 2001 From: Derek Fang Date: Mon, 27 Dec 2021 13:54:46 +0800 Subject: ASoC: rt5682: Register wclk with its parent_hws instead of parent_data The mclk might not be registered as a fixed clk name "mclk" on some platforms. In those platforms, if the mclk needed to be controlled by codec driver and acquired by a fixed name, it would be a problem. This patch to fix the issue that wclk becomes an orphan due to the fixed mclk's name. Signed-off-by: Derek Fang Link: https://lore.kernel.org/r/20211227055446.27563-1-derek.fang@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5682.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index 5224123d0d3b..4709c0628804 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -2858,7 +2858,6 @@ int rt5682_register_dai_clks(struct rt5682_priv *rt5682) for (i = 0; i < RT5682_DAI_NUM_CLKS; ++i) { struct clk_init_data init = { }; - struct clk_parent_data parent_data; const struct clk_hw *parent; dai_clk_hw = &rt5682->dai_clks_hw[i]; @@ -2867,10 +2866,8 @@ int rt5682_register_dai_clks(struct rt5682_priv *rt5682) case RT5682_DAI_WCLK_IDX: /* Make MCLK the parent of WCLK */ if (rt5682->mclk) { - parent_data = (struct clk_parent_data){ - .fw_name = "mclk", - }; - init.parent_data = &parent_data; + parent = __clk_get_hw(rt5682->mclk); + init.parent_hws = &parent; init.num_parents = 1; } break; -- cgit v1.2.3 From 2355028c0c54c03afb66c589347f1dc9f6fe2e38 Mon Sep 17 00:00:00 2001 From: Trevor Wu Date: Thu, 30 Dec 2021 16:47:30 +0800 Subject: ASoC: mediatek: mt8195: correct pcmif BE dai control flow Originally, the conditions for preventing reentry are not correct. dai->component->active is not the state specifically for pcmif dai, so it is not a correct condition to indicate the status of pcmif dai. On the other hand, snd_soc_dai_stream_actvie() in prepare ops for both playback and capture possibly return true at the first entry when these two streams are opened at the same time. In the patch, I refer to the implementation in mt8192-dai-pcm.c. Clock and enabling bit for PCMIF are managed by DAPM, and the condition for prepare ops is replaced by the status of dai widget. Fixes: 1f95c019115c ("ASoC: mediatek: mt8195: support pcm in platform driver") Signed-off-by: Trevor Wu Link: https://lore.kernel.org/r/20211230084731.31372-2-trevor.wu@mediatek.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8195/mt8195-dai-pcm.c | 73 +++++++++--------------------- sound/soc/mediatek/mt8195/mt8195-reg.h | 1 + 2 files changed, 22 insertions(+), 52 deletions(-) diff --git a/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c b/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c index 5d10d2c4c991..151914c873ac 100644 --- a/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c +++ b/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c @@ -80,8 +80,15 @@ static const struct snd_soc_dapm_widget mtk_dai_pcm_widgets[] = { mtk_dai_pcm_o001_mix, ARRAY_SIZE(mtk_dai_pcm_o001_mix)), + SND_SOC_DAPM_SUPPLY("PCM_EN", PCM_INTF_CON1, + PCM_INTF_CON1_PCM_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_INPUT("PCM1_INPUT"), SND_SOC_DAPM_OUTPUT("PCM1_OUTPUT"), + + SND_SOC_DAPM_CLOCK_SUPPLY("aud_asrc11"), + SND_SOC_DAPM_CLOCK_SUPPLY("aud_asrc12"), + SND_SOC_DAPM_CLOCK_SUPPLY("aud_pcmif"), }; static const struct snd_soc_dapm_route mtk_dai_pcm_routes[] = { @@ -97,22 +104,18 @@ static const struct snd_soc_dapm_route mtk_dai_pcm_routes[] = { {"PCM1 Playback", NULL, "O000"}, {"PCM1 Playback", NULL, "O001"}, + {"PCM1 Playback", NULL, "PCM_EN"}, + {"PCM1 Playback", NULL, "aud_asrc12"}, + {"PCM1 Playback", NULL, "aud_pcmif"}, + + {"PCM1 Capture", NULL, "PCM_EN"}, + {"PCM1 Capture", NULL, "aud_asrc11"}, + {"PCM1 Capture", NULL, "aud_pcmif"}, + {"PCM1_OUTPUT", NULL, "PCM1 Playback"}, {"PCM1 Capture", NULL, "PCM1_INPUT"}, }; -static void mtk_dai_pcm_enable(struct mtk_base_afe *afe) -{ - regmap_update_bits(afe->regmap, PCM_INTF_CON1, - PCM_INTF_CON1_PCM_EN, PCM_INTF_CON1_PCM_EN); -} - -static void mtk_dai_pcm_disable(struct mtk_base_afe *afe) -{ - regmap_update_bits(afe->regmap, PCM_INTF_CON1, - PCM_INTF_CON1_PCM_EN, 0x0); -} - static int mtk_dai_pcm_configure(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -207,54 +210,22 @@ static int mtk_dai_pcm_configure(struct snd_pcm_substream *substream, } /* dai ops */ -static int mtk_dai_pcm_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); - struct mt8195_afe_private *afe_priv = afe->platform_priv; - - if (dai->component->active) - return 0; - - mt8195_afe_enable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_ASRC11]); - mt8195_afe_enable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_ASRC12]); - mt8195_afe_enable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_PCMIF]); - - return 0; -} - -static void mtk_dai_pcm_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); - struct mt8195_afe_private *afe_priv = afe->platform_priv; - - if (dai->component->active) - return; - - mtk_dai_pcm_disable(afe); - - mt8195_afe_disable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_PCMIF]); - mt8195_afe_disable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_ASRC12]); - mt8195_afe_disable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_ASRC11]); -} - static int mtk_dai_pcm_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); - int ret = 0; + int ret; - if (snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_PLAYBACK) && - snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_CAPTURE)) + dev_dbg(dai->dev, "%s(), id %d, stream %d, widget active p %d, c %d\n", + __func__, dai->id, substream->stream, + dai->playback_widget->active, dai->capture_widget->active); + + if (dai->playback_widget->active || dai->capture_widget->active) return 0; ret = mtk_dai_pcm_configure(substream, dai); if (ret) return ret; - mtk_dai_pcm_enable(afe); - return 0; } @@ -316,8 +287,6 @@ static int mtk_dai_pcm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) } static const struct snd_soc_dai_ops mtk_dai_pcm_ops = { - .startup = mtk_dai_pcm_startup, - .shutdown = mtk_dai_pcm_shutdown, .prepare = mtk_dai_pcm_prepare, .set_fmt = mtk_dai_pcm_set_fmt, }; diff --git a/sound/soc/mediatek/mt8195/mt8195-reg.h b/sound/soc/mediatek/mt8195/mt8195-reg.h index d06f9cf85a4e..d3871353db41 100644 --- a/sound/soc/mediatek/mt8195/mt8195-reg.h +++ b/sound/soc/mediatek/mt8195/mt8195-reg.h @@ -2550,6 +2550,7 @@ #define PCM_INTF_CON1_PCM_FMT(x) (((x) & 0x3) << 1) #define PCM_INTF_CON1_PCM_FMT_MASK (0x3 << 1) #define PCM_INTF_CON1_PCM_EN BIT(0) +#define PCM_INTF_CON1_PCM_EN_SHIFT 0 /* PCM_INTF_CON2 */ #define PCM_INTF_CON2_CLK_DOMAIN_SEL(x) (((x) & 0x3) << 23) -- cgit v1.2.3 From db5e1c209b92a67ab7c1d7771a48294c9c093f7c Mon Sep 17 00:00:00 2001 From: Trevor Wu Date: Thu, 30 Dec 2021 16:47:31 +0800 Subject: ASoC: mediatek: mt8195: add playback support to PCM1_BE dai_link PCM1_BE should be a dai_link for both playback and capture. In the patch, the missing DPCM playback support is added. Signed-off-by: Trevor Wu Link: https://lore.kernel.org/r/20211230084731.31372-3-trevor.wu@mediatek.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c | 1 + sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c | 1 + 2 files changed, 2 insertions(+) diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c index 8adbd46c4bc4..5443a29da7b1 100644 --- a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c +++ b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c @@ -1045,6 +1045,7 @@ static struct snd_soc_dai_link mt8195_mt6359_rt1011_rt5682_dai_links[] = { .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, + .dpcm_playback = 1, .dpcm_capture = 1, SND_SOC_DAILINK_REG(PCM1_BE), }, diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c index 20b351faeaff..29c2d3407cc7 100644 --- a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c +++ b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c @@ -1191,6 +1191,7 @@ static struct snd_soc_dai_link mt8195_mt6359_rt1019_rt5682_dai_links[] = { .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, + .dpcm_playback = 1, .dpcm_capture = 1, SND_SOC_DAILINK_REG(PCM1_BE), }, -- cgit v1.2.3 From a87d42227cf5614fe0040ddd1fe642c54298b42c Mon Sep 17 00:00:00 2001 From: Lucas Tanure Date: Fri, 17 Dec 2021 11:56:59 +0000 Subject: ASoC: cs35l41: Convert tables to shared source code To support CS35L41 in HDA systems the HDA driver for CS35L41 would have to duplicate some functions that already exist on ASoC driver So instead of duplicate the code, use the new lib source as a shared resource for both ASoC and HDA Also, change the way CONFIG_SND_SOC_CS35L41 is selected, as reported by Intel Kernel test robot, it is possible to build SND_SOC_CS35L41_SPI/I2C without the main driver, which would lead to build failures. Signed-off-by: Lucas Tanure Reported-by: kernel test robot Link: https://lore.kernel.org/r/20211217115708.882525-2-tanureal@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/cs35l41.h | 733 +++++++++++++++++++++++++++++++++++++ sound/soc/codecs/Kconfig | 11 +- sound/soc/codecs/Makefile | 4 +- sound/soc/codecs/cs35l41-i2c.c | 1 - sound/soc/codecs/cs35l41-lib.c | 733 +++++++++++++++++++++++++++++++++++++ sound/soc/codecs/cs35l41-spi.c | 1 - sound/soc/codecs/cs35l41-tables.c | 723 ------------------------------------- sound/soc/codecs/cs35l41.h | 734 -------------------------------------- 8 files changed, 1476 insertions(+), 1464 deletions(-) create mode 100644 sound/soc/codecs/cs35l41-lib.c delete mode 100644 sound/soc/codecs/cs35l41-tables.c diff --git a/include/sound/cs35l41.h b/include/sound/cs35l41.h index 1f1e3c6c9be1..aac3ffb9bc89 100644 --- a/include/sound/cs35l41.h +++ b/include/sound/cs35l41.h @@ -10,6 +10,721 @@ #ifndef __CS35L41_H #define __CS35L41_H +#include + +#define CS35L41_FIRSTREG 0x00000000 +#define CS35L41_LASTREG 0x03804FE8 +#define CS35L41_DEVID 0x00000000 +#define CS35L41_REVID 0x00000004 +#define CS35L41_FABID 0x00000008 +#define CS35L41_RELID 0x0000000C +#define CS35L41_OTPID 0x00000010 +#define CS35L41_SFT_RESET 0x00000020 +#define CS35L41_TEST_KEY_CTL 0x00000040 +#define CS35L41_USER_KEY_CTL 0x00000044 +#define CS35L41_OTP_MEM0 0x00000400 +#define CS35L41_OTP_MEM31 0x0000047C +#define CS35L41_OTP_CTRL0 0x00000500 +#define CS35L41_OTP_CTRL1 0x00000504 +#define CS35L41_OTP_CTRL3 0x00000508 +#define CS35L41_OTP_CTRL4 0x0000050C +#define CS35L41_OTP_CTRL5 0x00000510 +#define CS35L41_OTP_CTRL6 0x00000514 +#define CS35L41_OTP_CTRL7 0x00000518 +#define CS35L41_OTP_CTRL8 0x0000051C +#define CS35L41_PWR_CTRL1 0x00002014 +#define CS35L41_PWR_CTRL2 0x00002018 +#define CS35L41_PWR_CTRL3 0x0000201C +#define CS35L41_CTRL_OVRRIDE 0x00002020 +#define CS35L41_AMP_OUT_MUTE 0x00002024 +#define CS35L41_PROTECT_REL_ERR_IGN 0x00002034 +#define CS35L41_GPIO_PAD_CONTROL 0x0000242C +#define CS35L41_JTAG_CONTROL 0x00002438 +#define CS35L41_PLL_CLK_CTRL 0x00002C04 +#define CS35L41_DSP_CLK_CTRL 0x00002C08 +#define CS35L41_GLOBAL_CLK_CTRL 0x00002C0C +#define CS35L41_DATA_FS_SEL 0x00002C10 +#define CS35L41_TST_FS_MON0 0x00002D10 +#define CS35L41_MDSYNC_EN 0x00003400 +#define CS35L41_MDSYNC_TX_ID 0x00003408 +#define CS35L41_MDSYNC_PWR_CTRL 0x0000340C +#define CS35L41_MDSYNC_DATA_TX 0x00003410 +#define CS35L41_MDSYNC_TX_STATUS 0x00003414 +#define CS35L41_MDSYNC_DATA_RX 0x0000341C +#define CS35L41_MDSYNC_RX_STATUS 0x00003420 +#define CS35L41_MDSYNC_ERR_STATUS 0x00003424 +#define CS35L41_MDSYNC_SYNC_PTE2 0x00003528 +#define CS35L41_MDSYNC_SYNC_PTE3 0x0000352C +#define CS35L41_MDSYNC_SYNC_MSM_STATUS 0x0000353C +#define CS35L41_BSTCVRT_VCTRL1 0x00003800 +#define CS35L41_BSTCVRT_VCTRL2 0x00003804 +#define CS35L41_BSTCVRT_PEAK_CUR 0x00003808 +#define CS35L41_BSTCVRT_SFT_RAMP 0x0000380C +#define CS35L41_BSTCVRT_COEFF 0x00003810 +#define CS35L41_BSTCVRT_SLOPE_LBST 0x00003814 +#define CS35L41_BSTCVRT_SW_FREQ 0x00003818 +#define CS35L41_BSTCVRT_DCM_CTRL 0x0000381C +#define CS35L41_BSTCVRT_DCM_MODE_FORCE 0x00003820 +#define CS35L41_BSTCVRT_OVERVOLT_CTRL 0x00003830 +#define CS35L41_VI_VOL_POL 0x00004000 +#define CS35L41_VIMON_SPKMON_RESYNC 0x00004100 +#define CS35L41_DTEMP_WARN_THLD 0x00004220 +#define CS35L41_DTEMP_CFG 0x00004224 +#define CS35L41_DTEMP_EN 0x00004308 +#define CS35L41_VPVBST_FS_SEL 0x00004400 +#define CS35L41_SP_ENABLES 0x00004800 +#define CS35L41_SP_RATE_CTRL 0x00004804 +#define CS35L41_SP_FORMAT 0x00004808 +#define CS35L41_SP_HIZ_CTRL 0x0000480C +#define CS35L41_SP_FRAME_TX_SLOT 0x00004810 +#define CS35L41_SP_FRAME_RX_SLOT 0x00004820 +#define CS35L41_SP_TX_WL 0x00004830 +#define CS35L41_SP_RX_WL 0x00004840 +#define CS35L41_ASP_CONTROL4 0x00004854 +#define CS35L41_DAC_PCM1_SRC 0x00004C00 +#define CS35L41_ASP_TX1_SRC 0x00004C20 +#define CS35L41_ASP_TX2_SRC 0x00004C24 +#define CS35L41_ASP_TX3_SRC 0x00004C28 +#define CS35L41_ASP_TX4_SRC 0x00004C2C +#define CS35L41_DSP1_RX1_SRC 0x00004C40 +#define CS35L41_DSP1_RX2_SRC 0x00004C44 +#define CS35L41_DSP1_RX3_SRC 0x00004C48 +#define CS35L41_DSP1_RX4_SRC 0x00004C4C +#define CS35L41_DSP1_RX5_SRC 0x00004C50 +#define CS35L41_DSP1_RX6_SRC 0x00004C54 +#define CS35L41_DSP1_RX7_SRC 0x00004C58 +#define CS35L41_DSP1_RX8_SRC 0x00004C5C +#define CS35L41_NGATE1_SRC 0x00004C60 +#define CS35L41_NGATE2_SRC 0x00004C64 +#define CS35L41_AMP_DIG_VOL_CTRL 0x00006000 +#define CS35L41_VPBR_CFG 0x00006404 +#define CS35L41_VBBR_CFG 0x00006408 +#define CS35L41_VPBR_STATUS 0x0000640C +#define CS35L41_VBBR_STATUS 0x00006410 +#define CS35L41_OVERTEMP_CFG 0x00006414 +#define CS35L41_AMP_ERR_VOL 0x00006418 +#define CS35L41_VOL_STATUS_TO_DSP 0x00006450 +#define CS35L41_CLASSH_CFG 0x00006800 +#define CS35L41_WKFET_CFG 0x00006804 +#define CS35L41_NG_CFG 0x00006808 +#define CS35L41_AMP_GAIN_CTRL 0x00006C04 +#define CS35L41_DAC_MSM_CFG 0x00007400 +#define CS35L41_IRQ1_CFG 0x00010000 +#define CS35L41_IRQ1_STATUS 0x00010004 +#define CS35L41_IRQ1_STATUS1 0x00010010 +#define CS35L41_IRQ1_STATUS2 0x00010014 +#define CS35L41_IRQ1_STATUS3 0x00010018 +#define CS35L41_IRQ1_STATUS4 0x0001001C +#define CS35L41_IRQ1_RAW_STATUS1 0x00010090 +#define CS35L41_IRQ1_RAW_STATUS2 0x00010094 +#define CS35L41_IRQ1_RAW_STATUS3 0x00010098 +#define CS35L41_IRQ1_RAW_STATUS4 0x0001009C +#define CS35L41_IRQ1_MASK1 0x00010110 +#define CS35L41_IRQ1_MASK2 0x00010114 +#define CS35L41_IRQ1_MASK3 0x00010118 +#define CS35L41_IRQ1_MASK4 0x0001011C +#define CS35L41_IRQ1_FRC1 0x00010190 +#define CS35L41_IRQ1_FRC2 0x00010194 +#define CS35L41_IRQ1_FRC3 0x00010198 +#define CS35L41_IRQ1_FRC4 0x0001019C +#define CS35L41_IRQ1_EDGE1 0x00010210 +#define CS35L41_IRQ1_EDGE4 0x0001021C +#define CS35L41_IRQ1_POL1 0x00010290 +#define CS35L41_IRQ1_POL2 0x00010294 +#define CS35L41_IRQ1_POL3 0x00010298 +#define CS35L41_IRQ1_POL4 0x0001029C +#define CS35L41_IRQ1_DB3 0x00010318 +#define CS35L41_IRQ2_CFG 0x00010800 +#define CS35L41_IRQ2_STATUS 0x00010804 +#define CS35L41_IRQ2_STATUS1 0x00010810 +#define CS35L41_IRQ2_STATUS2 0x00010814 +#define CS35L41_IRQ2_STATUS3 0x00010818 +#define CS35L41_IRQ2_STATUS4 0x0001081C +#define CS35L41_IRQ2_RAW_STATUS1 0x00010890 +#define CS35L41_IRQ2_RAW_STATUS2 0x00010894 +#define CS35L41_IRQ2_RAW_STATUS3 0x00010898 +#define CS35L41_IRQ2_RAW_STATUS4 0x0001089C +#define CS35L41_IRQ2_MASK1 0x00010910 +#define CS35L41_IRQ2_MASK2 0x00010914 +#define CS35L41_IRQ2_MASK3 0x00010918 +#define CS35L41_IRQ2_MASK4 0x0001091C +#define CS35L41_IRQ2_FRC1 0x00010990 +#define CS35L41_IRQ2_FRC2 0x00010994 +#define CS35L41_IRQ2_FRC3 0x00010998 +#define CS35L41_IRQ2_FRC4 0x0001099C +#define CS35L41_IRQ2_EDGE1 0x00010A10 +#define CS35L41_IRQ2_EDGE4 0x00010A1C +#define CS35L41_IRQ2_POL1 0x00010A90 +#define CS35L41_IRQ2_POL2 0x00010A94 +#define CS35L41_IRQ2_POL3 0x00010A98 +#define CS35L41_IRQ2_POL4 0x00010A9C +#define CS35L41_IRQ2_DB3 0x00010B18 +#define CS35L41_GPIO_STATUS1 0x00011000 +#define CS35L41_GPIO1_CTRL1 0x00011008 +#define CS35L41_GPIO2_CTRL1 0x0001100C +#define CS35L41_MIXER_NGATE_CFG 0x00012000 +#define CS35L41_MIXER_NGATE_CH1_CFG 0x00012004 +#define CS35L41_MIXER_NGATE_CH2_CFG 0x00012008 +#define CS35L41_DSP_MBOX_1 0x00013000 +#define CS35L41_DSP_MBOX_2 0x00013004 +#define CS35L41_DSP_MBOX_3 0x00013008 +#define CS35L41_DSP_MBOX_4 0x0001300C +#define CS35L41_DSP_MBOX_5 0x00013010 +#define CS35L41_DSP_MBOX_6 0x00013014 +#define CS35L41_DSP_MBOX_7 0x00013018 +#define CS35L41_DSP_MBOX_8 0x0001301C +#define CS35L41_DSP_VIRT1_MBOX_1 0x00013020 +#define CS35L41_DSP_VIRT1_MBOX_2 0x00013024 +#define CS35L41_DSP_VIRT1_MBOX_3 0x00013028 +#define CS35L41_DSP_VIRT1_MBOX_4 0x0001302C +#define CS35L41_DSP_VIRT1_MBOX_5 0x00013030 +#define CS35L41_DSP_VIRT1_MBOX_6 0x00013034 +#define CS35L41_DSP_VIRT1_MBOX_7 0x00013038 +#define CS35L41_DSP_VIRT1_MBOX_8 0x0001303C +#define CS35L41_DSP_VIRT2_MBOX_1 0x00013040 +#define CS35L41_DSP_VIRT2_MBOX_2 0x00013044 +#define CS35L41_DSP_VIRT2_MBOX_3 0x00013048 +#define CS35L41_DSP_VIRT2_MBOX_4 0x0001304C +#define CS35L41_DSP_VIRT2_MBOX_5 0x00013050 +#define CS35L41_DSP_VIRT2_MBOX_6 0x00013054 +#define CS35L41_DSP_VIRT2_MBOX_7 0x00013058 +#define CS35L41_DSP_VIRT2_MBOX_8 0x0001305C +#define CS35L41_CLOCK_DETECT_1 0x00014000 +#define CS35L41_TIMER1_CONTROL 0x00015000 +#define CS35L41_TIMER1_COUNT_PRESET 0x00015004 +#define CS35L41_TIMER1_START_STOP 0x0001500C +#define CS35L41_TIMER1_STATUS 0x00015010 +#define CS35L41_TIMER1_COUNT_READBACK 0x00015014 +#define CS35L41_TIMER1_DSP_CLK_CFG 0x00015018 +#define CS35L41_TIMER1_DSP_CLK_STATUS 0x0001501C +#define CS35L41_TIMER2_CONTROL 0x00015100 +#define CS35L41_TIMER2_COUNT_PRESET 0x00015104 +#define CS35L41_TIMER2_START_STOP 0x0001510C +#define CS35L41_TIMER2_STATUS 0x00015110 +#define CS35L41_TIMER2_COUNT_READBACK 0x00015114 +#define CS35L41_TIMER2_DSP_CLK_CFG 0x00015118 +#define CS35L41_TIMER2_DSP_CLK_STATUS 0x0001511C +#define CS35L41_DFT_JTAG_CONTROL 0x00016000 +#define CS35L41_DIE_STS1 0x00017040 +#define CS35L41_DIE_STS2 0x00017044 +#define CS35L41_TEMP_CAL1 0x00017048 +#define CS35L41_TEMP_CAL2 0x0001704C +#define CS35L41_DSP1_XMEM_PACK_0 0x02000000 +#define CS35L41_DSP1_XMEM_PACK_3068 0x02002FF0 +#define CS35L41_DSP1_XMEM_UNPACK32_0 0x02400000 +#define CS35L41_DSP1_XMEM_UNPACK32_2046 0x02401FF8 +#define CS35L41_DSP1_TIMESTAMP_COUNT 0x025C0800 +#define CS35L41_DSP1_SYS_ID 0x025E0000 +#define CS35L41_DSP1_SYS_VERSION 0x025E0004 +#define CS35L41_DSP1_SYS_CORE_ID 0x025E0008 +#define CS35L41_DSP1_SYS_AHB_ADDR 0x025E000C +#define CS35L41_DSP1_SYS_XSRAM_SIZE 0x025E0010 +#define CS35L41_DSP1_SYS_YSRAM_SIZE 0x025E0018 +#define CS35L41_DSP1_SYS_PSRAM_SIZE 0x025E0020 +#define CS35L41_DSP1_SYS_PM_BOOT_SIZE 0x025E0028 +#define CS35L41_DSP1_SYS_FEATURES 0x025E002C +#define CS35L41_DSP1_SYS_FIR_FILTERS 0x025E0030 +#define CS35L41_DSP1_SYS_LMS_FILTERS 0x025E0034 +#define CS35L41_DSP1_SYS_XM_BANK_SIZE 0x025E0038 +#define CS35L41_DSP1_SYS_YM_BANK_SIZE 0x025E003C +#define CS35L41_DSP1_SYS_PM_BANK_SIZE 0x025E0040 +#define CS35L41_DSP1_AHBM_WIN0_CTRL0 0x025E2000 +#define CS35L41_DSP1_AHBM_WIN0_CTRL1 0x025E2004 +#define CS35L41_DSP1_AHBM_WIN1_CTRL0 0x025E2008 +#define CS35L41_DSP1_AHBM_WIN1_CTRL1 0x025E200C +#define CS35L41_DSP1_AHBM_WIN2_CTRL0 0x025E2010 +#define CS35L41_DSP1_AHBM_WIN2_CTRL1 0x025E2014 +#define CS35L41_DSP1_AHBM_WIN3_CTRL0 0x025E2018 +#define CS35L41_DSP1_AHBM_WIN3_CTRL1 0x025E201C +#define CS35L41_DSP1_AHBM_WIN4_CTRL0 0x025E2020 +#define CS35L41_DSP1_AHBM_WIN4_CTRL1 0x025E2024 +#define CS35L41_DSP1_AHBM_WIN5_CTRL0 0x025E2028 +#define CS35L41_DSP1_AHBM_WIN5_CTRL1 0x025E202C +#define CS35L41_DSP1_AHBM_WIN6_CTRL0 0x025E2030 +#define CS35L41_DSP1_AHBM_WIN6_CTRL1 0x025E2034 +#define CS35L41_DSP1_AHBM_WIN7_CTRL0 0x025E2038 +#define CS35L41_DSP1_AHBM_WIN7_CTRL1 0x025E203C +#define CS35L41_DSP1_AHBM_WIN_DBG_CTRL0 0x025E2040 +#define CS35L41_DSP1_AHBM_WIN_DBG_CTRL1 0x025E2044 +#define CS35L41_DSP1_XMEM_UNPACK24_0 0x02800000 +#define CS35L41_DSP1_XMEM_UNPACK24_4093 0x02803FF4 +#define CS35L41_DSP1_CTRL_BASE 0x02B80000 +#define CS35L41_DSP1_CORE_SOFT_RESET 0x02B80010 +#define CS35L41_DSP1_DEBUG 0x02B80040 +#define CS35L41_DSP1_TIMER_CTRL 0x02B80048 +#define CS35L41_DSP1_STREAM_ARB_CTRL 0x02B80050 +#define CS35L41_DSP1_RX1_RATE 0x02B80080 +#define CS35L41_DSP1_RX2_RATE 0x02B80088 +#define CS35L41_DSP1_RX3_RATE 0x02B80090 +#define CS35L41_DSP1_RX4_RATE 0x02B80098 +#define CS35L41_DSP1_RX5_RATE 0x02B800A0 +#define CS35L41_DSP1_RX6_RATE 0x02B800A8 +#define CS35L41_DSP1_RX7_RATE 0x02B800B0 +#define CS35L41_DSP1_RX8_RATE 0x02B800B8 +#define CS35L41_DSP1_TX1_RATE 0x02B80280 +#define CS35L41_DSP1_TX2_RATE 0x02B80288 +#define CS35L41_DSP1_TX3_RATE 0x02B80290 +#define CS35L41_DSP1_TX4_RATE 0x02B80298 +#define CS35L41_DSP1_TX5_RATE 0x02B802A0 +#define CS35L41_DSP1_TX6_RATE 0x02B802A8 +#define CS35L41_DSP1_TX7_RATE 0x02B802B0 +#define CS35L41_DSP1_TX8_RATE 0x02B802B8 +#define CS35L41_DSP1_NMI_CTRL1 0x02B80480 +#define CS35L41_DSP1_NMI_CTRL2 0x02B80488 +#define CS35L41_DSP1_NMI_CTRL3 0x02B80490 +#define CS35L41_DSP1_NMI_CTRL4 0x02B80498 +#define CS35L41_DSP1_NMI_CTRL5 0x02B804A0 +#define CS35L41_DSP1_NMI_CTRL6 0x02B804A8 +#define CS35L41_DSP1_NMI_CTRL7 0x02B804B0 +#define CS35L41_DSP1_NMI_CTRL8 0x02B804B8 +#define CS35L41_DSP1_RESUME_CTRL 0x02B80500 +#define CS35L41_DSP1_IRQ1_CTRL 0x02B80508 +#define CS35L41_DSP1_IRQ2_CTRL 0x02B80510 +#define CS35L41_DSP1_IRQ3_CTRL 0x02B80518 +#define CS35L41_DSP1_IRQ4_CTRL 0x02B80520 +#define CS35L41_DSP1_IRQ5_CTRL 0x02B80528 +#define CS35L41_DSP1_IRQ6_CTRL 0x02B80530 +#define CS35L41_DSP1_IRQ7_CTRL 0x02B80538 +#define CS35L41_DSP1_IRQ8_CTRL 0x02B80540 +#define CS35L41_DSP1_IRQ9_CTRL 0x02B80548 +#define CS35L41_DSP1_IRQ10_CTRL 0x02B80550 +#define CS35L41_DSP1_IRQ11_CTRL 0x02B80558 +#define CS35L41_DSP1_IRQ12_CTRL 0x02B80560 +#define CS35L41_DSP1_IRQ13_CTRL 0x02B80568 +#define CS35L41_DSP1_IRQ14_CTRL 0x02B80570 +#define CS35L41_DSP1_IRQ15_CTRL 0x02B80578 +#define CS35L41_DSP1_IRQ16_CTRL 0x02B80580 +#define CS35L41_DSP1_IRQ17_CTRL 0x02B80588 +#define CS35L41_DSP1_IRQ18_CTRL 0x02B80590 +#define CS35L41_DSP1_IRQ19_CTRL 0x02B80598 +#define CS35L41_DSP1_IRQ20_CTRL 0x02B805A0 +#define CS35L41_DSP1_IRQ21_CTRL 0x02B805A8 +#define CS35L41_DSP1_IRQ22_CTRL 0x02B805B0 +#define CS35L41_DSP1_IRQ23_CTRL 0x02B805B8 +#define CS35L41_DSP1_SCRATCH1 0x02B805C0 +#define CS35L41_DSP1_SCRATCH2 0x02B805C8 +#define CS35L41_DSP1_SCRATCH3 0x02B805D0 +#define CS35L41_DSP1_SCRATCH4 0x02B805D8 +#define CS35L41_DSP1_CCM_CORE_CTRL 0x02BC1000 +#define CS35L41_DSP1_CCM_CLK_OVERRIDE 0x02BC1008 +#define CS35L41_DSP1_XM_MSTR_EN 0x02BC2000 +#define CS35L41_DSP1_XM_CORE_PRI 0x02BC2008 +#define CS35L41_DSP1_XM_AHB_PACK_PL_PRI 0x02BC2010 +#define CS35L41_DSP1_XM_AHB_UP_PL_PRI 0x02BC2018 +#define CS35L41_DSP1_XM_ACCEL_PL0_PRI 0x02BC2020 +#define CS35L41_DSP1_XM_NPL0_PRI 0x02BC2078 +#define CS35L41_DSP1_YM_MSTR_EN 0x02BC20C0 +#define CS35L41_DSP1_YM_CORE_PRI 0x02BC20C8 +#define CS35L41_DSP1_YM_AHB_PACK_PL_PRI 0x02BC20D0 +#define CS35L41_DSP1_YM_AHB_UP_PL_PRI 0x02BC20D8 +#define CS35L41_DSP1_YM_ACCEL_PL0_PRI 0x02BC20E0 +#define CS35L41_DSP1_YM_NPL0_PRI 0x02BC2138 +#define CS35L41_DSP1_PM_MSTR_EN 0x02BC2180 +#define CS35L41_DSP1_PM_PATCH0_ADDR 0x02BC2188 +#define CS35L41_DSP1_PM_PATCH0_EN 0x02BC218C +#define CS35L41_DSP1_PM_PATCH0_DATA_LO 0x02BC2190 +#define CS35L41_DSP1_PM_PATCH0_DATA_HI 0x02BC2194 +#define CS35L41_DSP1_PM_PATCH1_ADDR 0x02BC2198 +#define CS35L41_DSP1_PM_PATCH1_EN 0x02BC219C +#define CS35L41_DSP1_PM_PATCH1_DATA_LO 0x02BC21A0 +#define CS35L41_DSP1_PM_PATCH1_DATA_HI 0x02BC21A4 +#define CS35L41_DSP1_PM_PATCH2_ADDR 0x02BC21A8 +#define CS35L41_DSP1_PM_PATCH2_EN 0x02BC21AC +#define CS35L41_DSP1_PM_PATCH2_DATA_LO 0x02BC21B0 +#define CS35L41_DSP1_PM_PATCH2_DATA_HI 0x02BC21B4 +#define CS35L41_DSP1_PM_PATCH3_ADDR 0x02BC21B8 +#define CS35L41_DSP1_PM_PATCH3_EN 0x02BC21BC +#define CS35L41_DSP1_PM_PATCH3_DATA_LO 0x02BC21C0 +#define CS35L41_DSP1_PM_PATCH3_DATA_HI 0x02BC21C4 +#define CS35L41_DSP1_PM_PATCH4_ADDR 0x02BC21C8 +#define CS35L41_DSP1_PM_PATCH4_EN 0x02BC21CC +#define CS35L41_DSP1_PM_PATCH4_DATA_LO 0x02BC21D0 +#define CS35L41_DSP1_PM_PATCH4_DATA_HI 0x02BC21D4 +#define CS35L41_DSP1_PM_PATCH5_ADDR 0x02BC21D8 +#define CS35L41_DSP1_PM_PATCH5_EN 0x02BC21DC +#define CS35L41_DSP1_PM_PATCH5_DATA_LO 0x02BC21E0 +#define CS35L41_DSP1_PM_PATCH5_DATA_HI 0x02BC21E4 +#define CS35L41_DSP1_PM_PATCH6_ADDR 0x02BC21E8 +#define CS35L41_DSP1_PM_PATCH6_EN 0x02BC21EC +#define CS35L41_DSP1_PM_PATCH6_DATA_LO 0x02BC21F0 +#define CS35L41_DSP1_PM_PATCH6_DATA_HI 0x02BC21F4 +#define CS35L41_DSP1_PM_PATCH7_ADDR 0x02BC21F8 +#define CS35L41_DSP1_PM_PATCH7_EN 0x02BC21FC +#define CS35L41_DSP1_PM_PATCH7_DATA_LO 0x02BC2200 +#define CS35L41_DSP1_PM_PATCH7_DATA_HI 0x02BC2204 +#define CS35L41_DSP1_MPU_XM_ACCESS0 0x02BC3000 +#define CS35L41_DSP1_MPU_YM_ACCESS0 0x02BC3004 +#define CS35L41_DSP1_MPU_WNDW_ACCESS0 0x02BC3008 +#define CS35L41_DSP1_MPU_XREG_ACCESS0 0x02BC300C +#define CS35L41_DSP1_MPU_YREG_ACCESS0 0x02BC3014 +#define CS35L41_DSP1_MPU_XM_ACCESS1 0x02BC3018 +#define CS35L41_DSP1_MPU_YM_ACCESS1 0x02BC301C +#define CS35L41_DSP1_MPU_WNDW_ACCESS1 0x02BC3020 +#define CS35L41_DSP1_MPU_XREG_ACCESS1 0x02BC3024 +#define CS35L41_DSP1_MPU_YREG_ACCESS1 0x02BC302C +#define CS35L41_DSP1_MPU_XM_ACCESS2 0x02BC3030 +#define CS35L41_DSP1_MPU_YM_ACCESS2 0x02BC3034 +#define CS35L41_DSP1_MPU_WNDW_ACCESS2 0x02BC3038 +#define CS35L41_DSP1_MPU_XREG_ACCESS2 0x02BC303C +#define CS35L41_DSP1_MPU_YREG_ACCESS2 0x02BC3044 +#define CS35L41_DSP1_MPU_XM_ACCESS3 0x02BC3048 +#define CS35L41_DSP1_MPU_YM_ACCESS3 0x02BC304C +#define CS35L41_DSP1_MPU_WNDW_ACCESS3 0x02BC3050 +#define CS35L41_DSP1_MPU_XREG_ACCESS3 0x02BC3054 +#define CS35L41_DSP1_MPU_YREG_ACCESS3 0x02BC305C +#define CS35L41_DSP1_MPU_XM_VIO_ADDR 0x02BC3100 +#define CS35L41_DSP1_MPU_XM_VIO_STATUS 0x02BC3104 +#define CS35L41_DSP1_MPU_YM_VIO_ADDR 0x02BC3108 +#define CS35L41_DSP1_MPU_YM_VIO_STATUS 0x02BC310C +#define CS35L41_DSP1_MPU_PM_VIO_ADDR 0x02BC3110 +#define CS35L41_DSP1_MPU_PM_VIO_STATUS 0x02BC3114 +#define CS35L41_DSP1_MPU_LOCK_CONFIG 0x02BC3140 +#define CS35L41_DSP1_MPU_WDT_RST_CTRL 0x02BC3180 +#define CS35L41_DSP1_STRMARB_MSTR0_CFG0 0x02BC5000 +#define CS35L41_DSP1_STRMARB_MSTR0_CFG1 0x02BC5004 +#define CS35L41_DSP1_STRMARB_MSTR0_CFG2 0x02BC5008 +#define CS35L41_DSP1_STRMARB_MSTR1_CFG0 0x02BC5010 +#define CS35L41_DSP1_STRMARB_MSTR1_CFG1 0x02BC5014 +#define CS35L41_DSP1_STRMARB_MSTR1_CFG2 0x02BC5018 +#define CS35L41_DSP1_STRMARB_MSTR2_CFG0 0x02BC5020 +#define CS35L41_DSP1_STRMARB_MSTR2_CFG1 0x02BC5024 +#define CS35L41_DSP1_STRMARB_MSTR2_CFG2 0x02BC5028 +#define CS35L41_DSP1_STRMARB_MSTR3_CFG0 0x02BC5030 +#define CS35L41_DSP1_STRMARB_MSTR3_CFG1 0x02BC5034 +#define CS35L41_DSP1_STRMARB_MSTR3_CFG2 0x02BC5038 +#define CS35L41_DSP1_STRMARB_MSTR4_CFG0 0x02BC5040 +#define CS35L41_DSP1_STRMARB_MSTR4_CFG1 0x02BC5044 +#define CS35L41_DSP1_STRMARB_MSTR4_CFG2 0x02BC5048 +#define CS35L41_DSP1_STRMARB_MSTR5_CFG0 0x02BC5050 +#define CS35L41_DSP1_STRMARB_MSTR5_CFG1 0x02BC5054 +#define CS35L41_DSP1_STRMARB_MSTR5_CFG2 0x02BC5058 +#define CS35L41_DSP1_STRMARB_MSTR6_CFG0 0x02BC5060 +#define CS35L41_DSP1_STRMARB_MSTR6_CFG1 0x02BC5064 +#define CS35L41_DSP1_STRMARB_MSTR6_CFG2 0x02BC5068 +#define CS35L41_DSP1_STRMARB_MSTR7_CFG0 0x02BC5070 +#define CS35L41_DSP1_STRMARB_MSTR7_CFG1 0x02BC5074 +#define CS35L41_DSP1_STRMARB_MSTR7_CFG2 0x02BC5078 +#define CS35L41_DSP1_STRMARB_TX0_CFG0 0x02BC5200 +#define CS35L41_DSP1_STRMARB_TX0_CFG1 0x02BC5204 +#define CS35L41_DSP1_STRMARB_TX1_CFG0 0x02BC5208 +#define CS35L41_DSP1_STRMARB_TX1_CFG1 0x02BC520C +#define CS35L41_DSP1_STRMARB_TX2_CFG0 0x02BC5210 +#define CS35L41_DSP1_STRMARB_TX2_CFG1 0x02BC5214 +#define CS35L41_DSP1_STRMARB_TX3_CFG0 0x02BC5218 +#define CS35L41_DSP1_STRMARB_TX3_CFG1 0x02BC521C +#define CS35L41_DSP1_STRMARB_TX4_CFG0 0x02BC5220 +#define CS35L41_DSP1_STRMARB_TX4_CFG1 0x02BC5224 +#define CS35L41_DSP1_STRMARB_TX5_CFG0 0x02BC5228 +#define CS35L41_DSP1_STRMARB_TX5_CFG1 0x02BC522C +#define CS35L41_DSP1_STRMARB_TX6_CFG0 0x02BC5230 +#define CS35L41_DSP1_STRMARB_TX6_CFG1 0x02BC5234 +#define CS35L41_DSP1_STRMARB_TX7_CFG0 0x02BC5238 +#define CS35L41_DSP1_STRMARB_TX7_CFG1 0x02BC523C +#define CS35L41_DSP1_STRMARB_RX0_CFG0 0x02BC5400 +#define CS35L41_DSP1_STRMARB_RX0_CFG1 0x02BC5404 +#define CS35L41_DSP1_STRMARB_RX1_CFG0 0x02BC5408 +#define CS35L41_DSP1_STRMARB_RX1_CFG1 0x02BC540C +#define CS35L41_DSP1_STRMARB_RX2_CFG0 0x02BC5410 +#define CS35L41_DSP1_STRMARB_RX2_CFG1 0x02BC5414 +#define CS35L41_DSP1_STRMARB_RX3_CFG0 0x02BC5418 +#define CS35L41_DSP1_STRMARB_RX3_CFG1 0x02BC541C +#define CS35L41_DSP1_STRMARB_RX4_CFG0 0x02BC5420 +#define CS35L41_DSP1_STRMARB_RX4_CFG1 0x02BC5424 +#define CS35L41_DSP1_STRMARB_RX5_CFG0 0x02BC5428 +#define CS35L41_DSP1_STRMARB_RX5_CFG1 0x02BC542C +#define CS35L41_DSP1_STRMARB_RX6_CFG0 0x02BC5430 +#define CS35L41_DSP1_STRMARB_RX6_CFG1 0x02BC5434 +#define CS35L41_DSP1_STRMARB_RX7_CFG0 0x02BC5438 +#define CS35L41_DSP1_STRMARB_RX7_CFG1 0x02BC543C +#define CS35L41_DSP1_STRMARB_IRQ0_CFG0 0x02BC5600 +#define CS35L41_DSP1_STRMARB_IRQ0_CFG1 0x02BC5604 +#define CS35L41_DSP1_STRMARB_IRQ0_CFG2 0x02BC5608 +#define CS35L41_DSP1_STRMARB_IRQ1_CFG0 0x02BC5610 +#define CS35L41_DSP1_STRMARB_IRQ1_CFG1 0x02BC5614 +#define CS35L41_DSP1_STRMARB_IRQ1_CFG2 0x02BC5618 +#define CS35L41_DSP1_STRMARB_IRQ2_CFG0 0x02BC5620 +#define CS35L41_DSP1_STRMARB_IRQ2_CFG1 0x02BC5624 +#define CS35L41_DSP1_STRMARB_IRQ2_CFG2 0x02BC5628 +#define CS35L41_DSP1_STRMARB_IRQ3_CFG0 0x02BC5630 +#define CS35L41_DSP1_STRMARB_IRQ3_CFG1 0x02BC5634 +#define CS35L41_DSP1_STRMARB_IRQ3_CFG2 0x02BC5638 +#define CS35L41_DSP1_STRMARB_IRQ4_CFG0 0x02BC5640 +#define CS35L41_DSP1_STRMARB_IRQ4_CFG1 0x02BC5644 +#define CS35L41_DSP1_STRMARB_IRQ4_CFG2 0x02BC5648 +#define CS35L41_DSP1_STRMARB_IRQ5_CFG0 0x02BC5650 +#define CS35L41_DSP1_STRMARB_IRQ5_CFG1 0x02BC5654 +#define CS35L41_DSP1_STRMARB_IRQ5_CFG2 0x02BC5658 +#define CS35L41_DSP1_STRMARB_IRQ6_CFG0 0x02BC5660 +#define CS35L41_DSP1_STRMARB_IRQ6_CFG1 0x02BC5664 +#define CS35L41_DSP1_STRMARB_IRQ6_CFG2 0x02BC5668 +#define CS35L41_DSP1_STRMARB_IRQ7_CFG0 0x02BC5670 +#define CS35L41_DSP1_STRMARB_IRQ7_CFG1 0x02BC5674 +#define CS35L41_DSP1_STRMARB_IRQ7_CFG2 0x02BC5678 +#define CS35L41_DSP1_STRMARB_RESYNC_MSK 0x02BC5A00 +#define CS35L41_DSP1_STRMARB_ERR_STATUS 0x02BC5A08 +#define CS35L41_DSP1_INTPCTL_RES_STATIC 0x02BC6000 +#define CS35L41_DSP1_INTPCTL_RES_DYN 0x02BC6004 +#define CS35L41_DSP1_INTPCTL_NMI_CTRL 0x02BC6008 +#define CS35L41_DSP1_INTPCTL_IRQ_INV 0x02BC6010 +#define CS35L41_DSP1_INTPCTL_IRQ_MODE 0x02BC6014 +#define CS35L41_DSP1_INTPCTL_IRQ_EN 0x02BC6018 +#define CS35L41_DSP1_INTPCTL_IRQ_MSK 0x02BC601C +#define CS35L41_DSP1_INTPCTL_IRQ_FLUSH 0x02BC6020 +#define CS35L41_DSP1_INTPCTL_IRQ_MSKCLR 0x02BC6024 +#define CS35L41_DSP1_INTPCTL_IRQ_FRC 0x02BC6028 +#define CS35L41_DSP1_INTPCTL_IRQ_MSKSET 0x02BC602C +#define CS35L41_DSP1_INTPCTL_IRQ_ERR 0x02BC6030 +#define CS35L41_DSP1_INTPCTL_IRQ_PEND 0x02BC6034 +#define CS35L41_DSP1_INTPCTL_IRQ_GEN 0x02BC6038 +#define CS35L41_DSP1_INTPCTL_TESTBITS 0x02BC6040 +#define CS35L41_DSP1_WDT_CONTROL 0x02BC7000 +#define CS35L41_DSP1_WDT_STATUS 0x02BC7008 +#define CS35L41_DSP1_YMEM_PACK_0 0x02C00000 +#define CS35L41_DSP1_YMEM_PACK_1532 0x02C017F0 +#define CS35L41_DSP1_YMEM_UNPACK32_0 0x03000000 +#define CS35L41_DSP1_YMEM_UNPACK32_1022 0x03000FF8 +#define CS35L41_DSP1_YMEM_UNPACK24_0 0x03400000 +#define CS35L41_DSP1_YMEM_UNPACK24_2045 0x03401FF4 +#define CS35L41_DSP1_PMEM_0 0x03800000 +#define CS35L41_DSP1_PMEM_5114 0x03804FE8 + +/*test regs for emulation bringup*/ +#define CS35L41_PLL_OVR 0x00003018 +#define CS35L41_BST_TEST_DUTY 0x00003900 +#define CS35L41_DIGPWM_IOCTRL 0x0000706C + +/*registers populated by OTP*/ +#define CS35L41_OTP_TRIM_1 0x0000208c +#define CS35L41_OTP_TRIM_2 0x00002090 +#define CS35L41_OTP_TRIM_3 0x00003010 +#define CS35L41_OTP_TRIM_4 0x0000300C +#define CS35L41_OTP_TRIM_5 0x0000394C +#define CS35L41_OTP_TRIM_6 0x00003950 +#define CS35L41_OTP_TRIM_7 0x00003954 +#define CS35L41_OTP_TRIM_8 0x00003958 +#define CS35L41_OTP_TRIM_9 0x0000395C +#define CS35L41_OTP_TRIM_10 0x0000416C +#define CS35L41_OTP_TRIM_11 0x00004160 +#define CS35L41_OTP_TRIM_12 0x00004170 +#define CS35L41_OTP_TRIM_13 0x00004360 +#define CS35L41_OTP_TRIM_14 0x00004448 +#define CS35L41_OTP_TRIM_15 0x0000444C +#define CS35L41_OTP_TRIM_16 0x00006E30 +#define CS35L41_OTP_TRIM_17 0x00006E34 +#define CS35L41_OTP_TRIM_18 0x00006E38 +#define CS35L41_OTP_TRIM_19 0x00006E3C +#define CS35L41_OTP_TRIM_20 0x00006E40 +#define CS35L41_OTP_TRIM_21 0x00006E44 +#define CS35L41_OTP_TRIM_22 0x00006E48 +#define CS35L41_OTP_TRIM_23 0x00006E4C +#define CS35L41_OTP_TRIM_24 0x00006E50 +#define CS35L41_OTP_TRIM_25 0x00006E54 +#define CS35L41_OTP_TRIM_26 0x00006E58 +#define CS35L41_OTP_TRIM_27 0x00006E5C +#define CS35L41_OTP_TRIM_28 0x00006E60 +#define CS35L41_OTP_TRIM_29 0x00006E64 +#define CS35L41_OTP_TRIM_30 0x00007418 +#define CS35L41_OTP_TRIM_31 0x0000741C +#define CS35L41_OTP_TRIM_32 0x00007434 +#define CS35L41_OTP_TRIM_33 0x00007068 +#define CS35L41_OTP_TRIM_34 0x0000410C +#define CS35L41_OTP_TRIM_35 0x0000400C +#define CS35L41_OTP_TRIM_36 0x00002030 + +#define CS35L41_MAX_CACHE_REG 36 +#define CS35L41_OTP_SIZE_WORDS 32 +#define CS35L41_NUM_OTP_ELEM 100 +#define CS35L41_NUM_OTP_MAPS 5 + +#define CS35L41_VALID_PDATA 0x80000000 +#define CS35L41_NUM_SUPPLIES 2 + +#define CS35L41_SCLK_MSTR_MASK 0x10 +#define CS35L41_SCLK_MSTR_SHIFT 4 +#define CS35L41_LRCLK_MSTR_MASK 0x01 +#define CS35L41_LRCLK_MSTR_SHIFT 0 +#define CS35L41_SCLK_INV_MASK 0x40 +#define CS35L41_SCLK_INV_SHIFT 6 +#define CS35L41_LRCLK_INV_MASK 0x04 +#define CS35L41_LRCLK_INV_SHIFT 2 +#define CS35L41_SCLK_FRC_MASK 0x20 +#define CS35L41_SCLK_FRC_SHIFT 5 +#define CS35L41_LRCLK_FRC_MASK 0x02 +#define CS35L41_LRCLK_FRC_SHIFT 1 + +#define CS35L41_AMP_GAIN_PCM_MASK 0x3E0 +#define CS35L41_AMP_GAIN_ZC_MASK 0x0400 +#define CS35L41_AMP_GAIN_ZC_SHIFT 10 + +#define CS35L41_BST_CTL_MASK 0xFF +#define CS35L41_BST_CTL_SEL_MASK 0x03 +#define CS35L41_BST_CTL_SEL_REG 0x00 +#define CS35L41_BST_CTL_SEL_CLASSH 0x01 +#define CS35L41_BST_IPK_MASK 0x7F +#define CS35L41_BST_IPK_SHIFT 0 +#define CS35L41_BST_LIM_MASK 0x4 +#define CS35L41_BST_LIM_SHIFT 2 +#define CS35L41_BST_K1_MASK 0x000000FF +#define CS35L41_BST_K1_SHIFT 0 +#define CS35L41_BST_K2_MASK 0x0000FF00 +#define CS35L41_BST_K2_SHIFT 8 +#define CS35L41_BST_SLOPE_MASK 0x0000FF00 +#define CS35L41_BST_SLOPE_SHIFT 8 +#define CS35L41_BST_LBST_VAL_MASK 0x00000003 +#define CS35L41_BST_LBST_VAL_SHIFT 0 + +#define CS35L41_TEMP_THLD_MASK 0x03 +#define CS35L41_VMON_IMON_VOL_MASK 0x07FF07FF +#define CS35L41_PDM_MODE_MASK 0x01 +#define CS35L41_PDM_MODE_SHIFT 0 + +#define CS35L41_CH_MEM_DEPTH_MASK 0x07 +#define CS35L41_CH_MEM_DEPTH_SHIFT 0 +#define CS35L41_CH_HDRM_CTL_MASK 0x007F0000 +#define CS35L41_CH_HDRM_CTL_SHIFT 16 +#define CS35L41_CH_REL_RATE_MASK 0xFF00 +#define CS35L41_CH_REL_RATE_SHIFT 8 +#define CS35L41_CH_WKFET_DLY_MASK 0x001C +#define CS35L41_CH_WKFET_DLY_SHIFT 2 +#define CS35L41_CH_WKFET_THLD_MASK 0x0F00 +#define CS35L41_CH_WKFET_THLD_SHIFT 8 + +#define CS35L41_HW_NG_SEL_MASK 0x3F00 +#define CS35L41_HW_NG_SEL_SHIFT 8 +#define CS35L41_HW_NG_DLY_MASK 0x0070 +#define CS35L41_HW_NG_DLY_SHIFT 4 +#define CS35L41_HW_NG_THLD_MASK 0x0007 +#define CS35L41_HW_NG_THLD_SHIFT 0 + +#define CS35L41_DSP_NG_ENABLE_MASK 0x00010000 +#define CS35L41_DSP_NG_ENABLE_SHIFT 16 +#define CS35L41_DSP_NG_THLD_MASK 0x7 +#define CS35L41_DSP_NG_THLD_SHIFT 0 +#define CS35L41_DSP_NG_DELAY_MASK 0x0F00 +#define CS35L41_DSP_NG_DELAY_SHIFT 8 + +#define CS35L41_ASP_FMT_MASK 0x0700 +#define CS35L41_ASP_FMT_SHIFT 8 +#define CS35L41_ASP_DOUT_HIZ_MASK 0x03 +#define CS35L41_ASP_DOUT_HIZ_SHIFT 0 +#define CS35L41_ASP_WIDTH_16 0x10 +#define CS35L41_ASP_WIDTH_24 0x18 +#define CS35L41_ASP_WIDTH_32 0x20 +#define CS35L41_ASP_WIDTH_TX_MASK 0xFF0000 +#define CS35L41_ASP_WIDTH_TX_SHIFT 16 +#define CS35L41_ASP_WIDTH_RX_MASK 0xFF000000 +#define CS35L41_ASP_WIDTH_RX_SHIFT 24 +#define CS35L41_ASP_RX1_SLOT_MASK 0x3F +#define CS35L41_ASP_RX1_SLOT_SHIFT 0 +#define CS35L41_ASP_RX2_SLOT_MASK 0x3F00 +#define CS35L41_ASP_RX2_SLOT_SHIFT 8 +#define CS35L41_ASP_RX_WL_MASK 0x3F +#define CS35L41_ASP_TX_WL_MASK 0x3F +#define CS35L41_ASP_RX_WL_SHIFT 0 +#define CS35L41_ASP_TX_WL_SHIFT 0 +#define CS35L41_ASP_SOURCE_MASK 0x7F + +#define CS35L41_INPUT_SRC_ASPRX1 0x08 +#define CS35L41_INPUT_SRC_ASPRX2 0x09 +#define CS35L41_INPUT_SRC_VMON 0x18 +#define CS35L41_INPUT_SRC_IMON 0x19 +#define CS35L41_INPUT_SRC_CLASSH 0x21 +#define CS35L41_INPUT_SRC_VPMON 0x28 +#define CS35L41_INPUT_SRC_VBSTMON 0x29 +#define CS35L41_INPUT_SRC_TEMPMON 0x3A +#define CS35L41_INPUT_SRC_RSVD 0x3B +#define CS35L41_INPUT_DSP_TX1 0x32 +#define CS35L41_INPUT_DSP_TX2 0x33 + +#define CS35L41_PLL_CLK_SEL_MASK 0x07 +#define CS35L41_PLL_CLK_SEL_SHIFT 0 +#define CS35L41_PLL_CLK_EN_MASK 0x10 +#define CS35L41_PLL_CLK_EN_SHIFT 4 +#define CS35L41_PLL_OPENLOOP_MASK 0x0800 +#define CS35L41_PLL_OPENLOOP_SHIFT 11 +#define CS35L41_PLLSRC_SCLK 0 +#define CS35L41_PLLSRC_LRCLK 1 +#define CS35L41_PLLSRC_SELF 3 +#define CS35L41_PLLSRC_PDMCLK 4 +#define CS35L41_PLLSRC_MCLK 5 +#define CS35L41_PLLSRC_SWIRE 7 +#define CS35L41_REFCLK_FREQ_MASK 0x7E0 +#define CS35L41_REFCLK_FREQ_SHIFT 5 + +#define CS35L41_GLOBAL_FS_MASK 0x1F +#define CS35L41_GLOBAL_FS_SHIFT 0 + +#define CS35L41_GLOBAL_EN_MASK 0x01 +#define CS35L41_GLOBAL_EN_SHIFT 0 +#define CS35L41_BST_EN_MASK 0x0030 +#define CS35L41_BST_EN_SHIFT 4 +#define CS35L41_BST_EN_DEFAULT 0x2 +#define CS35L41_AMP_EN_SHIFT 0 +#define CS35L41_AMP_EN_MASK 1 + +#define CS35L41_PDN_DONE_MASK 0x00800000 +#define CS35L41_PDN_DONE_SHIFT 23 +#define CS35L41_PUP_DONE_MASK 0x01000000 +#define CS35L41_PUP_DONE_SHIFT 24 + +#define CS35L36_PUP_DONE_IRQ_UNMASK 0x5F +#define CS35L36_PUP_DONE_IRQ_MASK 0xBF + +#define CS35L41_AMP_SHORT_ERR 0x80000000 +#define CS35L41_BST_SHORT_ERR 0x0100 +#define CS35L41_TEMP_WARN 0x8000 +#define CS35L41_TEMP_ERR 0x00020000 +#define CS35L41_BST_OVP_ERR 0x40 +#define CS35L41_BST_DCM_UVP_ERR 0x80 +#define CS35L41_OTP_BOOT_DONE 0x02 +#define CS35L41_PLL_UNLOCK 0x10 +#define CS35L41_OTP_BOOT_ERR 0x80000000 + +#define CS35L41_AMP_SHORT_ERR_RLS 0x02 +#define CS35L41_BST_SHORT_ERR_RLS 0x04 +#define CS35L41_BST_OVP_ERR_RLS 0x08 +#define CS35L41_BST_UVP_ERR_RLS 0x10 +#define CS35L41_TEMP_WARN_ERR_RLS 0x20 +#define CS35L41_TEMP_ERR_RLS 0x40 + +#define CS35L41_INT1_MASK_DEFAULT 0x7FFCFE3F +#define CS35L41_INT1_UNMASK_PUP 0xFEFFFFFF +#define CS35L41_INT1_UNMASK_PDN 0xFF7FFFFF + +#define CS35L41_GPIO_DIR_MASK 0x80000000 +#define CS35L41_GPIO_DIR_SHIFT 31 +#define CS35L41_GPIO1_CTRL_MASK 0x00030000 +#define CS35L41_GPIO1_CTRL_SHIFT 16 +#define CS35L41_GPIO2_CTRL_MASK 0x07000000 +#define CS35L41_GPIO2_CTRL_SHIFT 24 +#define CS35L41_GPIO_CTRL_OPEN_INT 2 +#define CS35L41_GPIO_CTRL_ACTV_LO 4 +#define CS35L41_GPIO_CTRL_ACTV_HI 5 +#define CS35L41_GPIO_POL_MASK 0x1000 +#define CS35L41_GPIO_POL_SHIFT 12 + +#define CS35L41_AMP_INV_PCM_SHIFT 14 +#define CS35L41_AMP_INV_PCM_MASK BIT(CS35L41_AMP_INV_PCM_SHIFT) +#define CS35L41_AMP_PCM_VOL_SHIFT 3 +#define CS35L41_AMP_PCM_VOL_MASK (0x7FF << 3) +#define CS35L41_AMP_PCM_VOL_MUTE 0x4CF + +#define CS35L41_CHIP_ID 0x35a40 +#define CS35L41R_CHIP_ID 0x35b40 +#define CS35L41_MTLREVID_MASK 0x0F +#define CS35L41_REVID_A0 0xA0 +#define CS35L41_REVID_B0 0xB0 +#define CS35L41_REVID_B2 0xB2 + +#define CS35L41_HALO_CORE_RESET 0x00000200 + +#define CS35L41_FS1_WINDOW_MASK 0x000007FF +#define CS35L41_FS2_WINDOW_MASK 0x00FFF800 +#define CS35L41_FS2_WINDOW_SHIFT 12 + +#define CS35L41_SPI_MAX_FREQ 4000000 +#define CS35L41_REGSTRIDE 4 + enum cs35l41_clk_ids { CS35L41_CLKID_SCLK = 0, CS35L41_CLKID_LRCLK = 1, @@ -31,4 +746,22 @@ struct cs35l41_platform_data { struct cs35l41_irq_cfg irq_config2; }; +struct cs35l41_otp_packed_element_t { + u32 reg; + u8 shift; + u8 size; +}; + +struct cs35l41_otp_map_element_t { + u32 id; + u32 num_elements; + const struct cs35l41_otp_packed_element_t *map; + u32 bit_offset; + u32 word_offset; +}; + +extern const struct cs35l41_otp_map_element_t cs35l41_otp_map_map[CS35L41_NUM_OTP_MAPS]; +extern struct regmap_config cs35l41_regmap_i2c; +extern struct regmap_config cs35l41_regmap_spi; + #endif /* __CS35L41_H */ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 5fe9ec924864..d3e5ae8310ef 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -624,21 +624,24 @@ config SND_SOC_CS35L36 tristate "Cirrus Logic CS35L36 CODEC" depends on I2C +config SND_SOC_CS35L41_LIB + tristate + config SND_SOC_CS35L41 tristate - default y if SND_SOC_CS35L41_SPI=y - default y if SND_SOC_CS35L41_I2C=y - default m if SND_SOC_CS35L41_SPI=m - default m if SND_SOC_CS35L41_I2C=m config SND_SOC_CS35L41_SPI tristate "Cirrus Logic CS35L41 CODEC (SPI)" depends on SPI_MASTER + select SND_SOC_CS35L41_LIB + select SND_SOC_CS35L41 select REGMAP_SPI config SND_SOC_CS35L41_I2C tristate "Cirrus Logic CS35L41 CODEC (I2C)" depends on I2C + select SND_SOC_CS35L41_LIB + select SND_SOC_CS35L41 select REGMAP_I2C config SND_SOC_CS42L42 diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 8dbdf3518bda..ac7f20972470 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -55,7 +55,8 @@ snd-soc-cs35l33-objs := cs35l33.o snd-soc-cs35l34-objs := cs35l34.o snd-soc-cs35l35-objs := cs35l35.o snd-soc-cs35l36-objs := cs35l36.o -snd-soc-cs35l41-objs := cs35l41.o cs35l41-tables.o +snd-soc-cs35l41-lib-objs := cs35l41-lib.o +snd-soc-cs35l41-objs := cs35l41.o snd-soc-cs35l41-spi-objs := cs35l41-spi.o snd-soc-cs35l41-i2c-objs := cs35l41-i2c.o snd-soc-cs42l42-objs := cs42l42.o @@ -396,6 +397,7 @@ obj-$(CONFIG_SND_SOC_CS35L34) += snd-soc-cs35l34.o obj-$(CONFIG_SND_SOC_CS35L35) += snd-soc-cs35l35.o obj-$(CONFIG_SND_SOC_CS35L36) += snd-soc-cs35l36.o obj-$(CONFIG_SND_SOC_CS35L41) += snd-soc-cs35l41.o +obj-$(CONFIG_SND_SOC_CS35L41_LIB) += snd-soc-cs35l41-lib.o obj-$(CONFIG_SND_SOC_CS35L41_SPI) += snd-soc-cs35l41-spi.o obj-$(CONFIG_SND_SOC_CS35L41_I2C) += snd-soc-cs35l41-i2c.o obj-$(CONFIG_SND_SOC_CS42L42) += snd-soc-cs42l42.o diff --git a/sound/soc/codecs/cs35l41-i2c.c b/sound/soc/codecs/cs35l41-i2c.c index c9b604af6b71..de5c8612f030 100644 --- a/sound/soc/codecs/cs35l41-i2c.c +++ b/sound/soc/codecs/cs35l41-i2c.c @@ -17,7 +17,6 @@ #include #include -#include #include "cs35l41.h" static const struct i2c_device_id cs35l41_id_i2c[] = { diff --git a/sound/soc/codecs/cs35l41-lib.c b/sound/soc/codecs/cs35l41-lib.c new file mode 100644 index 000000000000..f19531ebf729 --- /dev/null +++ b/sound/soc/codecs/cs35l41-lib.c @@ -0,0 +1,733 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// cs35l41-lib.c -- CS35L41 Common functions for HDA and ASoC Audio drivers +// +// Copyright 2017-2021 Cirrus Logic, Inc. +// +// Author: David Rhodes +// Author: Lucas Tanure + +#include +#include + +#include + +static const struct reg_default cs35l41_reg[] = { + { CS35L41_PWR_CTRL1, 0x00000000 }, + { CS35L41_PWR_CTRL3, 0x01000010 }, + { CS35L41_GPIO_PAD_CONTROL, 0x00000000 }, + { CS35L41_SP_ENABLES, 0x00000000 }, + { CS35L41_SP_RATE_CTRL, 0x00000028 }, + { CS35L41_SP_FORMAT, 0x18180200 }, + { CS35L41_SP_HIZ_CTRL, 0x00000002 }, + { CS35L41_SP_FRAME_TX_SLOT, 0x03020100 }, + { CS35L41_SP_FRAME_RX_SLOT, 0x00000100 }, + { CS35L41_SP_TX_WL, 0x00000018 }, + { CS35L41_SP_RX_WL, 0x00000018 }, + { CS35L41_DAC_PCM1_SRC, 0x00000008 }, + { CS35L41_ASP_TX1_SRC, 0x00000018 }, + { CS35L41_ASP_TX2_SRC, 0x00000019 }, + { CS35L41_ASP_TX3_SRC, 0x00000020 }, + { CS35L41_ASP_TX4_SRC, 0x00000021 }, + { CS35L41_DSP1_RX1_SRC, 0x00000008 }, + { CS35L41_DSP1_RX2_SRC, 0x00000009 }, + { CS35L41_DSP1_RX3_SRC, 0x00000018 }, + { CS35L41_DSP1_RX4_SRC, 0x00000019 }, + { CS35L41_DSP1_RX5_SRC, 0x00000020 }, + { CS35L41_DSP1_RX6_SRC, 0x00000021 }, + { CS35L41_DSP1_RX7_SRC, 0x0000003A }, + { CS35L41_DSP1_RX8_SRC, 0x00000001 }, + { CS35L41_NGATE1_SRC, 0x00000008 }, + { CS35L41_NGATE2_SRC, 0x00000009 }, + { CS35L41_AMP_DIG_VOL_CTRL, 0x00008000 }, + { CS35L41_CLASSH_CFG, 0x000B0405 }, + { CS35L41_WKFET_CFG, 0x00000111 }, + { CS35L41_NG_CFG, 0x00000033 }, + { CS35L41_AMP_GAIN_CTRL, 0x00000273 }, + { CS35L41_GPIO1_CTRL1, 0xE1000001 }, + { CS35L41_GPIO2_CTRL1, 0xE1000001 }, + { CS35L41_MIXER_NGATE_CFG, 0x00000000 }, + { CS35L41_MIXER_NGATE_CH1_CFG, 0x00000303 }, + { CS35L41_MIXER_NGATE_CH2_CFG, 0x00000303 }, +}; + +static bool cs35l41_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L41_DEVID: + case CS35L41_REVID: + case CS35L41_FABID: + case CS35L41_RELID: + case CS35L41_OTPID: + case CS35L41_TEST_KEY_CTL: + case CS35L41_USER_KEY_CTL: + case CS35L41_OTP_CTRL0: + case CS35L41_OTP_CTRL3: + case CS35L41_OTP_CTRL4: + case CS35L41_OTP_CTRL5: + case CS35L41_OTP_CTRL6: + case CS35L41_OTP_CTRL7: + case CS35L41_OTP_CTRL8: + case CS35L41_PWR_CTRL1: + case CS35L41_PWR_CTRL2: + case CS35L41_PWR_CTRL3: + case CS35L41_CTRL_OVRRIDE: + case CS35L41_AMP_OUT_MUTE: + case CS35L41_PROTECT_REL_ERR_IGN: + case CS35L41_GPIO_PAD_CONTROL: + case CS35L41_JTAG_CONTROL: + case CS35L41_PLL_CLK_CTRL: + case CS35L41_DSP_CLK_CTRL: + case CS35L41_GLOBAL_CLK_CTRL: + case CS35L41_DATA_FS_SEL: + case CS35L41_MDSYNC_EN: + case CS35L41_MDSYNC_TX_ID: + case CS35L41_MDSYNC_PWR_CTRL: + case CS35L41_MDSYNC_DATA_TX: + case CS35L41_MDSYNC_TX_STATUS: + case CS35L41_MDSYNC_DATA_RX: + case CS35L41_MDSYNC_RX_STATUS: + case CS35L41_MDSYNC_ERR_STATUS: + case CS35L41_MDSYNC_SYNC_PTE2: + case CS35L41_MDSYNC_SYNC_PTE3: + case CS35L41_MDSYNC_SYNC_MSM_STATUS: + case CS35L41_BSTCVRT_VCTRL1: + case CS35L41_BSTCVRT_VCTRL2: + case CS35L41_BSTCVRT_PEAK_CUR: + case CS35L41_BSTCVRT_SFT_RAMP: + case CS35L41_BSTCVRT_COEFF: + case CS35L41_BSTCVRT_SLOPE_LBST: + case CS35L41_BSTCVRT_SW_FREQ: + case CS35L41_BSTCVRT_DCM_CTRL: + case CS35L41_BSTCVRT_DCM_MODE_FORCE: + case CS35L41_BSTCVRT_OVERVOLT_CTRL: + case CS35L41_VI_VOL_POL: + case CS35L41_DTEMP_WARN_THLD: + case CS35L41_DTEMP_CFG: + case CS35L41_DTEMP_EN: + case CS35L41_VPVBST_FS_SEL: + case CS35L41_SP_ENABLES: + case CS35L41_SP_RATE_CTRL: + case CS35L41_SP_FORMAT: + case CS35L41_SP_HIZ_CTRL: + case CS35L41_SP_FRAME_TX_SLOT: + case CS35L41_SP_FRAME_RX_SLOT: + case CS35L41_SP_TX_WL: + case CS35L41_SP_RX_WL: + case CS35L41_DAC_PCM1_SRC: + case CS35L41_ASP_TX1_SRC: + case CS35L41_ASP_TX2_SRC: + case CS35L41_ASP_TX3_SRC: + case CS35L41_ASP_TX4_SRC: + case CS35L41_DSP1_RX1_SRC: + case CS35L41_DSP1_RX2_SRC: + case CS35L41_DSP1_RX3_SRC: + case CS35L41_DSP1_RX4_SRC: + case CS35L41_DSP1_RX5_SRC: + case CS35L41_DSP1_RX6_SRC: + case CS35L41_DSP1_RX7_SRC: + case CS35L41_DSP1_RX8_SRC: + case CS35L41_NGATE1_SRC: + case CS35L41_NGATE2_SRC: + case CS35L41_AMP_DIG_VOL_CTRL: + case CS35L41_VPBR_CFG: + case CS35L41_VBBR_CFG: + case CS35L41_VPBR_STATUS: + case CS35L41_VBBR_STATUS: + case CS35L41_OVERTEMP_CFG: + case CS35L41_AMP_ERR_VOL: + case CS35L41_VOL_STATUS_TO_DSP: + case CS35L41_CLASSH_CFG: + case CS35L41_WKFET_CFG: + case CS35L41_NG_CFG: + case CS35L41_AMP_GAIN_CTRL: + case CS35L41_DAC_MSM_CFG: + case CS35L41_IRQ1_CFG: + case CS35L41_IRQ1_STATUS: + case CS35L41_IRQ1_STATUS1: + case CS35L41_IRQ1_STATUS2: + case CS35L41_IRQ1_STATUS3: + case CS35L41_IRQ1_STATUS4: + case CS35L41_IRQ1_RAW_STATUS1: + case CS35L41_IRQ1_RAW_STATUS2: + case CS35L41_IRQ1_RAW_STATUS3: + case CS35L41_IRQ1_RAW_STATUS4: + case CS35L41_IRQ1_MASK1: + case CS35L41_IRQ1_MASK2: + case CS35L41_IRQ1_MASK3: + case CS35L41_IRQ1_MASK4: + case CS35L41_IRQ1_FRC1: + case CS35L41_IRQ1_FRC2: + case CS35L41_IRQ1_FRC3: + case CS35L41_IRQ1_FRC4: + case CS35L41_IRQ1_EDGE1: + case CS35L41_IRQ1_EDGE4: + case CS35L41_IRQ1_POL1: + case CS35L41_IRQ1_POL2: + case CS35L41_IRQ1_POL3: + case CS35L41_IRQ1_POL4: + case CS35L41_IRQ1_DB3: + case CS35L41_IRQ2_CFG: + case CS35L41_IRQ2_STATUS: + case CS35L41_IRQ2_STATUS1: + case CS35L41_IRQ2_STATUS2: + case CS35L41_IRQ2_STATUS3: + case CS35L41_IRQ2_STATUS4: + case CS35L41_IRQ2_RAW_STATUS1: + case CS35L41_IRQ2_RAW_STATUS2: + case CS35L41_IRQ2_RAW_STATUS3: + case CS35L41_IRQ2_RAW_STATUS4: + case CS35L41_IRQ2_MASK1: + case CS35L41_IRQ2_MASK2: + case CS35L41_IRQ2_MASK3: + case CS35L41_IRQ2_MASK4: + case CS35L41_IRQ2_FRC1: + case CS35L41_IRQ2_FRC2: + case CS35L41_IRQ2_FRC3: + case CS35L41_IRQ2_FRC4: + case CS35L41_IRQ2_EDGE1: + case CS35L41_IRQ2_EDGE4: + case CS35L41_IRQ2_POL1: + case CS35L41_IRQ2_POL2: + case CS35L41_IRQ2_POL3: + case CS35L41_IRQ2_POL4: + case CS35L41_IRQ2_DB3: + case CS35L41_GPIO_STATUS1: + case CS35L41_GPIO1_CTRL1: + case CS35L41_GPIO2_CTRL1: + case CS35L41_MIXER_NGATE_CFG: + case CS35L41_MIXER_NGATE_CH1_CFG: + case CS35L41_MIXER_NGATE_CH2_CFG: + case CS35L41_DSP_MBOX_1 ... CS35L41_DSP_VIRT2_MBOX_8: + case CS35L41_CLOCK_DETECT_1: + case CS35L41_DIE_STS1: + case CS35L41_DIE_STS2: + case CS35L41_TEMP_CAL1: + case CS35L41_TEMP_CAL2: + case CS35L41_DSP1_TIMESTAMP_COUNT: + case CS35L41_DSP1_SYS_ID: + case CS35L41_DSP1_SYS_VERSION: + case CS35L41_DSP1_SYS_CORE_ID: + case CS35L41_DSP1_SYS_AHB_ADDR: + case CS35L41_DSP1_SYS_XSRAM_SIZE: + case CS35L41_DSP1_SYS_YSRAM_SIZE: + case CS35L41_DSP1_SYS_PSRAM_SIZE: + case CS35L41_DSP1_SYS_PM_BOOT_SIZE: + case CS35L41_DSP1_SYS_FEATURES: + case CS35L41_DSP1_SYS_FIR_FILTERS: + case CS35L41_DSP1_SYS_LMS_FILTERS: + case CS35L41_DSP1_SYS_XM_BANK_SIZE: + case CS35L41_DSP1_SYS_YM_BANK_SIZE: + case CS35L41_DSP1_SYS_PM_BANK_SIZE: + case CS35L41_DSP1_RX1_RATE: + case CS35L41_DSP1_RX2_RATE: + case CS35L41_DSP1_RX3_RATE: + case CS35L41_DSP1_RX4_RATE: + case CS35L41_DSP1_RX5_RATE: + case CS35L41_DSP1_RX6_RATE: + case CS35L41_DSP1_RX7_RATE: + case CS35L41_DSP1_RX8_RATE: + case CS35L41_DSP1_TX1_RATE: + case CS35L41_DSP1_TX2_RATE: + case CS35L41_DSP1_TX3_RATE: + case CS35L41_DSP1_TX4_RATE: + case CS35L41_DSP1_TX5_RATE: + case CS35L41_DSP1_TX6_RATE: + case CS35L41_DSP1_TX7_RATE: + case CS35L41_DSP1_TX8_RATE: + case CS35L41_DSP1_SCRATCH1: + case CS35L41_DSP1_SCRATCH2: + case CS35L41_DSP1_SCRATCH3: + case CS35L41_DSP1_SCRATCH4: + case CS35L41_DSP1_CCM_CORE_CTRL: + case CS35L41_DSP1_CCM_CLK_OVERRIDE: + case CS35L41_DSP1_XM_MSTR_EN: + case CS35L41_DSP1_XM_CORE_PRI: + case CS35L41_DSP1_XM_AHB_PACK_PL_PRI: + case CS35L41_DSP1_XM_AHB_UP_PL_PRI: + case CS35L41_DSP1_XM_ACCEL_PL0_PRI: + case CS35L41_DSP1_XM_NPL0_PRI: + case CS35L41_DSP1_YM_MSTR_EN: + case CS35L41_DSP1_YM_CORE_PRI: + case CS35L41_DSP1_YM_AHB_PACK_PL_PRI: + case CS35L41_DSP1_YM_AHB_UP_PL_PRI: + case CS35L41_DSP1_YM_ACCEL_PL0_PRI: + case CS35L41_DSP1_YM_NPL0_PRI: + case CS35L41_DSP1_MPU_XM_ACCESS0: + case CS35L41_DSP1_MPU_YM_ACCESS0: + case CS35L41_DSP1_MPU_WNDW_ACCESS0: + case CS35L41_DSP1_MPU_XREG_ACCESS0: + case CS35L41_DSP1_MPU_YREG_ACCESS0: + case CS35L41_DSP1_MPU_XM_ACCESS1: + case CS35L41_DSP1_MPU_YM_ACCESS1: + case CS35L41_DSP1_MPU_WNDW_ACCESS1: + case CS35L41_DSP1_MPU_XREG_ACCESS1: + case CS35L41_DSP1_MPU_YREG_ACCESS1: + case CS35L41_DSP1_MPU_XM_ACCESS2: + case CS35L41_DSP1_MPU_YM_ACCESS2: + case CS35L41_DSP1_MPU_WNDW_ACCESS2: + case CS35L41_DSP1_MPU_XREG_ACCESS2: + case CS35L41_DSP1_MPU_YREG_ACCESS2: + case CS35L41_DSP1_MPU_XM_ACCESS3: + case CS35L41_DSP1_MPU_YM_ACCESS3: + case CS35L41_DSP1_MPU_WNDW_ACCESS3: + case CS35L41_DSP1_MPU_XREG_ACCESS3: + case CS35L41_DSP1_MPU_YREG_ACCESS3: + case CS35L41_DSP1_MPU_XM_VIO_ADDR: + case CS35L41_DSP1_MPU_XM_VIO_STATUS: + case CS35L41_DSP1_MPU_YM_VIO_ADDR: + case CS35L41_DSP1_MPU_YM_VIO_STATUS: + case CS35L41_DSP1_MPU_PM_VIO_ADDR: + case CS35L41_DSP1_MPU_PM_VIO_STATUS: + case CS35L41_DSP1_MPU_LOCK_CONFIG: + case CS35L41_DSP1_MPU_WDT_RST_CTRL: + case CS35L41_OTP_TRIM_1: + case CS35L41_OTP_TRIM_2: + case CS35L41_OTP_TRIM_3: + case CS35L41_OTP_TRIM_4: + case CS35L41_OTP_TRIM_5: + case CS35L41_OTP_TRIM_6: + case CS35L41_OTP_TRIM_7: + case CS35L41_OTP_TRIM_8: + case CS35L41_OTP_TRIM_9: + case CS35L41_OTP_TRIM_10: + case CS35L41_OTP_TRIM_11: + case CS35L41_OTP_TRIM_12: + case CS35L41_OTP_TRIM_13: + case CS35L41_OTP_TRIM_14: + case CS35L41_OTP_TRIM_15: + case CS35L41_OTP_TRIM_16: + case CS35L41_OTP_TRIM_17: + case CS35L41_OTP_TRIM_18: + case CS35L41_OTP_TRIM_19: + case CS35L41_OTP_TRIM_20: + case CS35L41_OTP_TRIM_21: + case CS35L41_OTP_TRIM_22: + case CS35L41_OTP_TRIM_23: + case CS35L41_OTP_TRIM_24: + case CS35L41_OTP_TRIM_25: + case CS35L41_OTP_TRIM_26: + case CS35L41_OTP_TRIM_27: + case CS35L41_OTP_TRIM_28: + case CS35L41_OTP_TRIM_29: + case CS35L41_OTP_TRIM_30: + case CS35L41_OTP_TRIM_31: + case CS35L41_OTP_TRIM_32: + case CS35L41_OTP_TRIM_33: + case CS35L41_OTP_TRIM_34: + case CS35L41_OTP_TRIM_35: + case CS35L41_OTP_TRIM_36: + case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31: + case CS35L41_DSP1_XMEM_PACK_0 ... CS35L41_DSP1_XMEM_PACK_3068: + case CS35L41_DSP1_XMEM_UNPACK32_0 ... CS35L41_DSP1_XMEM_UNPACK32_2046: + case CS35L41_DSP1_XMEM_UNPACK24_0 ... CS35L41_DSP1_XMEM_UNPACK24_4093: + case CS35L41_DSP1_YMEM_PACK_0 ... CS35L41_DSP1_YMEM_PACK_1532: + case CS35L41_DSP1_YMEM_UNPACK32_0 ... CS35L41_DSP1_YMEM_UNPACK32_1022: + case CS35L41_DSP1_YMEM_UNPACK24_0 ... CS35L41_DSP1_YMEM_UNPACK24_2045: + case CS35L41_DSP1_PMEM_0 ... CS35L41_DSP1_PMEM_5114: + /*test regs*/ + case CS35L41_PLL_OVR: + case CS35L41_BST_TEST_DUTY: + case CS35L41_DIGPWM_IOCTRL: + return true; + default: + return false; + } +} + +static bool cs35l41_precious_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31: + case CS35L41_DSP1_XMEM_PACK_0 ... CS35L41_DSP1_XMEM_PACK_3068: + case CS35L41_DSP1_YMEM_PACK_0 ... CS35L41_DSP1_YMEM_PACK_1532: + case CS35L41_DSP1_PMEM_0 ... CS35L41_DSP1_PMEM_5114: + return true; + default: + return false; + } +} + +static bool cs35l41_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L41_DEVID: + case CS35L41_SFT_RESET: + case CS35L41_FABID: + case CS35L41_REVID: + case CS35L41_DTEMP_EN: + case CS35L41_IRQ1_STATUS: + case CS35L41_IRQ1_STATUS1: + case CS35L41_IRQ1_STATUS2: + case CS35L41_IRQ1_STATUS3: + case CS35L41_IRQ1_STATUS4: + case CS35L41_IRQ1_RAW_STATUS1: + case CS35L41_IRQ1_RAW_STATUS2: + case CS35L41_IRQ1_RAW_STATUS3: + case CS35L41_IRQ1_RAW_STATUS4: + case CS35L41_IRQ1_FRC1: + case CS35L41_IRQ1_FRC2: + case CS35L41_IRQ1_FRC3: + case CS35L41_IRQ1_FRC4: + case CS35L41_IRQ1_EDGE1: + case CS35L41_IRQ1_EDGE4: + case CS35L41_IRQ1_POL1: + case CS35L41_IRQ1_POL2: + case CS35L41_IRQ1_POL3: + case CS35L41_IRQ1_POL4: + case CS35L41_IRQ1_DB3: + case CS35L41_IRQ2_STATUS: + case CS35L41_IRQ2_STATUS1: + case CS35L41_IRQ2_STATUS2: + case CS35L41_IRQ2_STATUS3: + case CS35L41_IRQ2_STATUS4: + case CS35L41_IRQ2_RAW_STATUS1: + case CS35L41_IRQ2_RAW_STATUS2: + case CS35L41_IRQ2_RAW_STATUS3: + case CS35L41_IRQ2_RAW_STATUS4: + case CS35L41_IRQ2_FRC1: + case CS35L41_IRQ2_FRC2: + case CS35L41_IRQ2_FRC3: + case CS35L41_IRQ2_FRC4: + case CS35L41_IRQ2_EDGE1: + case CS35L41_IRQ2_EDGE4: + case CS35L41_IRQ2_POL1: + case CS35L41_IRQ2_POL2: + case CS35L41_IRQ2_POL3: + case CS35L41_IRQ2_POL4: + case CS35L41_IRQ2_DB3: + case CS35L41_GPIO_STATUS1: + case CS35L41_OTP_TRIM_1: + case CS35L41_OTP_TRIM_2: + case CS35L41_OTP_TRIM_3: + case CS35L41_OTP_TRIM_4: + case CS35L41_OTP_TRIM_5: + case CS35L41_OTP_TRIM_6: + case CS35L41_OTP_TRIM_7: + case CS35L41_OTP_TRIM_8: + case CS35L41_OTP_TRIM_9: + case CS35L41_OTP_TRIM_10: + case CS35L41_OTP_TRIM_11: + case CS35L41_OTP_TRIM_12: + case CS35L41_OTP_TRIM_13: + case CS35L41_OTP_TRIM_14: + case CS35L41_OTP_TRIM_15: + case CS35L41_OTP_TRIM_16: + case CS35L41_OTP_TRIM_17: + case CS35L41_OTP_TRIM_18: + case CS35L41_OTP_TRIM_19: + case CS35L41_OTP_TRIM_20: + case CS35L41_OTP_TRIM_21: + case CS35L41_OTP_TRIM_22: + case CS35L41_OTP_TRIM_23: + case CS35L41_OTP_TRIM_24: + case CS35L41_OTP_TRIM_25: + case CS35L41_OTP_TRIM_26: + case CS35L41_OTP_TRIM_27: + case CS35L41_OTP_TRIM_28: + case CS35L41_OTP_TRIM_29: + case CS35L41_OTP_TRIM_30: + case CS35L41_OTP_TRIM_31: + case CS35L41_OTP_TRIM_32: + case CS35L41_OTP_TRIM_33: + case CS35L41_OTP_TRIM_34: + case CS35L41_OTP_TRIM_35: + case CS35L41_OTP_TRIM_36: + case CS35L41_DSP_MBOX_1 ... CS35L41_DSP_VIRT2_MBOX_8: + case CS35L41_DSP1_XMEM_PACK_0 ... CS35L41_DSP1_XMEM_PACK_3068: + case CS35L41_DSP1_XMEM_UNPACK32_0 ... CS35L41_DSP1_XMEM_UNPACK32_2046: + case CS35L41_DSP1_XMEM_UNPACK24_0 ... CS35L41_DSP1_XMEM_UNPACK24_4093: + case CS35L41_DSP1_YMEM_PACK_0 ... CS35L41_DSP1_YMEM_PACK_1532: + case CS35L41_DSP1_YMEM_UNPACK32_0 ... CS35L41_DSP1_YMEM_UNPACK32_1022: + case CS35L41_DSP1_YMEM_UNPACK24_0 ... CS35L41_DSP1_YMEM_UNPACK24_2045: + case CS35L41_DSP1_PMEM_0 ... CS35L41_DSP1_PMEM_5114: + case CS35L41_DSP1_CCM_CORE_CTRL ... CS35L41_DSP1_WDT_STATUS: + case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31: + return true; + default: + return false; + } +} + +static const struct cs35l41_otp_packed_element_t otp_map_1[CS35L41_NUM_OTP_ELEM] = { + /* addr shift size */ + { 0x00002030, 0, 4 }, /*TRIM_OSC_FREQ_TRIM*/ + { 0x00002030, 7, 1 }, /*TRIM_OSC_TRIM_DONE*/ + { 0x0000208c, 24, 6 }, /*TST_DIGREG_VREF_TRIM*/ + { 0x00002090, 14, 4 }, /*TST_REF_TRIM*/ + { 0x00002090, 10, 4 }, /*TST_REF_TEMPCO_TRIM*/ + { 0x0000300C, 11, 4 }, /*PLL_LDOA_TST_VREF_TRIM*/ + { 0x0000394C, 23, 2 }, /*BST_ATEST_CM_VOFF*/ + { 0x00003950, 0, 7 }, /*BST_ATRIM_IADC_OFFSET*/ + { 0x00003950, 8, 7 }, /*BST_ATRIM_IADC_GAIN1*/ + { 0x00003950, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET1*/ + { 0x00003950, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN1*/ + { 0x00003954, 0, 7 }, /*BST_ATRIM_IADC_OFFSET2*/ + { 0x00003954, 8, 7 }, /*BST_ATRIM_IADC_GAIN2*/ + { 0x00003954, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET2*/ + { 0x00003954, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN2*/ + { 0x00003958, 0, 7 }, /*BST_ATRIM_IADC_OFFSET3*/ + { 0x00003958, 8, 7 }, /*BST_ATRIM_IADC_GAIN3*/ + { 0x00003958, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET3*/ + { 0x00003958, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN3*/ + { 0x0000395C, 0, 7 }, /*BST_ATRIM_IADC_OFFSET4*/ + { 0x0000395C, 8, 7 }, /*BST_ATRIM_IADC_GAIN4*/ + { 0x0000395C, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET4*/ + { 0x0000395C, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN4*/ + { 0x0000416C, 0, 8 }, /*VMON_GAIN_OTP_VAL*/ + { 0x00004160, 0, 7 }, /*VMON_OFFSET_OTP_VAL*/ + { 0x0000416C, 8, 8 }, /*IMON_GAIN_OTP_VAL*/ + { 0x00004160, 16, 10 }, /*IMON_OFFSET_OTP_VAL*/ + { 0x0000416C, 16, 12 }, /*VMON_CM_GAIN_OTP_VAL*/ + { 0x0000416C, 28, 1 }, /*VMON_CM_GAIN_SIGN_OTP_VAL*/ + { 0x00004170, 0, 6 }, /*IMON_CAL_TEMPCO_OTP_VAL*/ + { 0x00004170, 6, 1 }, /*IMON_CAL_TEMPCO_SIGN_OTP*/ + { 0x00004170, 8, 6 }, /*IMON_CAL_TEMPCO2_OTP_VAL*/ + { 0x00004170, 14, 1 }, /*IMON_CAL_TEMPCO2_DN_UPB_OTP_VAL*/ + { 0x00004170, 16, 9 }, /*IMON_CAL_TEMPCO_TBASE_OTP_VAL*/ + { 0x00004360, 0, 5 }, /*TEMP_GAIN_OTP_VAL*/ + { 0x00004360, 6, 9 }, /*TEMP_OFFSET_OTP_VAL*/ + { 0x00004448, 0, 8 }, /*VP_SARADC_OFFSET*/ + { 0x00004448, 8, 8 }, /*VP_GAIN_INDEX*/ + { 0x00004448, 16, 8 }, /*VBST_SARADC_OFFSET*/ + { 0x00004448, 24, 8 }, /*VBST_GAIN_INDEX*/ + { 0x0000444C, 0, 3 }, /*ANA_SELINVREF*/ + { 0x00006E30, 0, 5 }, /*GAIN_ERR_COEFF_0*/ + { 0x00006E30, 8, 5 }, /*GAIN_ERR_COEFF_1*/ + { 0x00006E30, 16, 5 }, /*GAIN_ERR_COEFF_2*/ + { 0x00006E30, 24, 5 }, /*GAIN_ERR_COEFF_3*/ + { 0x00006E34, 0, 5 }, /*GAIN_ERR_COEFF_4*/ + { 0x00006E34, 8, 5 }, /*GAIN_ERR_COEFF_5*/ + { 0x00006E34, 16, 5 }, /*GAIN_ERR_COEFF_6*/ + { 0x00006E34, 24, 5 }, /*GAIN_ERR_COEFF_7*/ + { 0x00006E38, 0, 5 }, /*GAIN_ERR_COEFF_8*/ + { 0x00006E38, 8, 5 }, /*GAIN_ERR_COEFF_9*/ + { 0x00006E38, 16, 5 }, /*GAIN_ERR_COEFF_10*/ + { 0x00006E38, 24, 5 }, /*GAIN_ERR_COEFF_11*/ + { 0x00006E3C, 0, 5 }, /*GAIN_ERR_COEFF_12*/ + { 0x00006E3C, 8, 5 }, /*GAIN_ERR_COEFF_13*/ + { 0x00006E3C, 16, 5 }, /*GAIN_ERR_COEFF_14*/ + { 0x00006E3C, 24, 5 }, /*GAIN_ERR_COEFF_15*/ + { 0x00006E40, 0, 5 }, /*GAIN_ERR_COEFF_16*/ + { 0x00006E40, 8, 5 }, /*GAIN_ERR_COEFF_17*/ + { 0x00006E40, 16, 5 }, /*GAIN_ERR_COEFF_18*/ + { 0x00006E40, 24, 5 }, /*GAIN_ERR_COEFF_19*/ + { 0x00006E44, 0, 5 }, /*GAIN_ERR_COEFF_20*/ + { 0x00006E48, 0, 10 }, /*VOFF_GAIN_0*/ + { 0x00006E48, 10, 10 }, /*VOFF_GAIN_1*/ + { 0x00006E48, 20, 10 }, /*VOFF_GAIN_2*/ + { 0x00006E4C, 0, 10 }, /*VOFF_GAIN_3*/ + { 0x00006E4C, 10, 10 }, /*VOFF_GAIN_4*/ + { 0x00006E4C, 20, 10 }, /*VOFF_GAIN_5*/ + { 0x00006E50, 0, 10 }, /*VOFF_GAIN_6*/ + { 0x00006E50, 10, 10 }, /*VOFF_GAIN_7*/ + { 0x00006E50, 20, 10 }, /*VOFF_GAIN_8*/ + { 0x00006E54, 0, 10 }, /*VOFF_GAIN_9*/ + { 0x00006E54, 10, 10 }, /*VOFF_GAIN_10*/ + { 0x00006E54, 20, 10 }, /*VOFF_GAIN_11*/ + { 0x00006E58, 0, 10 }, /*VOFF_GAIN_12*/ + { 0x00006E58, 10, 10 }, /*VOFF_GAIN_13*/ + { 0x00006E58, 20, 10 }, /*VOFF_GAIN_14*/ + { 0x00006E5C, 0, 10 }, /*VOFF_GAIN_15*/ + { 0x00006E5C, 10, 10 }, /*VOFF_GAIN_16*/ + { 0x00006E5C, 20, 10 }, /*VOFF_GAIN_17*/ + { 0x00006E60, 0, 10 }, /*VOFF_GAIN_18*/ + { 0x00006E60, 10, 10 }, /*VOFF_GAIN_19*/ + { 0x00006E60, 20, 10 }, /*VOFF_GAIN_20*/ + { 0x00006E64, 0, 10 }, /*VOFF_INT1*/ + { 0x00007418, 7, 5 }, /*DS_SPK_INT1_CAP_TRIM*/ + { 0x0000741C, 0, 5 }, /*DS_SPK_INT2_CAP_TRIM*/ + { 0x0000741C, 11, 4 }, /*DS_SPK_LPF_CAP_TRIM*/ + { 0x0000741C, 19, 4 }, /*DS_SPK_QUAN_CAP_TRIM*/ + { 0x00007434, 17, 1 }, /*FORCE_CAL*/ + { 0x00007434, 18, 7 }, /*CAL_OVERRIDE*/ + { 0x00007068, 0, 9 }, /*MODIX*/ + { 0x0000410C, 7, 1 }, /*VIMON_DLY_NOT_COMB*/ + { 0x0000400C, 0, 7 }, /*VIMON_DLY*/ + { 0x00000000, 0, 1 }, /*extra bit*/ + { 0x00017040, 0, 8 }, /*X_COORDINATE*/ + { 0x00017040, 8, 8 }, /*Y_COORDINATE*/ + { 0x00017040, 16, 8 }, /*WAFER_ID*/ + { 0x00017040, 24, 8 }, /*DVS*/ + { 0x00017044, 0, 24 }, /*LOT_NUMBER*/ +}; + +static const struct cs35l41_otp_packed_element_t otp_map_2[CS35L41_NUM_OTP_ELEM] = { + /* addr shift size */ + { 0x00002030, 0, 4 }, /*TRIM_OSC_FREQ_TRIM*/ + { 0x00002030, 7, 1 }, /*TRIM_OSC_TRIM_DONE*/ + { 0x0000208c, 24, 6 }, /*TST_DIGREG_VREF_TRIM*/ + { 0x00002090, 14, 4 }, /*TST_REF_TRIM*/ + { 0x00002090, 10, 4 }, /*TST_REF_TEMPCO_TRIM*/ + { 0x0000300C, 11, 4 }, /*PLL_LDOA_TST_VREF_TRIM*/ + { 0x0000394C, 23, 2 }, /*BST_ATEST_CM_VOFF*/ + { 0x00003950, 0, 7 }, /*BST_ATRIM_IADC_OFFSET*/ + { 0x00003950, 8, 7 }, /*BST_ATRIM_IADC_GAIN1*/ + { 0x00003950, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET1*/ + { 0x00003950, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN1*/ + { 0x00003954, 0, 7 }, /*BST_ATRIM_IADC_OFFSET2*/ + { 0x00003954, 8, 7 }, /*BST_ATRIM_IADC_GAIN2*/ + { 0x00003954, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET2*/ + { 0x00003954, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN2*/ + { 0x00003958, 0, 7 }, /*BST_ATRIM_IADC_OFFSET3*/ + { 0x00003958, 8, 7 }, /*BST_ATRIM_IADC_GAIN3*/ + { 0x00003958, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET3*/ + { 0x00003958, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN3*/ + { 0x0000395C, 0, 7 }, /*BST_ATRIM_IADC_OFFSET4*/ + { 0x0000395C, 8, 7 }, /*BST_ATRIM_IADC_GAIN4*/ + { 0x0000395C, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET4*/ + { 0x0000395C, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN4*/ + { 0x0000416C, 0, 8 }, /*VMON_GAIN_OTP_VAL*/ + { 0x00004160, 0, 7 }, /*VMON_OFFSET_OTP_VAL*/ + { 0x0000416C, 8, 8 }, /*IMON_GAIN_OTP_VAL*/ + { 0x00004160, 16, 10 }, /*IMON_OFFSET_OTP_VAL*/ + { 0x0000416C, 16, 12 }, /*VMON_CM_GAIN_OTP_VAL*/ + { 0x0000416C, 28, 1 }, /*VMON_CM_GAIN_SIGN_OTP_VAL*/ + { 0x00004170, 0, 6 }, /*IMON_CAL_TEMPCO_OTP_VAL*/ + { 0x00004170, 6, 1 }, /*IMON_CAL_TEMPCO_SIGN_OTP*/ + { 0x00004170, 8, 6 }, /*IMON_CAL_TEMPCO2_OTP_VAL*/ + { 0x00004170, 14, 1 }, /*IMON_CAL_TEMPCO2_DN_UPB_OTP_VAL*/ + { 0x00004170, 16, 9 }, /*IMON_CAL_TEMPCO_TBASE_OTP_VAL*/ + { 0x00004360, 0, 5 }, /*TEMP_GAIN_OTP_VAL*/ + { 0x00004360, 6, 9 }, /*TEMP_OFFSET_OTP_VAL*/ + { 0x00004448, 0, 8 }, /*VP_SARADC_OFFSET*/ + { 0x00004448, 8, 8 }, /*VP_GAIN_INDEX*/ + { 0x00004448, 16, 8 }, /*VBST_SARADC_OFFSET*/ + { 0x00004448, 24, 8 }, /*VBST_GAIN_INDEX*/ + { 0x0000444C, 0, 3 }, /*ANA_SELINVREF*/ + { 0x00006E30, 0, 5 }, /*GAIN_ERR_COEFF_0*/ + { 0x00006E30, 8, 5 }, /*GAIN_ERR_COEFF_1*/ + { 0x00006E30, 16, 5 }, /*GAIN_ERR_COEFF_2*/ + { 0x00006E30, 24, 5 }, /*GAIN_ERR_COEFF_3*/ + { 0x00006E34, 0, 5 }, /*GAIN_ERR_COEFF_4*/ + { 0x00006E34, 8, 5 }, /*GAIN_ERR_COEFF_5*/ + { 0x00006E34, 16, 5 }, /*GAIN_ERR_COEFF_6*/ + { 0x00006E34, 24, 5 }, /*GAIN_ERR_COEFF_7*/ + { 0x00006E38, 0, 5 }, /*GAIN_ERR_COEFF_8*/ + { 0x00006E38, 8, 5 }, /*GAIN_ERR_COEFF_9*/ + { 0x00006E38, 16, 5 }, /*GAIN_ERR_COEFF_10*/ + { 0x00006E38, 24, 5 }, /*GAIN_ERR_COEFF_11*/ + { 0x00006E3C, 0, 5 }, /*GAIN_ERR_COEFF_12*/ + { 0x00006E3C, 8, 5 }, /*GAIN_ERR_COEFF_13*/ + { 0x00006E3C, 16, 5 }, /*GAIN_ERR_COEFF_14*/ + { 0x00006E3C, 24, 5 }, /*GAIN_ERR_COEFF_15*/ + { 0x00006E40, 0, 5 }, /*GAIN_ERR_COEFF_16*/ + { 0x00006E40, 8, 5 }, /*GAIN_ERR_COEFF_17*/ + { 0x00006E40, 16, 5 }, /*GAIN_ERR_COEFF_18*/ + { 0x00006E40, 24, 5 }, /*GAIN_ERR_COEFF_19*/ + { 0x00006E44, 0, 5 }, /*GAIN_ERR_COEFF_20*/ + { 0x00006E48, 0, 10 }, /*VOFF_GAIN_0*/ + { 0x00006E48, 10, 10 }, /*VOFF_GAIN_1*/ + { 0x00006E48, 20, 10 }, /*VOFF_GAIN_2*/ + { 0x00006E4C, 0, 10 }, /*VOFF_GAIN_3*/ + { 0x00006E4C, 10, 10 }, /*VOFF_GAIN_4*/ + { 0x00006E4C, 20, 10 }, /*VOFF_GAIN_5*/ + { 0x00006E50, 0, 10 }, /*VOFF_GAIN_6*/ + { 0x00006E50, 10, 10 }, /*VOFF_GAIN_7*/ + { 0x00006E50, 20, 10 }, /*VOFF_GAIN_8*/ + { 0x00006E54, 0, 10 }, /*VOFF_GAIN_9*/ + { 0x00006E54, 10, 10 }, /*VOFF_GAIN_10*/ + { 0x00006E54, 20, 10 }, /*VOFF_GAIN_11*/ + { 0x00006E58, 0, 10 }, /*VOFF_GAIN_12*/ + { 0x00006E58, 10, 10 }, /*VOFF_GAIN_13*/ + { 0x00006E58, 20, 10 }, /*VOFF_GAIN_14*/ + { 0x00006E5C, 0, 10 }, /*VOFF_GAIN_15*/ + { 0x00006E5C, 10, 10 }, /*VOFF_GAIN_16*/ + { 0x00006E5C, 20, 10 }, /*VOFF_GAIN_17*/ + { 0x00006E60, 0, 10 }, /*VOFF_GAIN_18*/ + { 0x00006E60, 10, 10 }, /*VOFF_GAIN_19*/ + { 0x00006E60, 20, 10 }, /*VOFF_GAIN_20*/ + { 0x00006E64, 0, 10 }, /*VOFF_INT1*/ + { 0x00007418, 7, 5 }, /*DS_SPK_INT1_CAP_TRIM*/ + { 0x0000741C, 0, 5 }, /*DS_SPK_INT2_CAP_TRIM*/ + { 0x0000741C, 11, 4 }, /*DS_SPK_LPF_CAP_TRIM*/ + { 0x0000741C, 19, 4 }, /*DS_SPK_QUAN_CAP_TRIM*/ + { 0x00007434, 17, 1 }, /*FORCE_CAL*/ + { 0x00007434, 18, 7 }, /*CAL_OVERRIDE*/ + { 0x00007068, 0, 9 }, /*MODIX*/ + { 0x0000410C, 7, 1 }, /*VIMON_DLY_NOT_COMB*/ + { 0x0000400C, 0, 7 }, /*VIMON_DLY*/ + { 0x00004000, 11, 1 }, /*VMON_POL*/ + { 0x00017040, 0, 8 }, /*X_COORDINATE*/ + { 0x00017040, 8, 8 }, /*Y_COORDINATE*/ + { 0x00017040, 16, 8 }, /*WAFER_ID*/ + { 0x00017040, 24, 8 }, /*DVS*/ + { 0x00017044, 0, 24 }, /*LOT_NUMBER*/ +}; + +const struct cs35l41_otp_map_element_t cs35l41_otp_map_map[CS35L41_NUM_OTP_MAPS] = { + { + .id = 0x01, + .map = otp_map_1, + .num_elements = CS35L41_NUM_OTP_ELEM, + .bit_offset = 16, + .word_offset = 2, + }, + { + .id = 0x02, + .map = otp_map_2, + .num_elements = CS35L41_NUM_OTP_ELEM, + .bit_offset = 16, + .word_offset = 2, + }, + { + .id = 0x03, + .map = otp_map_2, + .num_elements = CS35L41_NUM_OTP_ELEM, + .bit_offset = 16, + .word_offset = 2, + }, + { + .id = 0x06, + .map = otp_map_2, + .num_elements = CS35L41_NUM_OTP_ELEM, + .bit_offset = 16, + .word_offset = 2, + }, + { + .id = 0x08, + .map = otp_map_1, + .num_elements = CS35L41_NUM_OTP_ELEM, + .bit_offset = 16, + .word_offset = 2, + }, +}; +EXPORT_SYMBOL_GPL(cs35l41_otp_map_map); + +struct regmap_config cs35l41_regmap_i2c = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = CS35L41_REGSTRIDE, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_BIG, + .max_register = CS35L41_LASTREG, + .reg_defaults = cs35l41_reg, + .num_reg_defaults = ARRAY_SIZE(cs35l41_reg), + .volatile_reg = cs35l41_volatile_reg, + .readable_reg = cs35l41_readable_reg, + .precious_reg = cs35l41_precious_reg, + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL_GPL(cs35l41_regmap_i2c); + +struct regmap_config cs35l41_regmap_spi = { + .reg_bits = 32, + .val_bits = 32, + .pad_bits = 16, + .reg_stride = CS35L41_REGSTRIDE, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_BIG, + .max_register = CS35L41_LASTREG, + .reg_defaults = cs35l41_reg, + .num_reg_defaults = ARRAY_SIZE(cs35l41_reg), + .volatile_reg = cs35l41_volatile_reg, + .readable_reg = cs35l41_readable_reg, + .precious_reg = cs35l41_precious_reg, + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL_GPL(cs35l41_regmap_spi); + +MODULE_DESCRIPTION("CS35L41 library"); +MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, "); +MODULE_AUTHOR("Lucas Tanure, Cirrus Logic Inc, "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l41-spi.c b/sound/soc/codecs/cs35l41-spi.c index c202d9df70ee..c157153f28d8 100644 --- a/sound/soc/codecs/cs35l41-spi.c +++ b/sound/soc/codecs/cs35l41-spi.c @@ -15,7 +15,6 @@ #include #include -#include #include "cs35l41.h" static const struct spi_device_id cs35l41_id_spi[] = { diff --git a/sound/soc/codecs/cs35l41-tables.c b/sound/soc/codecs/cs35l41-tables.c deleted file mode 100644 index 3eb18b17a7b0..000000000000 --- a/sound/soc/codecs/cs35l41-tables.c +++ /dev/null @@ -1,723 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// cs35l41-tables.c -- CS35L41 ALSA SoC audio driver -// -// Copyright 2017-2021 Cirrus Logic, Inc. -// -// Author: David Rhodes - -#include "cs35l41.h" - -static const struct reg_default cs35l41_reg[] = { - { CS35L41_PWR_CTRL1, 0x00000000 }, - { CS35L41_PWR_CTRL3, 0x01000010 }, - { CS35L41_GPIO_PAD_CONTROL, 0x00000000 }, - { CS35L41_SP_ENABLES, 0x00000000 }, - { CS35L41_SP_RATE_CTRL, 0x00000028 }, - { CS35L41_SP_FORMAT, 0x18180200 }, - { CS35L41_SP_HIZ_CTRL, 0x00000002 }, - { CS35L41_SP_FRAME_TX_SLOT, 0x03020100 }, - { CS35L41_SP_FRAME_RX_SLOT, 0x00000100 }, - { CS35L41_SP_TX_WL, 0x00000018 }, - { CS35L41_SP_RX_WL, 0x00000018 }, - { CS35L41_DAC_PCM1_SRC, 0x00000008 }, - { CS35L41_ASP_TX1_SRC, 0x00000018 }, - { CS35L41_ASP_TX2_SRC, 0x00000019 }, - { CS35L41_ASP_TX3_SRC, 0x00000020 }, - { CS35L41_ASP_TX4_SRC, 0x00000021 }, - { CS35L41_DSP1_RX1_SRC, 0x00000008 }, - { CS35L41_DSP1_RX2_SRC, 0x00000009 }, - { CS35L41_DSP1_RX3_SRC, 0x00000018 }, - { CS35L41_DSP1_RX4_SRC, 0x00000019 }, - { CS35L41_DSP1_RX5_SRC, 0x00000020 }, - { CS35L41_DSP1_RX6_SRC, 0x00000021 }, - { CS35L41_DSP1_RX7_SRC, 0x0000003A }, - { CS35L41_DSP1_RX8_SRC, 0x00000001 }, - { CS35L41_NGATE1_SRC, 0x00000008 }, - { CS35L41_NGATE2_SRC, 0x00000009 }, - { CS35L41_AMP_DIG_VOL_CTRL, 0x00008000 }, - { CS35L41_CLASSH_CFG, 0x000B0405 }, - { CS35L41_WKFET_CFG, 0x00000111 }, - { CS35L41_NG_CFG, 0x00000033 }, - { CS35L41_AMP_GAIN_CTRL, 0x00000273 }, - { CS35L41_GPIO1_CTRL1, 0xE1000001 }, - { CS35L41_GPIO2_CTRL1, 0xE1000001 }, - { CS35L41_MIXER_NGATE_CFG, 0x00000000 }, - { CS35L41_MIXER_NGATE_CH1_CFG, 0x00000303 }, - { CS35L41_MIXER_NGATE_CH2_CFG, 0x00000303 }, -}; - -static bool cs35l41_readable_reg(struct device *dev, unsigned int reg) -{ - switch (reg) { - case CS35L41_DEVID: - case CS35L41_REVID: - case CS35L41_FABID: - case CS35L41_RELID: - case CS35L41_OTPID: - case CS35L41_TEST_KEY_CTL: - case CS35L41_USER_KEY_CTL: - case CS35L41_OTP_CTRL0: - case CS35L41_OTP_CTRL3: - case CS35L41_OTP_CTRL4: - case CS35L41_OTP_CTRL5: - case CS35L41_OTP_CTRL6: - case CS35L41_OTP_CTRL7: - case CS35L41_OTP_CTRL8: - case CS35L41_PWR_CTRL1: - case CS35L41_PWR_CTRL2: - case CS35L41_PWR_CTRL3: - case CS35L41_CTRL_OVRRIDE: - case CS35L41_AMP_OUT_MUTE: - case CS35L41_PROTECT_REL_ERR_IGN: - case CS35L41_GPIO_PAD_CONTROL: - case CS35L41_JTAG_CONTROL: - case CS35L41_PLL_CLK_CTRL: - case CS35L41_DSP_CLK_CTRL: - case CS35L41_GLOBAL_CLK_CTRL: - case CS35L41_DATA_FS_SEL: - case CS35L41_MDSYNC_EN: - case CS35L41_MDSYNC_TX_ID: - case CS35L41_MDSYNC_PWR_CTRL: - case CS35L41_MDSYNC_DATA_TX: - case CS35L41_MDSYNC_TX_STATUS: - case CS35L41_MDSYNC_DATA_RX: - case CS35L41_MDSYNC_RX_STATUS: - case CS35L41_MDSYNC_ERR_STATUS: - case CS35L41_MDSYNC_SYNC_PTE2: - case CS35L41_MDSYNC_SYNC_PTE3: - case CS35L41_MDSYNC_SYNC_MSM_STATUS: - case CS35L41_BSTCVRT_VCTRL1: - case CS35L41_BSTCVRT_VCTRL2: - case CS35L41_BSTCVRT_PEAK_CUR: - case CS35L41_BSTCVRT_SFT_RAMP: - case CS35L41_BSTCVRT_COEFF: - case CS35L41_BSTCVRT_SLOPE_LBST: - case CS35L41_BSTCVRT_SW_FREQ: - case CS35L41_BSTCVRT_DCM_CTRL: - case CS35L41_BSTCVRT_DCM_MODE_FORCE: - case CS35L41_BSTCVRT_OVERVOLT_CTRL: - case CS35L41_VI_VOL_POL: - case CS35L41_DTEMP_WARN_THLD: - case CS35L41_DTEMP_CFG: - case CS35L41_DTEMP_EN: - case CS35L41_VPVBST_FS_SEL: - case CS35L41_SP_ENABLES: - case CS35L41_SP_RATE_CTRL: - case CS35L41_SP_FORMAT: - case CS35L41_SP_HIZ_CTRL: - case CS35L41_SP_FRAME_TX_SLOT: - case CS35L41_SP_FRAME_RX_SLOT: - case CS35L41_SP_TX_WL: - case CS35L41_SP_RX_WL: - case CS35L41_DAC_PCM1_SRC: - case CS35L41_ASP_TX1_SRC: - case CS35L41_ASP_TX2_SRC: - case CS35L41_ASP_TX3_SRC: - case CS35L41_ASP_TX4_SRC: - case CS35L41_DSP1_RX1_SRC: - case CS35L41_DSP1_RX2_SRC: - case CS35L41_DSP1_RX3_SRC: - case CS35L41_DSP1_RX4_SRC: - case CS35L41_DSP1_RX5_SRC: - case CS35L41_DSP1_RX6_SRC: - case CS35L41_DSP1_RX7_SRC: - case CS35L41_DSP1_RX8_SRC: - case CS35L41_NGATE1_SRC: - case CS35L41_NGATE2_SRC: - case CS35L41_AMP_DIG_VOL_CTRL: - case CS35L41_VPBR_CFG: - case CS35L41_VBBR_CFG: - case CS35L41_VPBR_STATUS: - case CS35L41_VBBR_STATUS: - case CS35L41_OVERTEMP_CFG: - case CS35L41_AMP_ERR_VOL: - case CS35L41_VOL_STATUS_TO_DSP: - case CS35L41_CLASSH_CFG: - case CS35L41_WKFET_CFG: - case CS35L41_NG_CFG: - case CS35L41_AMP_GAIN_CTRL: - case CS35L41_DAC_MSM_CFG: - case CS35L41_IRQ1_CFG: - case CS35L41_IRQ1_STATUS: - case CS35L41_IRQ1_STATUS1: - case CS35L41_IRQ1_STATUS2: - case CS35L41_IRQ1_STATUS3: - case CS35L41_IRQ1_STATUS4: - case CS35L41_IRQ1_RAW_STATUS1: - case CS35L41_IRQ1_RAW_STATUS2: - case CS35L41_IRQ1_RAW_STATUS3: - case CS35L41_IRQ1_RAW_STATUS4: - case CS35L41_IRQ1_MASK1: - case CS35L41_IRQ1_MASK2: - case CS35L41_IRQ1_MASK3: - case CS35L41_IRQ1_MASK4: - case CS35L41_IRQ1_FRC1: - case CS35L41_IRQ1_FRC2: - case CS35L41_IRQ1_FRC3: - case CS35L41_IRQ1_FRC4: - case CS35L41_IRQ1_EDGE1: - case CS35L41_IRQ1_EDGE4: - case CS35L41_IRQ1_POL1: - case CS35L41_IRQ1_POL2: - case CS35L41_IRQ1_POL3: - case CS35L41_IRQ1_POL4: - case CS35L41_IRQ1_DB3: - case CS35L41_IRQ2_CFG: - case CS35L41_IRQ2_STATUS: - case CS35L41_IRQ2_STATUS1: - case CS35L41_IRQ2_STATUS2: - case CS35L41_IRQ2_STATUS3: - case CS35L41_IRQ2_STATUS4: - case CS35L41_IRQ2_RAW_STATUS1: - case CS35L41_IRQ2_RAW_STATUS2: - case CS35L41_IRQ2_RAW_STATUS3: - case CS35L41_IRQ2_RAW_STATUS4: - case CS35L41_IRQ2_MASK1: - case CS35L41_IRQ2_MASK2: - case CS35L41_IRQ2_MASK3: - case CS35L41_IRQ2_MASK4: - case CS35L41_IRQ2_FRC1: - case CS35L41_IRQ2_FRC2: - case CS35L41_IRQ2_FRC3: - case CS35L41_IRQ2_FRC4: - case CS35L41_IRQ2_EDGE1: - case CS35L41_IRQ2_EDGE4: - case CS35L41_IRQ2_POL1: - case CS35L41_IRQ2_POL2: - case CS35L41_IRQ2_POL3: - case CS35L41_IRQ2_POL4: - case CS35L41_IRQ2_DB3: - case CS35L41_GPIO_STATUS1: - case CS35L41_GPIO1_CTRL1: - case CS35L41_GPIO2_CTRL1: - case CS35L41_MIXER_NGATE_CFG: - case CS35L41_MIXER_NGATE_CH1_CFG: - case CS35L41_MIXER_NGATE_CH2_CFG: - case CS35L41_DSP_MBOX_1 ... CS35L41_DSP_VIRT2_MBOX_8: - case CS35L41_CLOCK_DETECT_1: - case CS35L41_DIE_STS1: - case CS35L41_DIE_STS2: - case CS35L41_TEMP_CAL1: - case CS35L41_TEMP_CAL2: - case CS35L41_DSP1_TIMESTAMP_COUNT: - case CS35L41_DSP1_SYS_ID: - case CS35L41_DSP1_SYS_VERSION: - case CS35L41_DSP1_SYS_CORE_ID: - case CS35L41_DSP1_SYS_AHB_ADDR: - case CS35L41_DSP1_SYS_XSRAM_SIZE: - case CS35L41_DSP1_SYS_YSRAM_SIZE: - case CS35L41_DSP1_SYS_PSRAM_SIZE: - case CS35L41_DSP1_SYS_PM_BOOT_SIZE: - case CS35L41_DSP1_SYS_FEATURES: - case CS35L41_DSP1_SYS_FIR_FILTERS: - case CS35L41_DSP1_SYS_LMS_FILTERS: - case CS35L41_DSP1_SYS_XM_BANK_SIZE: - case CS35L41_DSP1_SYS_YM_BANK_SIZE: - case CS35L41_DSP1_SYS_PM_BANK_SIZE: - case CS35L41_DSP1_RX1_RATE: - case CS35L41_DSP1_RX2_RATE: - case CS35L41_DSP1_RX3_RATE: - case CS35L41_DSP1_RX4_RATE: - case CS35L41_DSP1_RX5_RATE: - case CS35L41_DSP1_RX6_RATE: - case CS35L41_DSP1_RX7_RATE: - case CS35L41_DSP1_RX8_RATE: - case CS35L41_DSP1_TX1_RATE: - case CS35L41_DSP1_TX2_RATE: - case CS35L41_DSP1_TX3_RATE: - case CS35L41_DSP1_TX4_RATE: - case CS35L41_DSP1_TX5_RATE: - case CS35L41_DSP1_TX6_RATE: - case CS35L41_DSP1_TX7_RATE: - case CS35L41_DSP1_TX8_RATE: - case CS35L41_DSP1_SCRATCH1: - case CS35L41_DSP1_SCRATCH2: - case CS35L41_DSP1_SCRATCH3: - case CS35L41_DSP1_SCRATCH4: - case CS35L41_DSP1_CCM_CORE_CTRL: - case CS35L41_DSP1_CCM_CLK_OVERRIDE: - case CS35L41_DSP1_XM_MSTR_EN: - case CS35L41_DSP1_XM_CORE_PRI: - case CS35L41_DSP1_XM_AHB_PACK_PL_PRI: - case CS35L41_DSP1_XM_AHB_UP_PL_PRI: - case CS35L41_DSP1_XM_ACCEL_PL0_PRI: - case CS35L41_DSP1_XM_NPL0_PRI: - case CS35L41_DSP1_YM_MSTR_EN: - case CS35L41_DSP1_YM_CORE_PRI: - case CS35L41_DSP1_YM_AHB_PACK_PL_PRI: - case CS35L41_DSP1_YM_AHB_UP_PL_PRI: - case CS35L41_DSP1_YM_ACCEL_PL0_PRI: - case CS35L41_DSP1_YM_NPL0_PRI: - case CS35L41_DSP1_MPU_XM_ACCESS0: - case CS35L41_DSP1_MPU_YM_ACCESS0: - case CS35L41_DSP1_MPU_WNDW_ACCESS0: - case CS35L41_DSP1_MPU_XREG_ACCESS0: - case CS35L41_DSP1_MPU_YREG_ACCESS0: - case CS35L41_DSP1_MPU_XM_ACCESS1: - case CS35L41_DSP1_MPU_YM_ACCESS1: - case CS35L41_DSP1_MPU_WNDW_ACCESS1: - case CS35L41_DSP1_MPU_XREG_ACCESS1: - case CS35L41_DSP1_MPU_YREG_ACCESS1: - case CS35L41_DSP1_MPU_XM_ACCESS2: - case CS35L41_DSP1_MPU_YM_ACCESS2: - case CS35L41_DSP1_MPU_WNDW_ACCESS2: - case CS35L41_DSP1_MPU_XREG_ACCESS2: - case CS35L41_DSP1_MPU_YREG_ACCESS2: - case CS35L41_DSP1_MPU_XM_ACCESS3: - case CS35L41_DSP1_MPU_YM_ACCESS3: - case CS35L41_DSP1_MPU_WNDW_ACCESS3: - case CS35L41_DSP1_MPU_XREG_ACCESS3: - case CS35L41_DSP1_MPU_YREG_ACCESS3: - case CS35L41_DSP1_MPU_XM_VIO_ADDR: - case CS35L41_DSP1_MPU_XM_VIO_STATUS: - case CS35L41_DSP1_MPU_YM_VIO_ADDR: - case CS35L41_DSP1_MPU_YM_VIO_STATUS: - case CS35L41_DSP1_MPU_PM_VIO_ADDR: - case CS35L41_DSP1_MPU_PM_VIO_STATUS: - case CS35L41_DSP1_MPU_LOCK_CONFIG: - case CS35L41_DSP1_MPU_WDT_RST_CTRL: - case CS35L41_OTP_TRIM_1: - case CS35L41_OTP_TRIM_2: - case CS35L41_OTP_TRIM_3: - case CS35L41_OTP_TRIM_4: - case CS35L41_OTP_TRIM_5: - case CS35L41_OTP_TRIM_6: - case CS35L41_OTP_TRIM_7: - case CS35L41_OTP_TRIM_8: - case CS35L41_OTP_TRIM_9: - case CS35L41_OTP_TRIM_10: - case CS35L41_OTP_TRIM_11: - case CS35L41_OTP_TRIM_12: - case CS35L41_OTP_TRIM_13: - case CS35L41_OTP_TRIM_14: - case CS35L41_OTP_TRIM_15: - case CS35L41_OTP_TRIM_16: - case CS35L41_OTP_TRIM_17: - case CS35L41_OTP_TRIM_18: - case CS35L41_OTP_TRIM_19: - case CS35L41_OTP_TRIM_20: - case CS35L41_OTP_TRIM_21: - case CS35L41_OTP_TRIM_22: - case CS35L41_OTP_TRIM_23: - case CS35L41_OTP_TRIM_24: - case CS35L41_OTP_TRIM_25: - case CS35L41_OTP_TRIM_26: - case CS35L41_OTP_TRIM_27: - case CS35L41_OTP_TRIM_28: - case CS35L41_OTP_TRIM_29: - case CS35L41_OTP_TRIM_30: - case CS35L41_OTP_TRIM_31: - case CS35L41_OTP_TRIM_32: - case CS35L41_OTP_TRIM_33: - case CS35L41_OTP_TRIM_34: - case CS35L41_OTP_TRIM_35: - case CS35L41_OTP_TRIM_36: - case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31: - case CS35L41_DSP1_XMEM_PACK_0 ... CS35L41_DSP1_XMEM_PACK_3068: - case CS35L41_DSP1_XMEM_UNPACK32_0 ... CS35L41_DSP1_XMEM_UNPACK32_2046: - case CS35L41_DSP1_XMEM_UNPACK24_0 ... CS35L41_DSP1_XMEM_UNPACK24_4093: - case CS35L41_DSP1_YMEM_PACK_0 ... CS35L41_DSP1_YMEM_PACK_1532: - case CS35L41_DSP1_YMEM_UNPACK32_0 ... CS35L41_DSP1_YMEM_UNPACK32_1022: - case CS35L41_DSP1_YMEM_UNPACK24_0 ... CS35L41_DSP1_YMEM_UNPACK24_2045: - case CS35L41_DSP1_PMEM_0 ... CS35L41_DSP1_PMEM_5114: - /*test regs*/ - case CS35L41_PLL_OVR: - case CS35L41_BST_TEST_DUTY: - case CS35L41_DIGPWM_IOCTRL: - return true; - default: - return false; - } -} - -static bool cs35l41_precious_reg(struct device *dev, unsigned int reg) -{ - switch (reg) { - case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31: - case CS35L41_DSP1_XMEM_PACK_0 ... CS35L41_DSP1_XMEM_PACK_3068: - case CS35L41_DSP1_YMEM_PACK_0 ... CS35L41_DSP1_YMEM_PACK_1532: - case CS35L41_DSP1_PMEM_0 ... CS35L41_DSP1_PMEM_5114: - return true; - default: - return false; - } -} - -static bool cs35l41_volatile_reg(struct device *dev, unsigned int reg) -{ - switch (reg) { - case CS35L41_DEVID: - case CS35L41_SFT_RESET: - case CS35L41_FABID: - case CS35L41_REVID: - case CS35L41_DTEMP_EN: - case CS35L41_IRQ1_STATUS: - case CS35L41_IRQ1_STATUS1: - case CS35L41_IRQ1_STATUS2: - case CS35L41_IRQ1_STATUS3: - case CS35L41_IRQ1_STATUS4: - case CS35L41_IRQ1_RAW_STATUS1: - case CS35L41_IRQ1_RAW_STATUS2: - case CS35L41_IRQ1_RAW_STATUS3: - case CS35L41_IRQ1_RAW_STATUS4: - case CS35L41_IRQ1_FRC1: - case CS35L41_IRQ1_FRC2: - case CS35L41_IRQ1_FRC3: - case CS35L41_IRQ1_FRC4: - case CS35L41_IRQ1_EDGE1: - case CS35L41_IRQ1_EDGE4: - case CS35L41_IRQ1_POL1: - case CS35L41_IRQ1_POL2: - case CS35L41_IRQ1_POL3: - case CS35L41_IRQ1_POL4: - case CS35L41_IRQ1_DB3: - case CS35L41_IRQ2_STATUS: - case CS35L41_IRQ2_STATUS1: - case CS35L41_IRQ2_STATUS2: - case CS35L41_IRQ2_STATUS3: - case CS35L41_IRQ2_STATUS4: - case CS35L41_IRQ2_RAW_STATUS1: - case CS35L41_IRQ2_RAW_STATUS2: - case CS35L41_IRQ2_RAW_STATUS3: - case CS35L41_IRQ2_RAW_STATUS4: - case CS35L41_IRQ2_FRC1: - case CS35L41_IRQ2_FRC2: - case CS35L41_IRQ2_FRC3: - case CS35L41_IRQ2_FRC4: - case CS35L41_IRQ2_EDGE1: - case CS35L41_IRQ2_EDGE4: - case CS35L41_IRQ2_POL1: - case CS35L41_IRQ2_POL2: - case CS35L41_IRQ2_POL3: - case CS35L41_IRQ2_POL4: - case CS35L41_IRQ2_DB3: - case CS35L41_GPIO_STATUS1: - case CS35L41_OTP_TRIM_1: - case CS35L41_OTP_TRIM_2: - case CS35L41_OTP_TRIM_3: - case CS35L41_OTP_TRIM_4: - case CS35L41_OTP_TRIM_5: - case CS35L41_OTP_TRIM_6: - case CS35L41_OTP_TRIM_7: - case CS35L41_OTP_TRIM_8: - case CS35L41_OTP_TRIM_9: - case CS35L41_OTP_TRIM_10: - case CS35L41_OTP_TRIM_11: - case CS35L41_OTP_TRIM_12: - case CS35L41_OTP_TRIM_13: - case CS35L41_OTP_TRIM_14: - case CS35L41_OTP_TRIM_15: - case CS35L41_OTP_TRIM_16: - case CS35L41_OTP_TRIM_17: - case CS35L41_OTP_TRIM_18: - case CS35L41_OTP_TRIM_19: - case CS35L41_OTP_TRIM_20: - case CS35L41_OTP_TRIM_21: - case CS35L41_OTP_TRIM_22: - case CS35L41_OTP_TRIM_23: - case CS35L41_OTP_TRIM_24: - case CS35L41_OTP_TRIM_25: - case CS35L41_OTP_TRIM_26: - case CS35L41_OTP_TRIM_27: - case CS35L41_OTP_TRIM_28: - case CS35L41_OTP_TRIM_29: - case CS35L41_OTP_TRIM_30: - case CS35L41_OTP_TRIM_31: - case CS35L41_OTP_TRIM_32: - case CS35L41_OTP_TRIM_33: - case CS35L41_OTP_TRIM_34: - case CS35L41_OTP_TRIM_35: - case CS35L41_OTP_TRIM_36: - case CS35L41_DSP_MBOX_1 ... CS35L41_DSP_VIRT2_MBOX_8: - case CS35L41_DSP1_XMEM_PACK_0 ... CS35L41_DSP1_XMEM_PACK_3068: - case CS35L41_DSP1_XMEM_UNPACK32_0 ... CS35L41_DSP1_XMEM_UNPACK32_2046: - case CS35L41_DSP1_XMEM_UNPACK24_0 ... CS35L41_DSP1_XMEM_UNPACK24_4093: - case CS35L41_DSP1_YMEM_PACK_0 ... CS35L41_DSP1_YMEM_PACK_1532: - case CS35L41_DSP1_YMEM_UNPACK32_0 ... CS35L41_DSP1_YMEM_UNPACK32_1022: - case CS35L41_DSP1_YMEM_UNPACK24_0 ... CS35L41_DSP1_YMEM_UNPACK24_2045: - case CS35L41_DSP1_PMEM_0 ... CS35L41_DSP1_PMEM_5114: - case CS35L41_DSP1_CCM_CORE_CTRL ... CS35L41_DSP1_WDT_STATUS: - case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31: - return true; - default: - return false; - } -} - -static const struct cs35l41_otp_packed_element_t otp_map_1[CS35L41_NUM_OTP_ELEM] = { - /* addr shift size */ - { 0x00002030, 0, 4 }, /*TRIM_OSC_FREQ_TRIM*/ - { 0x00002030, 7, 1 }, /*TRIM_OSC_TRIM_DONE*/ - { 0x0000208c, 24, 6 }, /*TST_DIGREG_VREF_TRIM*/ - { 0x00002090, 14, 4 }, /*TST_REF_TRIM*/ - { 0x00002090, 10, 4 }, /*TST_REF_TEMPCO_TRIM*/ - { 0x0000300C, 11, 4 }, /*PLL_LDOA_TST_VREF_TRIM*/ - { 0x0000394C, 23, 2 }, /*BST_ATEST_CM_VOFF*/ - { 0x00003950, 0, 7 }, /*BST_ATRIM_IADC_OFFSET*/ - { 0x00003950, 8, 7 }, /*BST_ATRIM_IADC_GAIN1*/ - { 0x00003950, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET1*/ - { 0x00003950, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN1*/ - { 0x00003954, 0, 7 }, /*BST_ATRIM_IADC_OFFSET2*/ - { 0x00003954, 8, 7 }, /*BST_ATRIM_IADC_GAIN2*/ - { 0x00003954, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET2*/ - { 0x00003954, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN2*/ - { 0x00003958, 0, 7 }, /*BST_ATRIM_IADC_OFFSET3*/ - { 0x00003958, 8, 7 }, /*BST_ATRIM_IADC_GAIN3*/ - { 0x00003958, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET3*/ - { 0x00003958, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN3*/ - { 0x0000395C, 0, 7 }, /*BST_ATRIM_IADC_OFFSET4*/ - { 0x0000395C, 8, 7 }, /*BST_ATRIM_IADC_GAIN4*/ - { 0x0000395C, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET4*/ - { 0x0000395C, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN4*/ - { 0x0000416C, 0, 8 }, /*VMON_GAIN_OTP_VAL*/ - { 0x00004160, 0, 7 }, /*VMON_OFFSET_OTP_VAL*/ - { 0x0000416C, 8, 8 }, /*IMON_GAIN_OTP_VAL*/ - { 0x00004160, 16, 10 }, /*IMON_OFFSET_OTP_VAL*/ - { 0x0000416C, 16, 12 }, /*VMON_CM_GAIN_OTP_VAL*/ - { 0x0000416C, 28, 1 }, /*VMON_CM_GAIN_SIGN_OTP_VAL*/ - { 0x00004170, 0, 6 }, /*IMON_CAL_TEMPCO_OTP_VAL*/ - { 0x00004170, 6, 1 }, /*IMON_CAL_TEMPCO_SIGN_OTP*/ - { 0x00004170, 8, 6 }, /*IMON_CAL_TEMPCO2_OTP_VAL*/ - { 0x00004170, 14, 1 }, /*IMON_CAL_TEMPCO2_DN_UPB_OTP_VAL*/ - { 0x00004170, 16, 9 }, /*IMON_CAL_TEMPCO_TBASE_OTP_VAL*/ - { 0x00004360, 0, 5 }, /*TEMP_GAIN_OTP_VAL*/ - { 0x00004360, 6, 9 }, /*TEMP_OFFSET_OTP_VAL*/ - { 0x00004448, 0, 8 }, /*VP_SARADC_OFFSET*/ - { 0x00004448, 8, 8 }, /*VP_GAIN_INDEX*/ - { 0x00004448, 16, 8 }, /*VBST_SARADC_OFFSET*/ - { 0x00004448, 24, 8 }, /*VBST_GAIN_INDEX*/ - { 0x0000444C, 0, 3 }, /*ANA_SELINVREF*/ - { 0x00006E30, 0, 5 }, /*GAIN_ERR_COEFF_0*/ - { 0x00006E30, 8, 5 }, /*GAIN_ERR_COEFF_1*/ - { 0x00006E30, 16, 5 }, /*GAIN_ERR_COEFF_2*/ - { 0x00006E30, 24, 5 }, /*GAIN_ERR_COEFF_3*/ - { 0x00006E34, 0, 5 }, /*GAIN_ERR_COEFF_4*/ - { 0x00006E34, 8, 5 }, /*GAIN_ERR_COEFF_5*/ - { 0x00006E34, 16, 5 }, /*GAIN_ERR_COEFF_6*/ - { 0x00006E34, 24, 5 }, /*GAIN_ERR_COEFF_7*/ - { 0x00006E38, 0, 5 }, /*GAIN_ERR_COEFF_8*/ - { 0x00006E38, 8, 5 }, /*GAIN_ERR_COEFF_9*/ - { 0x00006E38, 16, 5 }, /*GAIN_ERR_COEFF_10*/ - { 0x00006E38, 24, 5 }, /*GAIN_ERR_COEFF_11*/ - { 0x00006E3C, 0, 5 }, /*GAIN_ERR_COEFF_12*/ - { 0x00006E3C, 8, 5 }, /*GAIN_ERR_COEFF_13*/ - { 0x00006E3C, 16, 5 }, /*GAIN_ERR_COEFF_14*/ - { 0x00006E3C, 24, 5 }, /*GAIN_ERR_COEFF_15*/ - { 0x00006E40, 0, 5 }, /*GAIN_ERR_COEFF_16*/ - { 0x00006E40, 8, 5 }, /*GAIN_ERR_COEFF_17*/ - { 0x00006E40, 16, 5 }, /*GAIN_ERR_COEFF_18*/ - { 0x00006E40, 24, 5 }, /*GAIN_ERR_COEFF_19*/ - { 0x00006E44, 0, 5 }, /*GAIN_ERR_COEFF_20*/ - { 0x00006E48, 0, 10 }, /*VOFF_GAIN_0*/ - { 0x00006E48, 10, 10 }, /*VOFF_GAIN_1*/ - { 0x00006E48, 20, 10 }, /*VOFF_GAIN_2*/ - { 0x00006E4C, 0, 10 }, /*VOFF_GAIN_3*/ - { 0x00006E4C, 10, 10 }, /*VOFF_GAIN_4*/ - { 0x00006E4C, 20, 10 }, /*VOFF_GAIN_5*/ - { 0x00006E50, 0, 10 }, /*VOFF_GAIN_6*/ - { 0x00006E50, 10, 10 }, /*VOFF_GAIN_7*/ - { 0x00006E50, 20, 10 }, /*VOFF_GAIN_8*/ - { 0x00006E54, 0, 10 }, /*VOFF_GAIN_9*/ - { 0x00006E54, 10, 10 }, /*VOFF_GAIN_10*/ - { 0x00006E54, 20, 10 }, /*VOFF_GAIN_11*/ - { 0x00006E58, 0, 10 }, /*VOFF_GAIN_12*/ - { 0x00006E58, 10, 10 }, /*VOFF_GAIN_13*/ - { 0x00006E58, 20, 10 }, /*VOFF_GAIN_14*/ - { 0x00006E5C, 0, 10 }, /*VOFF_GAIN_15*/ - { 0x00006E5C, 10, 10 }, /*VOFF_GAIN_16*/ - { 0x00006E5C, 20, 10 }, /*VOFF_GAIN_17*/ - { 0x00006E60, 0, 10 }, /*VOFF_GAIN_18*/ - { 0x00006E60, 10, 10 }, /*VOFF_GAIN_19*/ - { 0x00006E60, 20, 10 }, /*VOFF_GAIN_20*/ - { 0x00006E64, 0, 10 }, /*VOFF_INT1*/ - { 0x00007418, 7, 5 }, /*DS_SPK_INT1_CAP_TRIM*/ - { 0x0000741C, 0, 5 }, /*DS_SPK_INT2_CAP_TRIM*/ - { 0x0000741C, 11, 4 }, /*DS_SPK_LPF_CAP_TRIM*/ - { 0x0000741C, 19, 4 }, /*DS_SPK_QUAN_CAP_TRIM*/ - { 0x00007434, 17, 1 }, /*FORCE_CAL*/ - { 0x00007434, 18, 7 }, /*CAL_OVERRIDE*/ - { 0x00007068, 0, 9 }, /*MODIX*/ - { 0x0000410C, 7, 1 }, /*VIMON_DLY_NOT_COMB*/ - { 0x0000400C, 0, 7 }, /*VIMON_DLY*/ - { 0x00000000, 0, 1 }, /*extra bit*/ - { 0x00017040, 0, 8 }, /*X_COORDINATE*/ - { 0x00017040, 8, 8 }, /*Y_COORDINATE*/ - { 0x00017040, 16, 8 }, /*WAFER_ID*/ - { 0x00017040, 24, 8 }, /*DVS*/ - { 0x00017044, 0, 24 }, /*LOT_NUMBER*/ -}; - -static const struct cs35l41_otp_packed_element_t otp_map_2[CS35L41_NUM_OTP_ELEM] = { - /* addr shift size */ - { 0x00002030, 0, 4 }, /*TRIM_OSC_FREQ_TRIM*/ - { 0x00002030, 7, 1 }, /*TRIM_OSC_TRIM_DONE*/ - { 0x0000208c, 24, 6 }, /*TST_DIGREG_VREF_TRIM*/ - { 0x00002090, 14, 4 }, /*TST_REF_TRIM*/ - { 0x00002090, 10, 4 }, /*TST_REF_TEMPCO_TRIM*/ - { 0x0000300C, 11, 4 }, /*PLL_LDOA_TST_VREF_TRIM*/ - { 0x0000394C, 23, 2 }, /*BST_ATEST_CM_VOFF*/ - { 0x00003950, 0, 7 }, /*BST_ATRIM_IADC_OFFSET*/ - { 0x00003950, 8, 7 }, /*BST_ATRIM_IADC_GAIN1*/ - { 0x00003950, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET1*/ - { 0x00003950, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN1*/ - { 0x00003954, 0, 7 }, /*BST_ATRIM_IADC_OFFSET2*/ - { 0x00003954, 8, 7 }, /*BST_ATRIM_IADC_GAIN2*/ - { 0x00003954, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET2*/ - { 0x00003954, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN2*/ - { 0x00003958, 0, 7 }, /*BST_ATRIM_IADC_OFFSET3*/ - { 0x00003958, 8, 7 }, /*BST_ATRIM_IADC_GAIN3*/ - { 0x00003958, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET3*/ - { 0x00003958, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN3*/ - { 0x0000395C, 0, 7 }, /*BST_ATRIM_IADC_OFFSET4*/ - { 0x0000395C, 8, 7 }, /*BST_ATRIM_IADC_GAIN4*/ - { 0x0000395C, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET4*/ - { 0x0000395C, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN4*/ - { 0x0000416C, 0, 8 }, /*VMON_GAIN_OTP_VAL*/ - { 0x00004160, 0, 7 }, /*VMON_OFFSET_OTP_VAL*/ - { 0x0000416C, 8, 8 }, /*IMON_GAIN_OTP_VAL*/ - { 0x00004160, 16, 10 }, /*IMON_OFFSET_OTP_VAL*/ - { 0x0000416C, 16, 12 }, /*VMON_CM_GAIN_OTP_VAL*/ - { 0x0000416C, 28, 1 }, /*VMON_CM_GAIN_SIGN_OTP_VAL*/ - { 0x00004170, 0, 6 }, /*IMON_CAL_TEMPCO_OTP_VAL*/ - { 0x00004170, 6, 1 }, /*IMON_CAL_TEMPCO_SIGN_OTP*/ - { 0x00004170, 8, 6 }, /*IMON_CAL_TEMPCO2_OTP_VAL*/ - { 0x00004170, 14, 1 }, /*IMON_CAL_TEMPCO2_DN_UPB_OTP_VAL*/ - { 0x00004170, 16, 9 }, /*IMON_CAL_TEMPCO_TBASE_OTP_VAL*/ - { 0x00004360, 0, 5 }, /*TEMP_GAIN_OTP_VAL*/ - { 0x00004360, 6, 9 }, /*TEMP_OFFSET_OTP_VAL*/ - { 0x00004448, 0, 8 }, /*VP_SARADC_OFFSET*/ - { 0x00004448, 8, 8 }, /*VP_GAIN_INDEX*/ - { 0x00004448, 16, 8 }, /*VBST_SARADC_OFFSET*/ - { 0x00004448, 24, 8 }, /*VBST_GAIN_INDEX*/ - { 0x0000444C, 0, 3 }, /*ANA_SELINVREF*/ - { 0x00006E30, 0, 5 }, /*GAIN_ERR_COEFF_0*/ - { 0x00006E30, 8, 5 }, /*GAIN_ERR_COEFF_1*/ - { 0x00006E30, 16, 5 }, /*GAIN_ERR_COEFF_2*/ - { 0x00006E30, 24, 5 }, /*GAIN_ERR_COEFF_3*/ - { 0x00006E34, 0, 5 }, /*GAIN_ERR_COEFF_4*/ - { 0x00006E34, 8, 5 }, /*GAIN_ERR_COEFF_5*/ - { 0x00006E34, 16, 5 }, /*GAIN_ERR_COEFF_6*/ - { 0x00006E34, 24, 5 }, /*GAIN_ERR_COEFF_7*/ - { 0x00006E38, 0, 5 }, /*GAIN_ERR_COEFF_8*/ - { 0x00006E38, 8, 5 }, /*GAIN_ERR_COEFF_9*/ - { 0x00006E38, 16, 5 }, /*GAIN_ERR_COEFF_10*/ - { 0x00006E38, 24, 5 }, /*GAIN_ERR_COEFF_11*/ - { 0x00006E3C, 0, 5 }, /*GAIN_ERR_COEFF_12*/ - { 0x00006E3C, 8, 5 }, /*GAIN_ERR_COEFF_13*/ - { 0x00006E3C, 16, 5 }, /*GAIN_ERR_COEFF_14*/ - { 0x00006E3C, 24, 5 }, /*GAIN_ERR_COEFF_15*/ - { 0x00006E40, 0, 5 }, /*GAIN_ERR_COEFF_16*/ - { 0x00006E40, 8, 5 }, /*GAIN_ERR_COEFF_17*/ - { 0x00006E40, 16, 5 }, /*GAIN_ERR_COEFF_18*/ - { 0x00006E40, 24, 5 }, /*GAIN_ERR_COEFF_19*/ - { 0x00006E44, 0, 5 }, /*GAIN_ERR_COEFF_20*/ - { 0x00006E48, 0, 10 }, /*VOFF_GAIN_0*/ - { 0x00006E48, 10, 10 }, /*VOFF_GAIN_1*/ - { 0x00006E48, 20, 10 }, /*VOFF_GAIN_2*/ - { 0x00006E4C, 0, 10 }, /*VOFF_GAIN_3*/ - { 0x00006E4C, 10, 10 }, /*VOFF_GAIN_4*/ - { 0x00006E4C, 20, 10 }, /*VOFF_GAIN_5*/ - { 0x00006E50, 0, 10 }, /*VOFF_GAIN_6*/ - { 0x00006E50, 10, 10 }, /*VOFF_GAIN_7*/ - { 0x00006E50, 20, 10 }, /*VOFF_GAIN_8*/ - { 0x00006E54, 0, 10 }, /*VOFF_GAIN_9*/ - { 0x00006E54, 10, 10 }, /*VOFF_GAIN_10*/ - { 0x00006E54, 20, 10 }, /*VOFF_GAIN_11*/ - { 0x00006E58, 0, 10 }, /*VOFF_GAIN_12*/ - { 0x00006E58, 10, 10 }, /*VOFF_GAIN_13*/ - { 0x00006E58, 20, 10 }, /*VOFF_GAIN_14*/ - { 0x00006E5C, 0, 10 }, /*VOFF_GAIN_15*/ - { 0x00006E5C, 10, 10 }, /*VOFF_GAIN_16*/ - { 0x00006E5C, 20, 10 }, /*VOFF_GAIN_17*/ - { 0x00006E60, 0, 10 }, /*VOFF_GAIN_18*/ - { 0x00006E60, 10, 10 }, /*VOFF_GAIN_19*/ - { 0x00006E60, 20, 10 }, /*VOFF_GAIN_20*/ - { 0x00006E64, 0, 10 }, /*VOFF_INT1*/ - { 0x00007418, 7, 5 }, /*DS_SPK_INT1_CAP_TRIM*/ - { 0x0000741C, 0, 5 }, /*DS_SPK_INT2_CAP_TRIM*/ - { 0x0000741C, 11, 4 }, /*DS_SPK_LPF_CAP_TRIM*/ - { 0x0000741C, 19, 4 }, /*DS_SPK_QUAN_CAP_TRIM*/ - { 0x00007434, 17, 1 }, /*FORCE_CAL*/ - { 0x00007434, 18, 7 }, /*CAL_OVERRIDE*/ - { 0x00007068, 0, 9 }, /*MODIX*/ - { 0x0000410C, 7, 1 }, /*VIMON_DLY_NOT_COMB*/ - { 0x0000400C, 0, 7 }, /*VIMON_DLY*/ - { 0x00004000, 11, 1 }, /*VMON_POL*/ - { 0x00017040, 0, 8 }, /*X_COORDINATE*/ - { 0x00017040, 8, 8 }, /*Y_COORDINATE*/ - { 0x00017040, 16, 8 }, /*WAFER_ID*/ - { 0x00017040, 24, 8 }, /*DVS*/ - { 0x00017044, 0, 24 }, /*LOT_NUMBER*/ -}; - -const struct cs35l41_otp_map_element_t cs35l41_otp_map_map[CS35L41_NUM_OTP_MAPS] = { - { - .id = 0x01, - .map = otp_map_1, - .num_elements = CS35L41_NUM_OTP_ELEM, - .bit_offset = 16, - .word_offset = 2, - }, - { - .id = 0x02, - .map = otp_map_2, - .num_elements = CS35L41_NUM_OTP_ELEM, - .bit_offset = 16, - .word_offset = 2, - }, - { - .id = 0x03, - .map = otp_map_2, - .num_elements = CS35L41_NUM_OTP_ELEM, - .bit_offset = 16, - .word_offset = 2, - }, - { - .id = 0x06, - .map = otp_map_2, - .num_elements = CS35L41_NUM_OTP_ELEM, - .bit_offset = 16, - .word_offset = 2, - }, - { - .id = 0x08, - .map = otp_map_1, - .num_elements = CS35L41_NUM_OTP_ELEM, - .bit_offset = 16, - .word_offset = 2, - }, -}; - -struct regmap_config cs35l41_regmap_i2c = { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = CS35L41_REGSTRIDE, - .reg_format_endian = REGMAP_ENDIAN_BIG, - .val_format_endian = REGMAP_ENDIAN_BIG, - .max_register = CS35L41_LASTREG, - .reg_defaults = cs35l41_reg, - .num_reg_defaults = ARRAY_SIZE(cs35l41_reg), - .volatile_reg = cs35l41_volatile_reg, - .readable_reg = cs35l41_readable_reg, - .precious_reg = cs35l41_precious_reg, - .cache_type = REGCACHE_RBTREE, -}; -EXPORT_SYMBOL_GPL(cs35l41_regmap_i2c); - -struct regmap_config cs35l41_regmap_spi = { - .reg_bits = 32, - .val_bits = 32, - .pad_bits = 16, - .reg_stride = CS35L41_REGSTRIDE, - .reg_format_endian = REGMAP_ENDIAN_BIG, - .val_format_endian = REGMAP_ENDIAN_BIG, - .max_register = CS35L41_LASTREG, - .reg_defaults = cs35l41_reg, - .num_reg_defaults = ARRAY_SIZE(cs35l41_reg), - .volatile_reg = cs35l41_volatile_reg, - .readable_reg = cs35l41_readable_reg, - .precious_reg = cs35l41_precious_reg, - .cache_type = REGCACHE_RBTREE, -}; -EXPORT_SYMBOL_GPL(cs35l41_regmap_spi); diff --git a/sound/soc/codecs/cs35l41.h b/sound/soc/codecs/cs35l41.h index c7c45f19754b..26a08d58a8c3 100644 --- a/sound/soc/codecs/cs35l41.h +++ b/sound/soc/codecs/cs35l41.h @@ -11,7 +11,6 @@ #define __CS35L41_H__ #include -#include #include #include #include @@ -19,742 +18,9 @@ #include "wm_adsp.h" -#define CS35L41_FIRSTREG 0x00000000 -#define CS35L41_LASTREG 0x03804FE8 -#define CS35L41_DEVID 0x00000000 -#define CS35L41_REVID 0x00000004 -#define CS35L41_FABID 0x00000008 -#define CS35L41_RELID 0x0000000C -#define CS35L41_OTPID 0x00000010 -#define CS35L41_SFT_RESET 0x00000020 -#define CS35L41_TEST_KEY_CTL 0x00000040 -#define CS35L41_USER_KEY_CTL 0x00000044 -#define CS35L41_OTP_MEM0 0x00000400 -#define CS35L41_OTP_MEM31 0x0000047C -#define CS35L41_OTP_CTRL0 0x00000500 -#define CS35L41_OTP_CTRL1 0x00000504 -#define CS35L41_OTP_CTRL3 0x00000508 -#define CS35L41_OTP_CTRL4 0x0000050C -#define CS35L41_OTP_CTRL5 0x00000510 -#define CS35L41_OTP_CTRL6 0x00000514 -#define CS35L41_OTP_CTRL7 0x00000518 -#define CS35L41_OTP_CTRL8 0x0000051C -#define CS35L41_PWR_CTRL1 0x00002014 -#define CS35L41_PWR_CTRL2 0x00002018 -#define CS35L41_PWR_CTRL3 0x0000201C -#define CS35L41_CTRL_OVRRIDE 0x00002020 -#define CS35L41_AMP_OUT_MUTE 0x00002024 -#define CS35L41_PROTECT_REL_ERR_IGN 0x00002034 -#define CS35L41_GPIO_PAD_CONTROL 0x0000242C -#define CS35L41_JTAG_CONTROL 0x00002438 -#define CS35L41_PLL_CLK_CTRL 0x00002C04 -#define CS35L41_DSP_CLK_CTRL 0x00002C08 -#define CS35L41_GLOBAL_CLK_CTRL 0x00002C0C -#define CS35L41_DATA_FS_SEL 0x00002C10 -#define CS35L41_TST_FS_MON0 0x00002D10 -#define CS35L41_MDSYNC_EN 0x00003400 -#define CS35L41_MDSYNC_TX_ID 0x00003408 -#define CS35L41_MDSYNC_PWR_CTRL 0x0000340C -#define CS35L41_MDSYNC_DATA_TX 0x00003410 -#define CS35L41_MDSYNC_TX_STATUS 0x00003414 -#define CS35L41_MDSYNC_DATA_RX 0x0000341C -#define CS35L41_MDSYNC_RX_STATUS 0x00003420 -#define CS35L41_MDSYNC_ERR_STATUS 0x00003424 -#define CS35L41_MDSYNC_SYNC_PTE2 0x00003528 -#define CS35L41_MDSYNC_SYNC_PTE3 0x0000352C -#define CS35L41_MDSYNC_SYNC_MSM_STATUS 0x0000353C -#define CS35L41_BSTCVRT_VCTRL1 0x00003800 -#define CS35L41_BSTCVRT_VCTRL2 0x00003804 -#define CS35L41_BSTCVRT_PEAK_CUR 0x00003808 -#define CS35L41_BSTCVRT_SFT_RAMP 0x0000380C -#define CS35L41_BSTCVRT_COEFF 0x00003810 -#define CS35L41_BSTCVRT_SLOPE_LBST 0x00003814 -#define CS35L41_BSTCVRT_SW_FREQ 0x00003818 -#define CS35L41_BSTCVRT_DCM_CTRL 0x0000381C -#define CS35L41_BSTCVRT_DCM_MODE_FORCE 0x00003820 -#define CS35L41_BSTCVRT_OVERVOLT_CTRL 0x00003830 -#define CS35L41_VI_VOL_POL 0x00004000 -#define CS35L41_VIMON_SPKMON_RESYNC 0x00004100 -#define CS35L41_DTEMP_WARN_THLD 0x00004220 -#define CS35L41_DTEMP_CFG 0x00004224 -#define CS35L41_DTEMP_EN 0x00004308 -#define CS35L41_VPVBST_FS_SEL 0x00004400 -#define CS35L41_SP_ENABLES 0x00004800 -#define CS35L41_SP_RATE_CTRL 0x00004804 -#define CS35L41_SP_FORMAT 0x00004808 -#define CS35L41_SP_HIZ_CTRL 0x0000480C -#define CS35L41_SP_FRAME_TX_SLOT 0x00004810 -#define CS35L41_SP_FRAME_RX_SLOT 0x00004820 -#define CS35L41_SP_TX_WL 0x00004830 -#define CS35L41_SP_RX_WL 0x00004840 -#define CS35L41_ASP_CONTROL4 0x00004854 -#define CS35L41_DAC_PCM1_SRC 0x00004C00 -#define CS35L41_ASP_TX1_SRC 0x00004C20 -#define CS35L41_ASP_TX2_SRC 0x00004C24 -#define CS35L41_ASP_TX3_SRC 0x00004C28 -#define CS35L41_ASP_TX4_SRC 0x00004C2C -#define CS35L41_DSP1_RX1_SRC 0x00004C40 -#define CS35L41_DSP1_RX2_SRC 0x00004C44 -#define CS35L41_DSP1_RX3_SRC 0x00004C48 -#define CS35L41_DSP1_RX4_SRC 0x00004C4C -#define CS35L41_DSP1_RX5_SRC 0x00004C50 -#define CS35L41_DSP1_RX6_SRC 0x00004C54 -#define CS35L41_DSP1_RX7_SRC 0x00004C58 -#define CS35L41_DSP1_RX8_SRC 0x00004C5C -#define CS35L41_NGATE1_SRC 0x00004C60 -#define CS35L41_NGATE2_SRC 0x00004C64 -#define CS35L41_AMP_DIG_VOL_CTRL 0x00006000 -#define CS35L41_VPBR_CFG 0x00006404 -#define CS35L41_VBBR_CFG 0x00006408 -#define CS35L41_VPBR_STATUS 0x0000640C -#define CS35L41_VBBR_STATUS 0x00006410 -#define CS35L41_OVERTEMP_CFG 0x00006414 -#define CS35L41_AMP_ERR_VOL 0x00006418 -#define CS35L41_VOL_STATUS_TO_DSP 0x00006450 -#define CS35L41_CLASSH_CFG 0x00006800 -#define CS35L41_WKFET_CFG 0x00006804 -#define CS35L41_NG_CFG 0x00006808 -#define CS35L41_AMP_GAIN_CTRL 0x00006C04 -#define CS35L41_DAC_MSM_CFG 0x00007400 -#define CS35L41_IRQ1_CFG 0x00010000 -#define CS35L41_IRQ1_STATUS 0x00010004 -#define CS35L41_IRQ1_STATUS1 0x00010010 -#define CS35L41_IRQ1_STATUS2 0x00010014 -#define CS35L41_IRQ1_STATUS3 0x00010018 -#define CS35L41_IRQ1_STATUS4 0x0001001C -#define CS35L41_IRQ1_RAW_STATUS1 0x00010090 -#define CS35L41_IRQ1_RAW_STATUS2 0x00010094 -#define CS35L41_IRQ1_RAW_STATUS3 0x00010098 -#define CS35L41_IRQ1_RAW_STATUS4 0x0001009C -#define CS35L41_IRQ1_MASK1 0x00010110 -#define CS35L41_IRQ1_MASK2 0x00010114 -#define CS35L41_IRQ1_MASK3 0x00010118 -#define CS35L41_IRQ1_MASK4 0x0001011C -#define CS35L41_IRQ1_FRC1 0x00010190 -#define CS35L41_IRQ1_FRC2 0x00010194 -#define CS35L41_IRQ1_FRC3 0x00010198 -#define CS35L41_IRQ1_FRC4 0x0001019C -#define CS35L41_IRQ1_EDGE1 0x00010210 -#define CS35L41_IRQ1_EDGE4 0x0001021C -#define CS35L41_IRQ1_POL1 0x00010290 -#define CS35L41_IRQ1_POL2 0x00010294 -#define CS35L41_IRQ1_POL3 0x00010298 -#define CS35L41_IRQ1_POL4 0x0001029C -#define CS35L41_IRQ1_DB3 0x00010318 -#define CS35L41_IRQ2_CFG 0x00010800 -#define CS35L41_IRQ2_STATUS 0x00010804 -#define CS35L41_IRQ2_STATUS1 0x00010810 -#define CS35L41_IRQ2_STATUS2 0x00010814 -#define CS35L41_IRQ2_STATUS3 0x00010818 -#define CS35L41_IRQ2_STATUS4 0x0001081C -#define CS35L41_IRQ2_RAW_STATUS1 0x00010890 -#define CS35L41_IRQ2_RAW_STATUS2 0x00010894 -#define CS35L41_IRQ2_RAW_STATUS3 0x00010898 -#define CS35L41_IRQ2_RAW_STATUS4 0x0001089C -#define CS35L41_IRQ2_MASK1 0x00010910 -#define CS35L41_IRQ2_MASK2 0x00010914 -#define CS35L41_IRQ2_MASK3 0x00010918 -#define CS35L41_IRQ2_MASK4 0x0001091C -#define CS35L41_IRQ2_FRC1 0x00010990 -#define CS35L41_IRQ2_FRC2 0x00010994 -#define CS35L41_IRQ2_FRC3 0x00010998 -#define CS35L41_IRQ2_FRC4 0x0001099C -#define CS35L41_IRQ2_EDGE1 0x00010A10 -#define CS35L41_IRQ2_EDGE4 0x00010A1C -#define CS35L41_IRQ2_POL1 0x00010A90 -#define CS35L41_IRQ2_POL2 0x00010A94 -#define CS35L41_IRQ2_POL3 0x00010A98 -#define CS35L41_IRQ2_POL4 0x00010A9C -#define CS35L41_IRQ2_DB3 0x00010B18 -#define CS35L41_GPIO_STATUS1 0x00011000 -#define CS35L41_GPIO1_CTRL1 0x00011008 -#define CS35L41_GPIO2_CTRL1 0x0001100C -#define CS35L41_MIXER_NGATE_CFG 0x00012000 -#define CS35L41_MIXER_NGATE_CH1_CFG 0x00012004 -#define CS35L41_MIXER_NGATE_CH2_CFG 0x00012008 -#define CS35L41_DSP_MBOX_1 0x00013000 -#define CS35L41_DSP_MBOX_2 0x00013004 -#define CS35L41_DSP_MBOX_3 0x00013008 -#define CS35L41_DSP_MBOX_4 0x0001300C -#define CS35L41_DSP_MBOX_5 0x00013010 -#define CS35L41_DSP_MBOX_6 0x00013014 -#define CS35L41_DSP_MBOX_7 0x00013018 -#define CS35L41_DSP_MBOX_8 0x0001301C -#define CS35L41_DSP_VIRT1_MBOX_1 0x00013020 -#define CS35L41_DSP_VIRT1_MBOX_2 0x00013024 -#define CS35L41_DSP_VIRT1_MBOX_3 0x00013028 -#define CS35L41_DSP_VIRT1_MBOX_4 0x0001302C -#define CS35L41_DSP_VIRT1_MBOX_5 0x00013030 -#define CS35L41_DSP_VIRT1_MBOX_6 0x00013034 -#define CS35L41_DSP_VIRT1_MBOX_7 0x00013038 -#define CS35L41_DSP_VIRT1_MBOX_8 0x0001303C -#define CS35L41_DSP_VIRT2_MBOX_1 0x00013040 -#define CS35L41_DSP_VIRT2_MBOX_2 0x00013044 -#define CS35L41_DSP_VIRT2_MBOX_3 0x00013048 -#define CS35L41_DSP_VIRT2_MBOX_4 0x0001304C -#define CS35L41_DSP_VIRT2_MBOX_5 0x00013050 -#define CS35L41_DSP_VIRT2_MBOX_6 0x00013054 -#define CS35L41_DSP_VIRT2_MBOX_7 0x00013058 -#define CS35L41_DSP_VIRT2_MBOX_8 0x0001305C -#define CS35L41_CLOCK_DETECT_1 0x00014000 -#define CS35L41_TIMER1_CONTROL 0x00015000 -#define CS35L41_TIMER1_COUNT_PRESET 0x00015004 -#define CS35L41_TIMER1_START_STOP 0x0001500C -#define CS35L41_TIMER1_STATUS 0x00015010 -#define CS35L41_TIMER1_COUNT_READBACK 0x00015014 -#define CS35L41_TIMER1_DSP_CLK_CFG 0x00015018 -#define CS35L41_TIMER1_DSP_CLK_STATUS 0x0001501C -#define CS35L41_TIMER2_CONTROL 0x00015100 -#define CS35L41_TIMER2_COUNT_PRESET 0x00015104 -#define CS35L41_TIMER2_START_STOP 0x0001510C -#define CS35L41_TIMER2_STATUS 0x00015110 -#define CS35L41_TIMER2_COUNT_READBACK 0x00015114 -#define CS35L41_TIMER2_DSP_CLK_CFG 0x00015118 -#define CS35L41_TIMER2_DSP_CLK_STATUS 0x0001511C -#define CS35L41_DFT_JTAG_CONTROL 0x00016000 -#define CS35L41_DIE_STS1 0x00017040 -#define CS35L41_DIE_STS2 0x00017044 -#define CS35L41_TEMP_CAL1 0x00017048 -#define CS35L41_TEMP_CAL2 0x0001704C -#define CS35L41_DSP1_XMEM_PACK_0 0x02000000 -#define CS35L41_DSP1_XMEM_PACK_3068 0x02002FF0 -#define CS35L41_DSP1_XMEM_UNPACK32_0 0x02400000 -#define CS35L41_DSP1_XMEM_UNPACK32_2046 0x02401FF8 -#define CS35L41_DSP1_TIMESTAMP_COUNT 0x025C0800 -#define CS35L41_DSP1_SYS_ID 0x025E0000 -#define CS35L41_DSP1_SYS_VERSION 0x025E0004 -#define CS35L41_DSP1_SYS_CORE_ID 0x025E0008 -#define CS35L41_DSP1_SYS_AHB_ADDR 0x025E000C -#define CS35L41_DSP1_SYS_XSRAM_SIZE 0x025E0010 -#define CS35L41_DSP1_SYS_YSRAM_SIZE 0x025E0018 -#define CS35L41_DSP1_SYS_PSRAM_SIZE 0x025E0020 -#define CS35L41_DSP1_SYS_PM_BOOT_SIZE 0x025E0028 -#define CS35L41_DSP1_SYS_FEATURES 0x025E002C -#define CS35L41_DSP1_SYS_FIR_FILTERS 0x025E0030 -#define CS35L41_DSP1_SYS_LMS_FILTERS 0x025E0034 -#define CS35L41_DSP1_SYS_XM_BANK_SIZE 0x025E0038 -#define CS35L41_DSP1_SYS_YM_BANK_SIZE 0x025E003C -#define CS35L41_DSP1_SYS_PM_BANK_SIZE 0x025E0040 -#define CS35L41_DSP1_AHBM_WIN0_CTRL0 0x025E2000 -#define CS35L41_DSP1_AHBM_WIN0_CTRL1 0x025E2004 -#define CS35L41_DSP1_AHBM_WIN1_CTRL0 0x025E2008 -#define CS35L41_DSP1_AHBM_WIN1_CTRL1 0x025E200C -#define CS35L41_DSP1_AHBM_WIN2_CTRL0 0x025E2010 -#define CS35L41_DSP1_AHBM_WIN2_CTRL1 0x025E2014 -#define CS35L41_DSP1_AHBM_WIN3_CTRL0 0x025E2018 -#define CS35L41_DSP1_AHBM_WIN3_CTRL1 0x025E201C -#define CS35L41_DSP1_AHBM_WIN4_CTRL0 0x025E2020 -#define CS35L41_DSP1_AHBM_WIN4_CTRL1 0x025E2024 -#define CS35L41_DSP1_AHBM_WIN5_CTRL0 0x025E2028 -#define CS35L41_DSP1_AHBM_WIN5_CTRL1 0x025E202C -#define CS35L41_DSP1_AHBM_WIN6_CTRL0 0x025E2030 -#define CS35L41_DSP1_AHBM_WIN6_CTRL1 0x025E2034 -#define CS35L41_DSP1_AHBM_WIN7_CTRL0 0x025E2038 -#define CS35L41_DSP1_AHBM_WIN7_CTRL1 0x025E203C -#define CS35L41_DSP1_AHBM_WIN_DBG_CTRL0 0x025E2040 -#define CS35L41_DSP1_AHBM_WIN_DBG_CTRL1 0x025E2044 -#define CS35L41_DSP1_XMEM_UNPACK24_0 0x02800000 -#define CS35L41_DSP1_XMEM_UNPACK24_4093 0x02803FF4 -#define CS35L41_DSP1_CTRL_BASE 0x02B80000 -#define CS35L41_DSP1_CORE_SOFT_RESET 0x02B80010 -#define CS35L41_DSP1_DEBUG 0x02B80040 -#define CS35L41_DSP1_TIMER_CTRL 0x02B80048 -#define CS35L41_DSP1_STREAM_ARB_CTRL 0x02B80050 -#define CS35L41_DSP1_RX1_RATE 0x02B80080 -#define CS35L41_DSP1_RX2_RATE 0x02B80088 -#define CS35L41_DSP1_RX3_RATE 0x02B80090 -#define CS35L41_DSP1_RX4_RATE 0x02B80098 -#define CS35L41_DSP1_RX5_RATE 0x02B800A0 -#define CS35L41_DSP1_RX6_RATE 0x02B800A8 -#define CS35L41_DSP1_RX7_RATE 0x02B800B0 -#define CS35L41_DSP1_RX8_RATE 0x02B800B8 -#define CS35L41_DSP1_TX1_RATE 0x02B80280 -#define CS35L41_DSP1_TX2_RATE 0x02B80288 -#define CS35L41_DSP1_TX3_RATE 0x02B80290 -#define CS35L41_DSP1_TX4_RATE 0x02B80298 -#define CS35L41_DSP1_TX5_RATE 0x02B802A0 -#define CS35L41_DSP1_TX6_RATE 0x02B802A8 -#define CS35L41_DSP1_TX7_RATE 0x02B802B0 -#define CS35L41_DSP1_TX8_RATE 0x02B802B8 -#define CS35L41_DSP1_NMI_CTRL1 0x02B80480 -#define CS35L41_DSP1_NMI_CTRL2 0x02B80488 -#define CS35L41_DSP1_NMI_CTRL3 0x02B80490 -#define CS35L41_DSP1_NMI_CTRL4 0x02B80498 -#define CS35L41_DSP1_NMI_CTRL5 0x02B804A0 -#define CS35L41_DSP1_NMI_CTRL6 0x02B804A8 -#define CS35L41_DSP1_NMI_CTRL7 0x02B804B0 -#define CS35L41_DSP1_NMI_CTRL8 0x02B804B8 -#define CS35L41_DSP1_RESUME_CTRL 0x02B80500 -#define CS35L41_DSP1_IRQ1_CTRL 0x02B80508 -#define CS35L41_DSP1_IRQ2_CTRL 0x02B80510 -#define CS35L41_DSP1_IRQ3_CTRL 0x02B80518 -#define CS35L41_DSP1_IRQ4_CTRL 0x02B80520 -#define CS35L41_DSP1_IRQ5_CTRL 0x02B80528 -#define CS35L41_DSP1_IRQ6_CTRL 0x02B80530 -#define CS35L41_DSP1_IRQ7_CTRL 0x02B80538 -#define CS35L41_DSP1_IRQ8_CTRL 0x02B80540 -#define CS35L41_DSP1_IRQ9_CTRL 0x02B80548 -#define CS35L41_DSP1_IRQ10_CTRL 0x02B80550 -#define CS35L41_DSP1_IRQ11_CTRL 0x02B80558 -#define CS35L41_DSP1_IRQ12_CTRL 0x02B80560 -#define CS35L41_DSP1_IRQ13_CTRL 0x02B80568 -#define CS35L41_DSP1_IRQ14_CTRL 0x02B80570 -#define CS35L41_DSP1_IRQ15_CTRL 0x02B80578 -#define CS35L41_DSP1_IRQ16_CTRL 0x02B80580 -#define CS35L41_DSP1_IRQ17_CTRL 0x02B80588 -#define CS35L41_DSP1_IRQ18_CTRL 0x02B80590 -#define CS35L41_DSP1_IRQ19_CTRL 0x02B80598 -#define CS35L41_DSP1_IRQ20_CTRL 0x02B805A0 -#define CS35L41_DSP1_IRQ21_CTRL 0x02B805A8 -#define CS35L41_DSP1_IRQ22_CTRL 0x02B805B0 -#define CS35L41_DSP1_IRQ23_CTRL 0x02B805B8 -#define CS35L41_DSP1_SCRATCH1 0x02B805C0 -#define CS35L41_DSP1_SCRATCH2 0x02B805C8 -#define CS35L41_DSP1_SCRATCH3 0x02B805D0 -#define CS35L41_DSP1_SCRATCH4 0x02B805D8 -#define CS35L41_DSP1_CCM_CORE_CTRL 0x02BC1000 -#define CS35L41_DSP1_CCM_CLK_OVERRIDE 0x02BC1008 -#define CS35L41_DSP1_XM_MSTR_EN 0x02BC2000 -#define CS35L41_DSP1_XM_CORE_PRI 0x02BC2008 -#define CS35L41_DSP1_XM_AHB_PACK_PL_PRI 0x02BC2010 -#define CS35L41_DSP1_XM_AHB_UP_PL_PRI 0x02BC2018 -#define CS35L41_DSP1_XM_ACCEL_PL0_PRI 0x02BC2020 -#define CS35L41_DSP1_XM_NPL0_PRI 0x02BC2078 -#define CS35L41_DSP1_YM_MSTR_EN 0x02BC20C0 -#define CS35L41_DSP1_YM_CORE_PRI 0x02BC20C8 -#define CS35L41_DSP1_YM_AHB_PACK_PL_PRI 0x02BC20D0 -#define CS35L41_DSP1_YM_AHB_UP_PL_PRI 0x02BC20D8 -#define CS35L41_DSP1_YM_ACCEL_PL0_PRI 0x02BC20E0 -#define CS35L41_DSP1_YM_NPL0_PRI 0x02BC2138 -#define CS35L41_DSP1_PM_MSTR_EN 0x02BC2180 -#define CS35L41_DSP1_PM_PATCH0_ADDR 0x02BC2188 -#define CS35L41_DSP1_PM_PATCH0_EN 0x02BC218C -#define CS35L41_DSP1_PM_PATCH0_DATA_LO 0x02BC2190 -#define CS35L41_DSP1_PM_PATCH0_DATA_HI 0x02BC2194 -#define CS35L41_DSP1_PM_PATCH1_ADDR 0x02BC2198 -#define CS35L41_DSP1_PM_PATCH1_EN 0x02BC219C -#define CS35L41_DSP1_PM_PATCH1_DATA_LO 0x02BC21A0 -#define CS35L41_DSP1_PM_PATCH1_DATA_HI 0x02BC21A4 -#define CS35L41_DSP1_PM_PATCH2_ADDR 0x02BC21A8 -#define CS35L41_DSP1_PM_PATCH2_EN 0x02BC21AC -#define CS35L41_DSP1_PM_PATCH2_DATA_LO 0x02BC21B0 -#define CS35L41_DSP1_PM_PATCH2_DATA_HI 0x02BC21B4 -#define CS35L41_DSP1_PM_PATCH3_ADDR 0x02BC21B8 -#define CS35L41_DSP1_PM_PATCH3_EN 0x02BC21BC -#define CS35L41_DSP1_PM_PATCH3_DATA_LO 0x02BC21C0 -#define CS35L41_DSP1_PM_PATCH3_DATA_HI 0x02BC21C4 -#define CS35L41_DSP1_PM_PATCH4_ADDR 0x02BC21C8 -#define CS35L41_DSP1_PM_PATCH4_EN 0x02BC21CC -#define CS35L41_DSP1_PM_PATCH4_DATA_LO 0x02BC21D0 -#define CS35L41_DSP1_PM_PATCH4_DATA_HI 0x02BC21D4 -#define CS35L41_DSP1_PM_PATCH5_ADDR 0x02BC21D8 -#define CS35L41_DSP1_PM_PATCH5_EN 0x02BC21DC -#define CS35L41_DSP1_PM_PATCH5_DATA_LO 0x02BC21E0 -#define CS35L41_DSP1_PM_PATCH5_DATA_HI 0x02BC21E4 -#define CS35L41_DSP1_PM_PATCH6_ADDR 0x02BC21E8 -#define CS35L41_DSP1_PM_PATCH6_EN 0x02BC21EC -#define CS35L41_DSP1_PM_PATCH6_DATA_LO 0x02BC21F0 -#define CS35L41_DSP1_PM_PATCH6_DATA_HI 0x02BC21F4 -#define CS35L41_DSP1_PM_PATCH7_ADDR 0x02BC21F8 -#define CS35L41_DSP1_PM_PATCH7_EN 0x02BC21FC -#define CS35L41_DSP1_PM_PATCH7_DATA_LO 0x02BC2200 -#define CS35L41_DSP1_PM_PATCH7_DATA_HI 0x02BC2204 -#define CS35L41_DSP1_MPU_XM_ACCESS0 0x02BC3000 -#define CS35L41_DSP1_MPU_YM_ACCESS0 0x02BC3004 -#define CS35L41_DSP1_MPU_WNDW_ACCESS0 0x02BC3008 -#define CS35L41_DSP1_MPU_XREG_ACCESS0 0x02BC300C -#define CS35L41_DSP1_MPU_YREG_ACCESS0 0x02BC3014 -#define CS35L41_DSP1_MPU_XM_ACCESS1 0x02BC3018 -#define CS35L41_DSP1_MPU_YM_ACCESS1 0x02BC301C -#define CS35L41_DSP1_MPU_WNDW_ACCESS1 0x02BC3020 -#define CS35L41_DSP1_MPU_XREG_ACCESS1 0x02BC3024 -#define CS35L41_DSP1_MPU_YREG_ACCESS1 0x02BC302C -#define CS35L41_DSP1_MPU_XM_ACCESS2 0x02BC3030 -#define CS35L41_DSP1_MPU_YM_ACCESS2 0x02BC3034 -#define CS35L41_DSP1_MPU_WNDW_ACCESS2 0x02BC3038 -#define CS35L41_DSP1_MPU_XREG_ACCESS2 0x02BC303C -#define CS35L41_DSP1_MPU_YREG_ACCESS2 0x02BC3044 -#define CS35L41_DSP1_MPU_XM_ACCESS3 0x02BC3048 -#define CS35L41_DSP1_MPU_YM_ACCESS3 0x02BC304C -#define CS35L41_DSP1_MPU_WNDW_ACCESS3 0x02BC3050 -#define CS35L41_DSP1_MPU_XREG_ACCESS3 0x02BC3054 -#define CS35L41_DSP1_MPU_YREG_ACCESS3 0x02BC305C -#define CS35L41_DSP1_MPU_XM_VIO_ADDR 0x02BC3100 -#define CS35L41_DSP1_MPU_XM_VIO_STATUS 0x02BC3104 -#define CS35L41_DSP1_MPU_YM_VIO_ADDR 0x02BC3108 -#define CS35L41_DSP1_MPU_YM_VIO_STATUS 0x02BC310C -#define CS35L41_DSP1_MPU_PM_VIO_ADDR 0x02BC3110 -#define CS35L41_DSP1_MPU_PM_VIO_STATUS 0x02BC3114 -#define CS35L41_DSP1_MPU_LOCK_CONFIG 0x02BC3140 -#define CS35L41_DSP1_MPU_WDT_RST_CTRL 0x02BC3180 -#define CS35L41_DSP1_STRMARB_MSTR0_CFG0 0x02BC5000 -#define CS35L41_DSP1_STRMARB_MSTR0_CFG1 0x02BC5004 -#define CS35L41_DSP1_STRMARB_MSTR0_CFG2 0x02BC5008 -#define CS35L41_DSP1_STRMARB_MSTR1_CFG0 0x02BC5010 -#define CS35L41_DSP1_STRMARB_MSTR1_CFG1 0x02BC5014 -#define CS35L41_DSP1_STRMARB_MSTR1_CFG2 0x02BC5018 -#define CS35L41_DSP1_STRMARB_MSTR2_CFG0 0x02BC5020 -#define CS35L41_DSP1_STRMARB_MSTR2_CFG1 0x02BC5024 -#define CS35L41_DSP1_STRMARB_MSTR2_CFG2 0x02BC5028 -#define CS35L41_DSP1_STRMARB_MSTR3_CFG0 0x02BC5030 -#define CS35L41_DSP1_STRMARB_MSTR3_CFG1 0x02BC5034 -#define CS35L41_DSP1_STRMARB_MSTR3_CFG2 0x02BC5038 -#define CS35L41_DSP1_STRMARB_MSTR4_CFG0 0x02BC5040 -#define CS35L41_DSP1_STRMARB_MSTR4_CFG1 0x02BC5044 -#define CS35L41_DSP1_STRMARB_MSTR4_CFG2 0x02BC5048 -#define CS35L41_DSP1_STRMARB_MSTR5_CFG0 0x02BC5050 -#define CS35L41_DSP1_STRMARB_MSTR5_CFG1 0x02BC5054 -#define CS35L41_DSP1_STRMARB_MSTR5_CFG2 0x02BC5058 -#define CS35L41_DSP1_STRMARB_MSTR6_CFG0 0x02BC5060 -#define CS35L41_DSP1_STRMARB_MSTR6_CFG1 0x02BC5064 -#define CS35L41_DSP1_STRMARB_MSTR6_CFG2 0x02BC5068 -#define CS35L41_DSP1_STRMARB_MSTR7_CFG0 0x02BC5070 -#define CS35L41_DSP1_STRMARB_MSTR7_CFG1 0x02BC5074 -#define CS35L41_DSP1_STRMARB_MSTR7_CFG2 0x02BC5078 -#define CS35L41_DSP1_STRMARB_TX0_CFG0 0x02BC5200 -#define CS35L41_DSP1_STRMARB_TX0_CFG1 0x02BC5204 -#define CS35L41_DSP1_STRMARB_TX1_CFG0 0x02BC5208 -#define CS35L41_DSP1_STRMARB_TX1_CFG1 0x02BC520C -#define CS35L41_DSP1_STRMARB_TX2_CFG0 0x02BC5210 -#define CS35L41_DSP1_STRMARB_TX2_CFG1 0x02BC5214 -#define CS35L41_DSP1_STRMARB_TX3_CFG0 0x02BC5218 -#define CS35L41_DSP1_STRMARB_TX3_CFG1 0x02BC521C -#define CS35L41_DSP1_STRMARB_TX4_CFG0 0x02BC5220 -#define CS35L41_DSP1_STRMARB_TX4_CFG1 0x02BC5224 -#define CS35L41_DSP1_STRMARB_TX5_CFG0 0x02BC5228 -#define CS35L41_DSP1_STRMARB_TX5_CFG1 0x02BC522C -#define CS35L41_DSP1_STRMARB_TX6_CFG0 0x02BC5230 -#define CS35L41_DSP1_STRMARB_TX6_CFG1 0x02BC5234 -#define CS35L41_DSP1_STRMARB_TX7_CFG0 0x02BC5238 -#define CS35L41_DSP1_STRMARB_TX7_CFG1 0x02BC523C -#define CS35L41_DSP1_STRMARB_RX0_CFG0 0x02BC5400 -#define CS35L41_DSP1_STRMARB_RX0_CFG1 0x02BC5404 -#define CS35L41_DSP1_STRMARB_RX1_CFG0 0x02BC5408 -#define CS35L41_DSP1_STRMARB_RX1_CFG1 0x02BC540C -#define CS35L41_DSP1_STRMARB_RX2_CFG0 0x02BC5410 -#define CS35L41_DSP1_STRMARB_RX2_CFG1 0x02BC5414 -#define CS35L41_DSP1_STRMARB_RX3_CFG0 0x02BC5418 -#define CS35L41_DSP1_STRMARB_RX3_CFG1 0x02BC541C -#define CS35L41_DSP1_STRMARB_RX4_CFG0 0x02BC5420 -#define CS35L41_DSP1_STRMARB_RX4_CFG1 0x02BC5424 -#define CS35L41_DSP1_STRMARB_RX5_CFG0 0x02BC5428 -#define CS35L41_DSP1_STRMARB_RX5_CFG1 0x02BC542C -#define CS35L41_DSP1_STRMARB_RX6_CFG0 0x02BC5430 -#define CS35L41_DSP1_STRMARB_RX6_CFG1 0x02BC5434 -#define CS35L41_DSP1_STRMARB_RX7_CFG0 0x02BC5438 -#define CS35L41_DSP1_STRMARB_RX7_CFG1 0x02BC543C -#define CS35L41_DSP1_STRMARB_IRQ0_CFG0 0x02BC5600 -#define CS35L41_DSP1_STRMARB_IRQ0_CFG1 0x02BC5604 -#define CS35L41_DSP1_STRMARB_IRQ0_CFG2 0x02BC5608 -#define CS35L41_DSP1_STRMARB_IRQ1_CFG0 0x02BC5610 -#define CS35L41_DSP1_STRMARB_IRQ1_CFG1 0x02BC5614 -#define CS35L41_DSP1_STRMARB_IRQ1_CFG2 0x02BC5618 -#define CS35L41_DSP1_STRMARB_IRQ2_CFG0 0x02BC5620 -#define CS35L41_DSP1_STRMARB_IRQ2_CFG1 0x02BC5624 -#define CS35L41_DSP1_STRMARB_IRQ2_CFG2 0x02BC5628 -#define CS35L41_DSP1_STRMARB_IRQ3_CFG0 0x02BC5630 -#define CS35L41_DSP1_STRMARB_IRQ3_CFG1 0x02BC5634 -#define CS35L41_DSP1_STRMARB_IRQ3_CFG2 0x02BC5638 -#define CS35L41_DSP1_STRMARB_IRQ4_CFG0 0x02BC5640 -#define CS35L41_DSP1_STRMARB_IRQ4_CFG1 0x02BC5644 -#define CS35L41_DSP1_STRMARB_IRQ4_CFG2 0x02BC5648 -#define CS35L41_DSP1_STRMARB_IRQ5_CFG0 0x02BC5650 -#define CS35L41_DSP1_STRMARB_IRQ5_CFG1 0x02BC5654 -#define CS35L41_DSP1_STRMARB_IRQ5_CFG2 0x02BC5658 -#define CS35L41_DSP1_STRMARB_IRQ6_CFG0 0x02BC5660 -#define CS35L41_DSP1_STRMARB_IRQ6_CFG1 0x02BC5664 -#define CS35L41_DSP1_STRMARB_IRQ6_CFG2 0x02BC5668 -#define CS35L41_DSP1_STRMARB_IRQ7_CFG0 0x02BC5670 -#define CS35L41_DSP1_STRMARB_IRQ7_CFG1 0x02BC5674 -#define CS35L41_DSP1_STRMARB_IRQ7_CFG2 0x02BC5678 -#define CS35L41_DSP1_STRMARB_RESYNC_MSK 0x02BC5A00 -#define CS35L41_DSP1_STRMARB_ERR_STATUS 0x02BC5A08 -#define CS35L41_DSP1_INTPCTL_RES_STATIC 0x02BC6000 -#define CS35L41_DSP1_INTPCTL_RES_DYN 0x02BC6004 -#define CS35L41_DSP1_INTPCTL_NMI_CTRL 0x02BC6008 -#define CS35L41_DSP1_INTPCTL_IRQ_INV 0x02BC6010 -#define CS35L41_DSP1_INTPCTL_IRQ_MODE 0x02BC6014 -#define CS35L41_DSP1_INTPCTL_IRQ_EN 0x02BC6018 -#define CS35L41_DSP1_INTPCTL_IRQ_MSK 0x02BC601C -#define CS35L41_DSP1_INTPCTL_IRQ_FLUSH 0x02BC6020 -#define CS35L41_DSP1_INTPCTL_IRQ_MSKCLR 0x02BC6024 -#define CS35L41_DSP1_INTPCTL_IRQ_FRC 0x02BC6028 -#define CS35L41_DSP1_INTPCTL_IRQ_MSKSET 0x02BC602C -#define CS35L41_DSP1_INTPCTL_IRQ_ERR 0x02BC6030 -#define CS35L41_DSP1_INTPCTL_IRQ_PEND 0x02BC6034 -#define CS35L41_DSP1_INTPCTL_IRQ_GEN 0x02BC6038 -#define CS35L41_DSP1_INTPCTL_TESTBITS 0x02BC6040 -#define CS35L41_DSP1_WDT_CONTROL 0x02BC7000 -#define CS35L41_DSP1_WDT_STATUS 0x02BC7008 -#define CS35L41_DSP1_YMEM_PACK_0 0x02C00000 -#define CS35L41_DSP1_YMEM_PACK_1532 0x02C017F0 -#define CS35L41_DSP1_YMEM_UNPACK32_0 0x03000000 -#define CS35L41_DSP1_YMEM_UNPACK32_1022 0x03000FF8 -#define CS35L41_DSP1_YMEM_UNPACK24_0 0x03400000 -#define CS35L41_DSP1_YMEM_UNPACK24_2045 0x03401FF4 -#define CS35L41_DSP1_PMEM_0 0x03800000 -#define CS35L41_DSP1_PMEM_5114 0x03804FE8 - -/*test regs for emulation bringup*/ -#define CS35L41_PLL_OVR 0x00003018 -#define CS35L41_BST_TEST_DUTY 0x00003900 -#define CS35L41_DIGPWM_IOCTRL 0x0000706C - -/*registers populated by OTP*/ -#define CS35L41_OTP_TRIM_1 0x0000208c -#define CS35L41_OTP_TRIM_2 0x00002090 -#define CS35L41_OTP_TRIM_3 0x00003010 -#define CS35L41_OTP_TRIM_4 0x0000300C -#define CS35L41_OTP_TRIM_5 0x0000394C -#define CS35L41_OTP_TRIM_6 0x00003950 -#define CS35L41_OTP_TRIM_7 0x00003954 -#define CS35L41_OTP_TRIM_8 0x00003958 -#define CS35L41_OTP_TRIM_9 0x0000395C -#define CS35L41_OTP_TRIM_10 0x0000416C -#define CS35L41_OTP_TRIM_11 0x00004160 -#define CS35L41_OTP_TRIM_12 0x00004170 -#define CS35L41_OTP_TRIM_13 0x00004360 -#define CS35L41_OTP_TRIM_14 0x00004448 -#define CS35L41_OTP_TRIM_15 0x0000444C -#define CS35L41_OTP_TRIM_16 0x00006E30 -#define CS35L41_OTP_TRIM_17 0x00006E34 -#define CS35L41_OTP_TRIM_18 0x00006E38 -#define CS35L41_OTP_TRIM_19 0x00006E3C -#define CS35L41_OTP_TRIM_20 0x00006E40 -#define CS35L41_OTP_TRIM_21 0x00006E44 -#define CS35L41_OTP_TRIM_22 0x00006E48 -#define CS35L41_OTP_TRIM_23 0x00006E4C -#define CS35L41_OTP_TRIM_24 0x00006E50 -#define CS35L41_OTP_TRIM_25 0x00006E54 -#define CS35L41_OTP_TRIM_26 0x00006E58 -#define CS35L41_OTP_TRIM_27 0x00006E5C -#define CS35L41_OTP_TRIM_28 0x00006E60 -#define CS35L41_OTP_TRIM_29 0x00006E64 -#define CS35L41_OTP_TRIM_30 0x00007418 -#define CS35L41_OTP_TRIM_31 0x0000741C -#define CS35L41_OTP_TRIM_32 0x00007434 -#define CS35L41_OTP_TRIM_33 0x00007068 -#define CS35L41_OTP_TRIM_34 0x0000410C -#define CS35L41_OTP_TRIM_35 0x0000400C -#define CS35L41_OTP_TRIM_36 0x00002030 - -#define CS35L41_OTP_SIZE_WORDS 32 -#define CS35L41_NUM_OTP_ELEM 100 -#define CS35L41_NUM_OTP_MAPS 5 - -#define CS35L41_VALID_PDATA 0x80000000 -#define CS35L41_NUM_SUPPLIES 2 - -#define CS35L41_SCLK_MSTR_MASK 0x10 -#define CS35L41_SCLK_MSTR_SHIFT 4 -#define CS35L41_LRCLK_MSTR_MASK 0x01 -#define CS35L41_LRCLK_MSTR_SHIFT 0 -#define CS35L41_SCLK_INV_MASK 0x40 -#define CS35L41_SCLK_INV_SHIFT 6 -#define CS35L41_LRCLK_INV_MASK 0x04 -#define CS35L41_LRCLK_INV_SHIFT 2 -#define CS35L41_SCLK_FRC_MASK 0x20 -#define CS35L41_SCLK_FRC_SHIFT 5 -#define CS35L41_LRCLK_FRC_MASK 0x02 -#define CS35L41_LRCLK_FRC_SHIFT 1 - -#define CS35L41_AMP_GAIN_PCM_MASK 0x3E0 -#define CS35L41_AMP_GAIN_ZC_MASK 0x0400 -#define CS35L41_AMP_GAIN_ZC_SHIFT 10 - -#define CS35L41_BST_CTL_MASK 0xFF -#define CS35L41_BST_CTL_SEL_MASK 0x03 -#define CS35L41_BST_CTL_SEL_REG 0x00 -#define CS35L41_BST_CTL_SEL_CLASSH 0x01 -#define CS35L41_BST_IPK_MASK 0x7F -#define CS35L41_BST_IPK_SHIFT 0 -#define CS35L41_BST_LIM_MASK 0x4 -#define CS35L41_BST_LIM_SHIFT 2 -#define CS35L41_BST_K1_MASK 0x000000FF -#define CS35L41_BST_K1_SHIFT 0 -#define CS35L41_BST_K2_MASK 0x0000FF00 -#define CS35L41_BST_K2_SHIFT 8 -#define CS35L41_BST_SLOPE_MASK 0x0000FF00 -#define CS35L41_BST_SLOPE_SHIFT 8 -#define CS35L41_BST_LBST_VAL_MASK 0x00000003 -#define CS35L41_BST_LBST_VAL_SHIFT 0 - -#define CS35L41_TEMP_THLD_MASK 0x03 -#define CS35L41_VMON_IMON_VOL_MASK 0x07FF07FF -#define CS35L41_PDM_MODE_MASK 0x01 -#define CS35L41_PDM_MODE_SHIFT 0 - -#define CS35L41_CH_MEM_DEPTH_MASK 0x07 -#define CS35L41_CH_MEM_DEPTH_SHIFT 0 -#define CS35L41_CH_HDRM_CTL_MASK 0x007F0000 -#define CS35L41_CH_HDRM_CTL_SHIFT 16 -#define CS35L41_CH_REL_RATE_MASK 0xFF00 -#define CS35L41_CH_REL_RATE_SHIFT 8 -#define CS35L41_CH_WKFET_DLY_MASK 0x001C -#define CS35L41_CH_WKFET_DLY_SHIFT 2 -#define CS35L41_CH_WKFET_THLD_MASK 0x0F00 -#define CS35L41_CH_WKFET_THLD_SHIFT 8 - -#define CS35L41_HW_NG_SEL_MASK 0x3F00 -#define CS35L41_HW_NG_SEL_SHIFT 8 -#define CS35L41_HW_NG_DLY_MASK 0x0070 -#define CS35L41_HW_NG_DLY_SHIFT 4 -#define CS35L41_HW_NG_THLD_MASK 0x0007 -#define CS35L41_HW_NG_THLD_SHIFT 0 - -#define CS35L41_DSP_NG_ENABLE_MASK 0x00010000 -#define CS35L41_DSP_NG_ENABLE_SHIFT 16 -#define CS35L41_DSP_NG_THLD_MASK 0x7 -#define CS35L41_DSP_NG_THLD_SHIFT 0 -#define CS35L41_DSP_NG_DELAY_MASK 0x0F00 -#define CS35L41_DSP_NG_DELAY_SHIFT 8 - -#define CS35L41_ASP_FMT_MASK 0x0700 -#define CS35L41_ASP_FMT_SHIFT 8 -#define CS35L41_ASP_DOUT_HIZ_MASK 0x03 -#define CS35L41_ASP_DOUT_HIZ_SHIFT 0 -#define CS35L41_ASP_WIDTH_16 0x10 -#define CS35L41_ASP_WIDTH_24 0x18 -#define CS35L41_ASP_WIDTH_32 0x20 -#define CS35L41_ASP_WIDTH_TX_MASK 0xFF0000 -#define CS35L41_ASP_WIDTH_TX_SHIFT 16 -#define CS35L41_ASP_WIDTH_RX_MASK 0xFF000000 -#define CS35L41_ASP_WIDTH_RX_SHIFT 24 -#define CS35L41_ASP_RX1_SLOT_MASK 0x3F -#define CS35L41_ASP_RX1_SLOT_SHIFT 0 -#define CS35L41_ASP_RX2_SLOT_MASK 0x3F00 -#define CS35L41_ASP_RX2_SLOT_SHIFT 8 -#define CS35L41_ASP_RX_WL_MASK 0x3F -#define CS35L41_ASP_TX_WL_MASK 0x3F -#define CS35L41_ASP_RX_WL_SHIFT 0 -#define CS35L41_ASP_TX_WL_SHIFT 0 -#define CS35L41_ASP_SOURCE_MASK 0x7F - -#define CS35L41_INPUT_SRC_ASPRX1 0x08 -#define CS35L41_INPUT_SRC_ASPRX2 0x09 -#define CS35L41_INPUT_SRC_VMON 0x18 -#define CS35L41_INPUT_SRC_IMON 0x19 -#define CS35L41_INPUT_SRC_CLASSH 0x21 -#define CS35L41_INPUT_SRC_VPMON 0x28 -#define CS35L41_INPUT_SRC_VBSTMON 0x29 -#define CS35L41_INPUT_SRC_TEMPMON 0x3A -#define CS35L41_INPUT_SRC_RSVD 0x3B -#define CS35L41_INPUT_DSP_TX1 0x32 -#define CS35L41_INPUT_DSP_TX2 0x33 - -#define CS35L41_PLL_CLK_SEL_MASK 0x07 -#define CS35L41_PLL_CLK_SEL_SHIFT 0 -#define CS35L41_PLL_CLK_EN_MASK 0x10 -#define CS35L41_PLL_CLK_EN_SHIFT 4 -#define CS35L41_PLL_OPENLOOP_MASK 0x0800 -#define CS35L41_PLL_OPENLOOP_SHIFT 11 -#define CS35L41_PLLSRC_SCLK 0 -#define CS35L41_PLLSRC_LRCLK 1 -#define CS35L41_PLLSRC_SELF 3 -#define CS35L41_PLLSRC_PDMCLK 4 -#define CS35L41_PLLSRC_MCLK 5 -#define CS35L41_PLLSRC_SWIRE 7 -#define CS35L41_REFCLK_FREQ_MASK 0x7E0 -#define CS35L41_REFCLK_FREQ_SHIFT 5 - -#define CS35L41_GLOBAL_FS_MASK 0x1F -#define CS35L41_GLOBAL_FS_SHIFT 0 - -#define CS35L41_GLOBAL_EN_MASK 0x01 -#define CS35L41_GLOBAL_EN_SHIFT 0 -#define CS35L41_BST_EN_MASK 0x0030 -#define CS35L41_BST_EN_SHIFT 4 -#define CS35L41_BST_EN_DEFAULT 0x2 -#define CS35L41_AMP_EN_SHIFT 0 -#define CS35L41_AMP_EN_MASK 1 - -#define CS35L41_PDN_DONE_MASK 0x00800000 -#define CS35L41_PDN_DONE_SHIFT 23 -#define CS35L41_PUP_DONE_MASK 0x01000000 -#define CS35L41_PUP_DONE_SHIFT 24 - -#define CS35L36_PUP_DONE_IRQ_UNMASK 0x5F -#define CS35L36_PUP_DONE_IRQ_MASK 0xBF - -#define CS35L41_AMP_SHORT_ERR 0x80000000 -#define CS35L41_BST_SHORT_ERR 0x0100 -#define CS35L41_TEMP_WARN 0x8000 -#define CS35L41_TEMP_ERR 0x00020000 -#define CS35L41_BST_OVP_ERR 0x40 -#define CS35L41_BST_DCM_UVP_ERR 0x80 -#define CS35L41_OTP_BOOT_DONE 0x02 -#define CS35L41_PLL_UNLOCK 0x10 -#define CS35L41_OTP_BOOT_ERR 0x80000000 - -#define CS35L41_AMP_SHORT_ERR_RLS 0x02 -#define CS35L41_BST_SHORT_ERR_RLS 0x04 -#define CS35L41_BST_OVP_ERR_RLS 0x08 -#define CS35L41_BST_UVP_ERR_RLS 0x10 -#define CS35L41_TEMP_WARN_ERR_RLS 0x20 -#define CS35L41_TEMP_ERR_RLS 0x40 - -#define CS35L41_INT1_MASK_DEFAULT 0x7FFCFE3F -#define CS35L41_INT1_UNMASK_PUP 0xFEFFFFFF -#define CS35L41_INT1_UNMASK_PDN 0xFF7FFFFF - -#define CS35L41_GPIO_DIR_MASK 0x80000000 -#define CS35L41_GPIO_DIR_SHIFT 31 -#define CS35L41_GPIO1_CTRL_MASK 0x00030000 -#define CS35L41_GPIO1_CTRL_SHIFT 16 -#define CS35L41_GPIO2_CTRL_MASK 0x07000000 -#define CS35L41_GPIO2_CTRL_SHIFT 24 -#define CS35L41_GPIO_CTRL_OPEN_INT 2 -#define CS35L41_GPIO_CTRL_ACTV_LO 4 -#define CS35L41_GPIO_CTRL_ACTV_HI 5 -#define CS35L41_GPIO_POL_MASK 0x1000 -#define CS35L41_GPIO_POL_SHIFT 12 - -#define CS35L41_AMP_INV_PCM_SHIFT 14 -#define CS35L41_AMP_INV_PCM_MASK BIT(CS35L41_AMP_INV_PCM_SHIFT) -#define CS35L41_AMP_PCM_VOL_SHIFT 3 -#define CS35L41_AMP_PCM_VOL_MASK (0x7FF << 3) -#define CS35L41_AMP_PCM_VOL_MUTE 0x4CF - -#define CS35L41_CHIP_ID 0x35a40 -#define CS35L41R_CHIP_ID 0x35b40 -#define CS35L41_MTLREVID_MASK 0x0F -#define CS35L41_REVID_A0 0xA0 -#define CS35L41_REVID_B0 0xB0 -#define CS35L41_REVID_B2 0xB2 - -#define CS35L41_HALO_CORE_RESET 0x00000200 - -#define CS35L41_FS1_WINDOW_MASK 0x000007FF -#define CS35L41_FS2_WINDOW_MASK 0x00FFF800 -#define CS35L41_FS2_WINDOW_SHIFT 12 - -#define CS35L41_SPI_MAX_FREQ 4000000 - #define CS35L41_RX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) #define CS35L41_TX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) -extern struct regmap_config cs35l41_regmap_i2c; -extern struct regmap_config cs35l41_regmap_spi; - -struct cs35l41_otp_packed_element_t { - u32 reg; - u8 shift; - u8 size; -}; - -struct cs35l41_otp_map_element_t { - u32 id; - u32 num_elements; - const struct cs35l41_otp_packed_element_t *map; - u32 bit_offset; - u32 word_offset; -}; - -extern const struct cs35l41_otp_map_element_t - cs35l41_otp_map_map[CS35L41_NUM_OTP_MAPS]; - -#define CS35L41_REGSTRIDE 4 - enum cs35l41_cspl_mbox_status { CSPL_MBOX_STS_RUNNING = 0, CSPL_MBOX_STS_PAUSED = 1, -- cgit v1.2.3 From fe120d4cb6f6cd03007239e7c578b8703fe6d336 Mon Sep 17 00:00:00 2001 From: Lucas Tanure Date: Fri, 17 Dec 2021 11:57:00 +0000 Subject: ASoC: cs35l41: Move cs35l41_otp_unpack to shared code ASoC and HDA will do the same cs35l41_otp_unpack, so move it to shared code Signed-off-by: Lucas Tanure Link: https://lore.kernel.org/r/20211217115708.882525-3-tanureal@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/cs35l41.h | 4 +- sound/soc/codecs/cs35l41-lib.c | 121 ++++++++++++++++++++++++++++++++++++++- sound/soc/codecs/cs35l41.c | 125 +---------------------------------------- 3 files changed, 122 insertions(+), 128 deletions(-) diff --git a/include/sound/cs35l41.h b/include/sound/cs35l41.h index aac3ffb9bc89..6cf3ef02b26a 100644 --- a/include/sound/cs35l41.h +++ b/include/sound/cs35l41.h @@ -534,7 +534,6 @@ #define CS35L41_MAX_CACHE_REG 36 #define CS35L41_OTP_SIZE_WORDS 32 #define CS35L41_NUM_OTP_ELEM 100 -#define CS35L41_NUM_OTP_MAPS 5 #define CS35L41_VALID_PDATA 0x80000000 #define CS35L41_NUM_SUPPLIES 2 @@ -760,8 +759,9 @@ struct cs35l41_otp_map_element_t { u32 word_offset; }; -extern const struct cs35l41_otp_map_element_t cs35l41_otp_map_map[CS35L41_NUM_OTP_MAPS]; extern struct regmap_config cs35l41_regmap_i2c; extern struct regmap_config cs35l41_regmap_spi; +int cs35l41_otp_unpack(struct device *dev, struct regmap *regmap); + #endif /* __CS35L41_H */ diff --git a/sound/soc/codecs/cs35l41-lib.c b/sound/soc/codecs/cs35l41-lib.c index f19531ebf729..dc5f502447a2 100644 --- a/sound/soc/codecs/cs35l41-lib.c +++ b/sound/soc/codecs/cs35l41-lib.c @@ -7,8 +7,11 @@ // Author: David Rhodes // Author: Lucas Tanure +#include #include #include +#include +#include #include @@ -655,7 +658,7 @@ static const struct cs35l41_otp_packed_element_t otp_map_2[CS35L41_NUM_OTP_ELEM] { 0x00017044, 0, 24 }, /*LOT_NUMBER*/ }; -const struct cs35l41_otp_map_element_t cs35l41_otp_map_map[CS35L41_NUM_OTP_MAPS] = { +static const struct cs35l41_otp_map_element_t cs35l41_otp_map_map[] = { { .id = 0x01, .map = otp_map_1, @@ -692,7 +695,6 @@ const struct cs35l41_otp_map_element_t cs35l41_otp_map_map[CS35L41_NUM_OTP_MAPS] .word_offset = 2, }, }; -EXPORT_SYMBOL_GPL(cs35l41_otp_map_map); struct regmap_config cs35l41_regmap_i2c = { .reg_bits = 32, @@ -727,6 +729,121 @@ struct regmap_config cs35l41_regmap_spi = { }; EXPORT_SYMBOL_GPL(cs35l41_regmap_spi); +static const struct cs35l41_otp_map_element_t *cs35l41_find_otp_map(u32 otp_id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cs35l41_otp_map_map); i++) { + if (cs35l41_otp_map_map[i].id == otp_id) + return &cs35l41_otp_map_map[i]; + } + + return NULL; +} + +int cs35l41_otp_unpack(struct device *dev, struct regmap *regmap) +{ + const struct cs35l41_otp_map_element_t *otp_map_match; + const struct cs35l41_otp_packed_element_t *otp_map; + int bit_offset, word_offset, ret, i; + unsigned int bit_sum = 8; + u32 otp_val, otp_id_reg; + u32 *otp_mem; + + otp_mem = kmalloc_array(CS35L41_OTP_SIZE_WORDS, sizeof(*otp_mem), GFP_KERNEL); + if (!otp_mem) + return -ENOMEM; + + ret = regmap_read(regmap, CS35L41_OTPID, &otp_id_reg); + if (ret) { + dev_err(dev, "Read OTP ID failed: %d\n", ret); + goto err_otp_unpack; + } + + otp_map_match = cs35l41_find_otp_map(otp_id_reg); + + if (!otp_map_match) { + dev_err(dev, "OTP Map matching ID %d not found\n", otp_id_reg); + ret = -EINVAL; + goto err_otp_unpack; + } + + ret = regmap_bulk_read(regmap, CS35L41_OTP_MEM0, otp_mem, CS35L41_OTP_SIZE_WORDS); + if (ret) { + dev_err(dev, "Read OTP Mem failed: %d\n", ret); + goto err_otp_unpack; + } + + otp_map = otp_map_match->map; + + bit_offset = otp_map_match->bit_offset; + word_offset = otp_map_match->word_offset; + + ret = regmap_write(regmap, CS35L41_TEST_KEY_CTL, 0x00000055); + if (ret) { + dev_err(dev, "Write Unlock key failed 1/2: %d\n", ret); + goto err_otp_unpack; + } + ret = regmap_write(regmap, CS35L41_TEST_KEY_CTL, 0x000000AA); + if (ret) { + dev_err(dev, "Write Unlock key failed 2/2: %d\n", ret); + goto err_otp_unpack; + } + + for (i = 0; i < otp_map_match->num_elements; i++) { + dev_dbg(dev, "bitoffset= %d, word_offset=%d, bit_sum mod 32=%d\n", + bit_offset, word_offset, bit_sum % 32); + if (bit_offset + otp_map[i].size - 1 >= 32) { + otp_val = (otp_mem[word_offset] & + GENMASK(31, bit_offset)) >> bit_offset; + otp_val |= (otp_mem[++word_offset] & + GENMASK(bit_offset + otp_map[i].size - 33, 0)) << + (32 - bit_offset); + bit_offset += otp_map[i].size - 32; + } else { + otp_val = (otp_mem[word_offset] & + GENMASK(bit_offset + otp_map[i].size - 1, bit_offset) + ) >> bit_offset; + bit_offset += otp_map[i].size; + } + bit_sum += otp_map[i].size; + + if (bit_offset == 32) { + bit_offset = 0; + word_offset++; + } + + if (otp_map[i].reg != 0) { + ret = regmap_update_bits(regmap, otp_map[i].reg, + GENMASK(otp_map[i].shift + otp_map[i].size - 1, + otp_map[i].shift), + otp_val << otp_map[i].shift); + if (ret < 0) { + dev_err(dev, "Write OTP val failed: %d\n", ret); + goto err_otp_unpack; + } + } + } + + ret = regmap_write(regmap, CS35L41_TEST_KEY_CTL, 0x000000CC); + if (ret) { + dev_err(dev, "Write Lock key failed 1/2: %d\n", ret); + goto err_otp_unpack; + } + ret = regmap_write(regmap, CS35L41_TEST_KEY_CTL, 0x00000033); + if (ret) { + dev_err(dev, "Write Lock key failed 2/2: %d\n", ret); + goto err_otp_unpack; + } + ret = 0; + +err_otp_unpack: + kfree(otp_mem); + + return ret; +} +EXPORT_SYMBOL_GPL(cs35l41_otp_unpack); + MODULE_DESCRIPTION("CS35L41 library"); MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, "); MODULE_AUTHOR("Lucas Tanure, Cirrus Logic Inc, "); diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index 60332eae1162..aa57c59b334d 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -420,128 +419,6 @@ static const struct snd_kcontrol_new cs35l41_aud_controls[] = { WM_ADSP_FW_CONTROL("DSP1", 0), }; -static const struct cs35l41_otp_map_element_t *cs35l41_find_otp_map(u32 otp_id) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(cs35l41_otp_map_map); i++) { - if (cs35l41_otp_map_map[i].id == otp_id) - return &cs35l41_otp_map_map[i]; - } - - return NULL; -} - -static int cs35l41_otp_unpack(void *data) -{ - const struct cs35l41_otp_map_element_t *otp_map_match; - const struct cs35l41_otp_packed_element_t *otp_map; - struct cs35l41_private *cs35l41 = data; - int bit_offset, word_offset, ret, i; - unsigned int bit_sum = 8; - u32 otp_val, otp_id_reg; - u32 *otp_mem; - - otp_mem = kmalloc_array(CS35L41_OTP_SIZE_WORDS, sizeof(*otp_mem), GFP_KERNEL); - if (!otp_mem) - return -ENOMEM; - - ret = regmap_read(cs35l41->regmap, CS35L41_OTPID, &otp_id_reg); - if (ret < 0) { - dev_err(cs35l41->dev, "Read OTP ID failed: %d\n", ret); - goto err_otp_unpack; - } - - otp_map_match = cs35l41_find_otp_map(otp_id_reg); - - if (!otp_map_match) { - dev_err(cs35l41->dev, "OTP Map matching ID %d not found\n", - otp_id_reg); - ret = -EINVAL; - goto err_otp_unpack; - } - - ret = regmap_bulk_read(cs35l41->regmap, CS35L41_OTP_MEM0, otp_mem, - CS35L41_OTP_SIZE_WORDS); - if (ret < 0) { - dev_err(cs35l41->dev, "Read OTP Mem failed: %d\n", ret); - goto err_otp_unpack; - } - - otp_map = otp_map_match->map; - - bit_offset = otp_map_match->bit_offset; - word_offset = otp_map_match->word_offset; - - ret = regmap_write(cs35l41->regmap, CS35L41_TEST_KEY_CTL, 0x00000055); - if (ret < 0) { - dev_err(cs35l41->dev, "Write Unlock key failed 1/2: %d\n", ret); - goto err_otp_unpack; - } - ret = regmap_write(cs35l41->regmap, CS35L41_TEST_KEY_CTL, 0x000000AA); - if (ret < 0) { - dev_err(cs35l41->dev, "Write Unlock key failed 2/2: %d\n", ret); - goto err_otp_unpack; - } - - for (i = 0; i < otp_map_match->num_elements; i++) { - dev_dbg(cs35l41->dev, - "bitoffset= %d, word_offset=%d, bit_sum mod 32=%d\n", - bit_offset, word_offset, bit_sum % 32); - if (bit_offset + otp_map[i].size - 1 >= 32) { - otp_val = (otp_mem[word_offset] & - GENMASK(31, bit_offset)) >> - bit_offset; - otp_val |= (otp_mem[++word_offset] & - GENMASK(bit_offset + - otp_map[i].size - 33, 0)) << - (32 - bit_offset); - bit_offset += otp_map[i].size - 32; - } else { - otp_val = (otp_mem[word_offset] & - GENMASK(bit_offset + otp_map[i].size - 1, - bit_offset)) >> bit_offset; - bit_offset += otp_map[i].size; - } - bit_sum += otp_map[i].size; - - if (bit_offset == 32) { - bit_offset = 0; - word_offset++; - } - - if (otp_map[i].reg != 0) { - ret = regmap_update_bits(cs35l41->regmap, - otp_map[i].reg, - GENMASK(otp_map[i].shift + - otp_map[i].size - 1, - otp_map[i].shift), - otp_val << otp_map[i].shift); - if (ret < 0) { - dev_err(cs35l41->dev, "Write OTP val failed: %d\n", - ret); - goto err_otp_unpack; - } - } - } - - ret = regmap_write(cs35l41->regmap, CS35L41_TEST_KEY_CTL, 0x000000CC); - if (ret < 0) { - dev_err(cs35l41->dev, "Write Lock key failed 1/2: %d\n", ret); - goto err_otp_unpack; - } - ret = regmap_write(cs35l41->regmap, CS35L41_TEST_KEY_CTL, 0x00000033); - if (ret < 0) { - dev_err(cs35l41->dev, "Write Lock key failed 2/2: %d\n", ret); - goto err_otp_unpack; - } - ret = 0; - -err_otp_unpack: - kfree(otp_mem); - return ret; -} - static irqreturn_t cs35l41_irq(int irq, void *data) { struct cs35l41_private *cs35l41 = data; @@ -1667,7 +1544,7 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, goto err; } - ret = cs35l41_otp_unpack(cs35l41); + ret = cs35l41_otp_unpack(cs35l41->dev, cs35l41->regmap); if (ret < 0) { dev_err(cs35l41->dev, "OTP Unpack failed: %d\n", ret); goto err; -- cgit v1.2.3 From 062ce0593315e22aac527389dd6dd4328c49f0fb Mon Sep 17 00:00:00 2001 From: Lucas Tanure Date: Fri, 17 Dec 2021 11:57:01 +0000 Subject: ASoC: cs35l41: Move power initializations to reg_sequence ASoC and HDA systems for all revisions of CS35L41 will benefit from having this initialization, so add it to reg_sequence of each revision By moving to reg_sequence all gains are set to zero. And boost, monitoring parts, and class D amplifier are disabled. Signed-off-by: Lucas Tanure Link: https://lore.kernel.org/r/20211217115708.882525-4-tanureal@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l41-lib.c | 3 ++- sound/soc/codecs/cs35l41.c | 20 ++++++-------------- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/sound/soc/codecs/cs35l41-lib.c b/sound/soc/codecs/cs35l41-lib.c index dc5f502447a2..b3567e10adc4 100644 --- a/sound/soc/codecs/cs35l41-lib.c +++ b/sound/soc/codecs/cs35l41-lib.c @@ -17,6 +17,7 @@ static const struct reg_default cs35l41_reg[] = { { CS35L41_PWR_CTRL1, 0x00000000 }, + { CS35L41_PWR_CTRL2, 0x00000000 }, { CS35L41_PWR_CTRL3, 0x01000010 }, { CS35L41_GPIO_PAD_CONTROL, 0x00000000 }, { CS35L41_SP_ENABLES, 0x00000000 }, @@ -46,7 +47,7 @@ static const struct reg_default cs35l41_reg[] = { { CS35L41_CLASSH_CFG, 0x000B0405 }, { CS35L41_WKFET_CFG, 0x00000111 }, { CS35L41_NG_CFG, 0x00000033 }, - { CS35L41_AMP_GAIN_CTRL, 0x00000273 }, + { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, { CS35L41_GPIO1_CTRL1, 0xE1000001 }, { CS35L41_GPIO2_CTRL1, 0xE1000001 }, { CS35L41_MIXER_NGATE_CFG, 0x00000000 }, diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index aa57c59b334d..7494710ae6e6 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -1288,6 +1288,8 @@ static const struct reg_sequence cs35l41_reva0_errata_patch[] = { { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 }, { 0x00000040, 0x0000CCCC }, { 0x00000040, 0x00003333 }, + { CS35L41_PWR_CTRL2, 0x00000000 }, + { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, }; static const struct reg_sequence cs35l41_revb0_errata_patch[] = { @@ -1301,6 +1303,8 @@ static const struct reg_sequence cs35l41_revb0_errata_patch[] = { { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 }, { 0x00000040, 0x0000CCCC }, { 0x00000040, 0x00003333 }, + { CS35L41_PWR_CTRL2, 0x00000000 }, + { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, }; static const struct reg_sequence cs35l41_revb2_errata_patch[] = { @@ -1314,6 +1318,8 @@ static const struct reg_sequence cs35l41_revb2_errata_patch[] = { { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 }, { 0x00000040, 0x0000CCCC }, { 0x00000040, 0x00003333 }, + { CS35L41_PWR_CTRL2, 0x00000000 }, + { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, }; static const struct reg_sequence cs35l41_fs_errata_patch[] = { @@ -1556,20 +1562,6 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, goto err; } - ret = regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2, - CS35L41_AMP_EN_MASK, 0); - if (ret < 0) { - dev_err(cs35l41->dev, "Write CS35L41_PWR_CTRL2 failed: %d\n", ret); - goto err; - } - - ret = regmap_update_bits(cs35l41->regmap, CS35L41_AMP_GAIN_CTRL, - CS35L41_AMP_GAIN_PCM_MASK, 0); - if (ret < 0) { - dev_err(cs35l41->dev, "Write CS35L41_AMP_GAIN_CTRL failed: %d\n", ret); - goto err; - } - ret = cs35l41_set_pdata(cs35l41); if (ret < 0) { dev_err(cs35l41->dev, "Set pdata failed: %d\n", ret); -- cgit v1.2.3 From 8b2278604b6de27329ec7ed82ca696c4751111b6 Mon Sep 17 00:00:00 2001 From: Lucas Tanure Date: Fri, 17 Dec 2021 11:57:02 +0000 Subject: ASoC: cs35l41: Create shared function for errata patches ASoC and HDA systems require the same errata patches, so move it to the shared code using a function the correctly applies the patches by revision Also, move CS35L41_DSP1_CCM_CORE_CTRL write to errata patch function as is required to be written at boot, but not in regmap_register_patch sequence as will affect waking up from hibernation Signed-off-by: Lucas Tanure Link: https://lore.kernel.org/r/20211217115708.882525-5-tanureal@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/cs35l41.h | 1 + sound/soc/codecs/cs35l41-lib.c | 89 ++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/cs35l41.c | 92 ++---------------------------------------- 3 files changed, 93 insertions(+), 89 deletions(-) diff --git a/include/sound/cs35l41.h b/include/sound/cs35l41.h index 6cf3ef02b26a..ad2e32a12b8c 100644 --- a/include/sound/cs35l41.h +++ b/include/sound/cs35l41.h @@ -763,5 +763,6 @@ extern struct regmap_config cs35l41_regmap_i2c; extern struct regmap_config cs35l41_regmap_spi; int cs35l41_otp_unpack(struct device *dev, struct regmap *regmap); +int cs35l41_register_errata_patch(struct device *dev, struct regmap *reg, unsigned int reg_revid); #endif /* __CS35L41_H */ diff --git a/sound/soc/codecs/cs35l41-lib.c b/sound/soc/codecs/cs35l41-lib.c index b3567e10adc4..5e382eaea340 100644 --- a/sound/soc/codecs/cs35l41-lib.c +++ b/sound/soc/codecs/cs35l41-lib.c @@ -659,6 +659,57 @@ static const struct cs35l41_otp_packed_element_t otp_map_2[CS35L41_NUM_OTP_ELEM] { 0x00017044, 0, 24 }, /*LOT_NUMBER*/ }; +static const struct reg_sequence cs35l41_reva0_errata_patch[] = { + { 0x00000040, 0x00005555 }, + { 0x00000040, 0x0000AAAA }, + { 0x00003854, 0x05180240 }, + { CS35L41_VIMON_SPKMON_RESYNC, 0x00000000 }, + { 0x00004310, 0x00000000 }, + { CS35L41_VPVBST_FS_SEL, 0x00000000 }, + { CS35L41_OTP_TRIM_30, 0x9091A1C8 }, + { 0x00003014, 0x0200EE0E }, + { CS35L41_BSTCVRT_DCM_CTRL, 0x00000051 }, + { 0x00000054, 0x00000004 }, + { CS35L41_IRQ1_DB3, 0x00000000 }, + { CS35L41_IRQ2_DB3, 0x00000000 }, + { CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 }, + { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 }, + { 0x00000040, 0x0000CCCC }, + { 0x00000040, 0x00003333 }, + { CS35L41_PWR_CTRL2, 0x00000000 }, + { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, +}; + +static const struct reg_sequence cs35l41_revb0_errata_patch[] = { + { 0x00000040, 0x00005555 }, + { 0x00000040, 0x0000AAAA }, + { CS35L41_VIMON_SPKMON_RESYNC, 0x00000000 }, + { 0x00004310, 0x00000000 }, + { CS35L41_VPVBST_FS_SEL, 0x00000000 }, + { CS35L41_BSTCVRT_DCM_CTRL, 0x00000051 }, + { CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 }, + { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 }, + { 0x00000040, 0x0000CCCC }, + { 0x00000040, 0x00003333 }, + { CS35L41_PWR_CTRL2, 0x00000000 }, + { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, +}; + +static const struct reg_sequence cs35l41_revb2_errata_patch[] = { + { 0x00000040, 0x00005555 }, + { 0x00000040, 0x0000AAAA }, + { CS35L41_VIMON_SPKMON_RESYNC, 0x00000000 }, + { 0x00004310, 0x00000000 }, + { CS35L41_VPVBST_FS_SEL, 0x00000000 }, + { CS35L41_BSTCVRT_DCM_CTRL, 0x00000051 }, + { CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 }, + { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 }, + { 0x00000040, 0x0000CCCC }, + { 0x00000040, 0x00003333 }, + { CS35L41_PWR_CTRL2, 0x00000000 }, + { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, +}; + static const struct cs35l41_otp_map_element_t cs35l41_otp_map_map[] = { { .id = 0x01, @@ -845,6 +896,44 @@ err_otp_unpack: } EXPORT_SYMBOL_GPL(cs35l41_otp_unpack); +int cs35l41_register_errata_patch(struct device *dev, struct regmap *reg, unsigned int reg_revid) +{ + char *rev; + int ret; + + switch (reg_revid) { + case CS35L41_REVID_A0: + ret = regmap_register_patch(reg, cs35l41_reva0_errata_patch, + ARRAY_SIZE(cs35l41_reva0_errata_patch)); + rev = "A0"; + break; + case CS35L41_REVID_B0: + ret = regmap_register_patch(reg, cs35l41_revb0_errata_patch, + ARRAY_SIZE(cs35l41_revb0_errata_patch)); + rev = "B0"; + break; + case CS35L41_REVID_B2: + ret = regmap_register_patch(reg, cs35l41_revb2_errata_patch, + ARRAY_SIZE(cs35l41_revb2_errata_patch)); + rev = "B2"; + break; + default: + ret = -EINVAL; + rev = "XX"; + break; + } + + if (ret) + dev_err(dev, "Failed to apply %s errata patch: %d\n", rev, ret); + + ret = regmap_write(reg, CS35L41_DSP1_CCM_CORE_CTRL, 0); + if (ret < 0) + dev_err(dev, "Write CCM_CORE_CTRL failed: %d\n", ret); + + return ret; +} +EXPORT_SYMBOL_GPL(cs35l41_register_errata_patch); + MODULE_DESCRIPTION("CS35L41 library"); MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, "); MODULE_AUTHOR("Lucas Tanure, Cirrus Logic Inc, "); diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index 7494710ae6e6..afc10f7ca65e 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -1271,57 +1271,6 @@ static int cs35l41_handle_pdata(struct device *dev, return 0; } -static const struct reg_sequence cs35l41_reva0_errata_patch[] = { - { 0x00000040, 0x00005555 }, - { 0x00000040, 0x0000AAAA }, - { 0x00003854, 0x05180240 }, - { CS35L41_VIMON_SPKMON_RESYNC, 0x00000000 }, - { 0x00004310, 0x00000000 }, - { CS35L41_VPVBST_FS_SEL, 0x00000000 }, - { CS35L41_OTP_TRIM_30, 0x9091A1C8 }, - { 0x00003014, 0x0200EE0E }, - { CS35L41_BSTCVRT_DCM_CTRL, 0x00000051 }, - { 0x00000054, 0x00000004 }, - { CS35L41_IRQ1_DB3, 0x00000000 }, - { CS35L41_IRQ2_DB3, 0x00000000 }, - { CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 }, - { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 }, - { 0x00000040, 0x0000CCCC }, - { 0x00000040, 0x00003333 }, - { CS35L41_PWR_CTRL2, 0x00000000 }, - { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, -}; - -static const struct reg_sequence cs35l41_revb0_errata_patch[] = { - { 0x00000040, 0x00005555 }, - { 0x00000040, 0x0000AAAA }, - { CS35L41_VIMON_SPKMON_RESYNC, 0x00000000 }, - { 0x00004310, 0x00000000 }, - { CS35L41_VPVBST_FS_SEL, 0x00000000 }, - { CS35L41_BSTCVRT_DCM_CTRL, 0x00000051 }, - { CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 }, - { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 }, - { 0x00000040, 0x0000CCCC }, - { 0x00000040, 0x00003333 }, - { CS35L41_PWR_CTRL2, 0x00000000 }, - { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, -}; - -static const struct reg_sequence cs35l41_revb2_errata_patch[] = { - { 0x00000040, 0x00005555 }, - { 0x00000040, 0x0000AAAA }, - { CS35L41_VIMON_SPKMON_RESYNC, 0x00000000 }, - { 0x00004310, 0x00000000 }, - { CS35L41_VPVBST_FS_SEL, 0x00000000 }, - { CS35L41_BSTCVRT_DCM_CTRL, 0x00000051 }, - { CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 }, - { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 }, - { 0x00000040, 0x0000CCCC }, - { 0x00000040, 0x00003333 }, - { CS35L41_PWR_CTRL2, 0x00000000 }, - { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, -}; - static const struct reg_sequence cs35l41_fs_errata_patch[] = { { CS35L41_DSP1_RX1_RATE, 0x00000001 }, { CS35L41_DSP1_RX2_RATE, 0x00000001 }, @@ -1501,38 +1450,9 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, goto err; } - switch (reg_revid) { - case CS35L41_REVID_A0: - ret = regmap_register_patch(cs35l41->regmap, - cs35l41_reva0_errata_patch, - ARRAY_SIZE(cs35l41_reva0_errata_patch)); - if (ret < 0) { - dev_err(cs35l41->dev, - "Failed to apply A0 errata patch: %d\n", ret); - goto err; - } - break; - case CS35L41_REVID_B0: - ret = regmap_register_patch(cs35l41->regmap, - cs35l41_revb0_errata_patch, - ARRAY_SIZE(cs35l41_revb0_errata_patch)); - if (ret < 0) { - dev_err(cs35l41->dev, - "Failed to apply B0 errata patch: %d\n", ret); - goto err; - } - break; - case CS35L41_REVID_B2: - ret = regmap_register_patch(cs35l41->regmap, - cs35l41_revb2_errata_patch, - ARRAY_SIZE(cs35l41_revb2_errata_patch)); - if (ret < 0) { - dev_err(cs35l41->dev, - "Failed to apply B2 errata patch: %d\n", ret); - goto err; - } - break; - } + ret = cs35l41_register_errata_patch(cs35l41->dev, cs35l41->regmap, reg_revid); + if (ret) + goto err; irq_pol = cs35l41_irq_gpio_config(cs35l41); @@ -1556,12 +1476,6 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, goto err; } - ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_CCM_CORE_CTRL, 0); - if (ret < 0) { - dev_err(cs35l41->dev, "Write CCM_CORE_CTRL failed: %d\n", ret); - goto err; - } - ret = cs35l41_set_pdata(cs35l41); if (ret < 0) { dev_err(cs35l41->dev, "Set pdata failed: %d\n", ret); -- cgit v1.2.3 From 3bc3e3da657f17c14df8ae8fab58183407bd7521 Mon Sep 17 00:00:00 2001 From: Lucas Tanure Date: Fri, 17 Dec 2021 11:57:03 +0000 Subject: ASoC: cs35l41: Create shared function for setting channels ASoC and HDA will use the same register to set channels for the device Signed-off-by: Lucas Tanure Link: https://lore.kernel.org/r/20211217115708.882525-6-tanureal@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/cs35l41.h | 3 +++ sound/soc/codecs/cs35l41-lib.c | 32 ++++++++++++++++++++++++++++++++ sound/soc/codecs/cs35l41.c | 30 +++--------------------------- 3 files changed, 38 insertions(+), 27 deletions(-) diff --git a/include/sound/cs35l41.h b/include/sound/cs35l41.h index ad2e32a12b8c..39d150f61382 100644 --- a/include/sound/cs35l41.h +++ b/include/sound/cs35l41.h @@ -764,5 +764,8 @@ extern struct regmap_config cs35l41_regmap_spi; int cs35l41_otp_unpack(struct device *dev, struct regmap *regmap); int cs35l41_register_errata_patch(struct device *dev, struct regmap *reg, unsigned int reg_revid); +int cs35l41_set_channels(struct device *dev, struct regmap *reg, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot); #endif /* __CS35L41_H */ diff --git a/sound/soc/codecs/cs35l41-lib.c b/sound/soc/codecs/cs35l41-lib.c index 5e382eaea340..afcec715374d 100644 --- a/sound/soc/codecs/cs35l41-lib.c +++ b/sound/soc/codecs/cs35l41-lib.c @@ -934,6 +934,38 @@ int cs35l41_register_errata_patch(struct device *dev, struct regmap *reg, unsign } EXPORT_SYMBOL_GPL(cs35l41_register_errata_patch); +int cs35l41_set_channels(struct device *dev, struct regmap *reg, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + unsigned int val, mask; + int i; + + if (tx_num > 4 || rx_num > 2) + return -EINVAL; + + val = 0; + mask = 0; + for (i = 0; i < rx_num; i++) { + dev_dbg(dev, "rx slot %d position = %d\n", i, rx_slot[i]); + val |= rx_slot[i] << (i * 8); + mask |= 0x3F << (i * 8); + } + regmap_update_bits(reg, CS35L41_SP_FRAME_RX_SLOT, mask, val); + + val = 0; + mask = 0; + for (i = 0; i < tx_num; i++) { + dev_dbg(dev, "tx slot %d position = %d\n", i, tx_slot[i]); + val |= tx_slot[i] << (i * 8); + mask |= 0x3F << (i * 8); + } + regmap_update_bits(reg, CS35L41_SP_FRAME_TX_SLOT, mask, val); + + return 0; +} +EXPORT_SYMBOL_GPL(cs35l41_set_channels); + MODULE_DESCRIPTION("CS35L41 library"); MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, "); MODULE_AUTHOR("Lucas Tanure, Cirrus Logic Inc, "); diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index afc10f7ca65e..88d6e77fdb50 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -751,36 +751,12 @@ static const struct cs_dsp_region cs35l41_dsp1_regions[] = { {. type = WMFW_ADSP2_YM, .base = CS35L41_DSP1_YMEM_UNPACK24_0}, }; -static int cs35l41_set_channel_map(struct snd_soc_dai *dai, unsigned int tx_num, - unsigned int *tx_slot, unsigned int rx_num, - unsigned int *rx_slot) +static int cs35l41_set_channel_map(struct snd_soc_dai *dai, unsigned int tx_n, + unsigned int *tx_slot, unsigned int rx_n, unsigned int *rx_slot) { struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(dai->component); - unsigned int val, mask; - int i; - - if (tx_num > 4 || rx_num > 2) - return -EINVAL; - val = 0; - mask = 0; - for (i = 0; i < rx_num; i++) { - dev_dbg(cs35l41->dev, "rx slot %d position = %d\n", i, rx_slot[i]); - val |= rx_slot[i] << (i * 8); - mask |= 0x3F << (i * 8); - } - regmap_update_bits(cs35l41->regmap, CS35L41_SP_FRAME_RX_SLOT, mask, val); - - val = 0; - mask = 0; - for (i = 0; i < tx_num; i++) { - dev_dbg(cs35l41->dev, "tx slot %d position = %d\n", i, tx_slot[i]); - val |= tx_slot[i] << (i * 8); - mask |= 0x3F << (i * 8); - } - regmap_update_bits(cs35l41->regmap, CS35L41_SP_FRAME_TX_SLOT, mask, val); - - return 0; + return cs35l41_set_channels(cs35l41->dev, cs35l41->regmap, tx_n, tx_slot, rx_n, rx_slot); } static int cs35l41_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) -- cgit v1.2.3 From e8e4fcc047c6e0c5411faeb8cc29aed2e5036a00 Mon Sep 17 00:00:00 2001 From: Lucas Tanure Date: Fri, 17 Dec 2021 11:57:04 +0000 Subject: ASoC: cs35l41: Create shared function for boost configuration ASoC and HDA will use the same registers to configure internal boost for the device Signed-off-by: Lucas Tanure Link: https://lore.kernel.org/r/20211217115708.882525-7-tanureal@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/cs35l41.h | 2 + sound/soc/codecs/cs35l41-lib.c | 98 ++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/cs35l41.c | 105 +---------------------------------------- 3 files changed, 102 insertions(+), 103 deletions(-) diff --git a/include/sound/cs35l41.h b/include/sound/cs35l41.h index 39d150f61382..29a527457b48 100644 --- a/include/sound/cs35l41.h +++ b/include/sound/cs35l41.h @@ -767,5 +767,7 @@ int cs35l41_register_errata_patch(struct device *dev, struct regmap *reg, unsign int cs35l41_set_channels(struct device *dev, struct regmap *reg, unsigned int tx_num, unsigned int *tx_slot, unsigned int rx_num, unsigned int *rx_slot); +int cs35l41_boost_config(struct device *dev, struct regmap *regmap, int boost_ind, int boost_cap, + int boost_ipk); #endif /* __CS35L41_H */ diff --git a/sound/soc/codecs/cs35l41-lib.c b/sound/soc/codecs/cs35l41-lib.c index afcec715374d..d026c5e3a378 100644 --- a/sound/soc/codecs/cs35l41-lib.c +++ b/sound/soc/codecs/cs35l41-lib.c @@ -966,6 +966,104 @@ int cs35l41_set_channels(struct device *dev, struct regmap *reg, } EXPORT_SYMBOL_GPL(cs35l41_set_channels); +static const unsigned char cs35l41_bst_k1_table[4][5] = { + { 0x24, 0x32, 0x32, 0x4F, 0x57 }, + { 0x24, 0x32, 0x32, 0x4F, 0x57 }, + { 0x40, 0x32, 0x32, 0x4F, 0x57 }, + { 0x40, 0x32, 0x32, 0x4F, 0x57 } +}; + +static const unsigned char cs35l41_bst_k2_table[4][5] = { + { 0x24, 0x49, 0x66, 0xA3, 0xEA }, + { 0x24, 0x49, 0x66, 0xA3, 0xEA }, + { 0x48, 0x49, 0x66, 0xA3, 0xEA }, + { 0x48, 0x49, 0x66, 0xA3, 0xEA } +}; + +static const unsigned char cs35l41_bst_slope_table[4] = { + 0x75, 0x6B, 0x3B, 0x28 +}; + + +int cs35l41_boost_config(struct device *dev, struct regmap *regmap, int boost_ind, int boost_cap, + int boost_ipk) +{ + unsigned char bst_lbst_val, bst_cbst_range, bst_ipk_scaled; + int ret; + + switch (boost_ind) { + case 1000: /* 1.0 uH */ + bst_lbst_val = 0; + break; + case 1200: /* 1.2 uH */ + bst_lbst_val = 1; + break; + case 1500: /* 1.5 uH */ + bst_lbst_val = 2; + break; + case 2200: /* 2.2 uH */ + bst_lbst_val = 3; + break; + default: + dev_err(dev, "Invalid boost inductor value: %d nH\n", boost_ind); + return -EINVAL; + } + + switch (boost_cap) { + case 0 ... 19: + bst_cbst_range = 0; + break; + case 20 ... 50: + bst_cbst_range = 1; + break; + case 51 ... 100: + bst_cbst_range = 2; + break; + case 101 ... 200: + bst_cbst_range = 3; + break; + default: /* 201 uF and greater */ + bst_cbst_range = 4; + } + + ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_COEFF, + CS35L41_BST_K1_MASK | CS35L41_BST_K2_MASK, + cs35l41_bst_k1_table[bst_lbst_val][bst_cbst_range] + << CS35L41_BST_K1_SHIFT | + cs35l41_bst_k2_table[bst_lbst_val][bst_cbst_range] + << CS35L41_BST_K2_SHIFT); + if (ret) { + dev_err(dev, "Failed to write boost coefficients: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_SLOPE_LBST, + CS35L41_BST_SLOPE_MASK | CS35L41_BST_LBST_VAL_MASK, + cs35l41_bst_slope_table[bst_lbst_val] + << CS35L41_BST_SLOPE_SHIFT | + bst_lbst_val << CS35L41_BST_LBST_VAL_SHIFT); + if (ret) { + dev_err(dev, "Failed to write boost slope/inductor value: %d\n", ret); + return ret; + } + + if (boost_ipk < 1600 || boost_ipk > 4500) { + dev_err(dev, "Invalid boost inductor peak current: %d mA\n", boost_ipk); + return -EINVAL; + } + bst_ipk_scaled = ((boost_ipk - 1600) / 50) + 0x10; + + ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_PEAK_CUR, CS35L41_BST_IPK_MASK, + bst_ipk_scaled << CS35L41_BST_IPK_SHIFT); + if (ret) { + dev_err(dev, "Failed to write boost inductor peak current: %d\n", ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(cs35l41_boost_config); + MODULE_DESCRIPTION("CS35L41 library"); MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, "); MODULE_AUTHOR("Lucas Tanure, Cirrus Logic Inc, "); diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index 88d6e77fdb50..d9e6e84e64d0 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -150,24 +150,6 @@ static const struct cs35l41_fs_mon_config cs35l41_fs_mon[] = { { 6144000, 16, 24 }, }; -static const unsigned char cs35l41_bst_k1_table[4][5] = { - { 0x24, 0x32, 0x32, 0x4F, 0x57 }, - { 0x24, 0x32, 0x32, 0x4F, 0x57 }, - { 0x40, 0x32, 0x32, 0x4F, 0x57 }, - { 0x40, 0x32, 0x32, 0x4F, 0x57 } -}; - -static const unsigned char cs35l41_bst_k2_table[4][5] = { - { 0x24, 0x49, 0x66, 0xA3, 0xEA }, - { 0x24, 0x49, 0x66, 0xA3, 0xEA }, - { 0x48, 0x49, 0x66, 0xA3, 0xEA }, - { 0x48, 0x49, 0x66, 0xA3, 0xEA } -}; - -static const unsigned char cs35l41_bst_slope_table[4] = { - 0x75, 0x6B, 0x3B, 0x28 -}; - static int cs35l41_get_fs_mon_config_index(int freq) { int i; @@ -992,88 +974,6 @@ static int cs35l41_dai_set_sysclk(struct snd_soc_dai *dai, return 0; } -static int cs35l41_boost_config(struct cs35l41_private *cs35l41, - int boost_ind, int boost_cap, int boost_ipk) -{ - unsigned char bst_lbst_val, bst_cbst_range, bst_ipk_scaled; - struct regmap *regmap = cs35l41->regmap; - struct device *dev = cs35l41->dev; - int ret; - - switch (boost_ind) { - case 1000: /* 1.0 uH */ - bst_lbst_val = 0; - break; - case 1200: /* 1.2 uH */ - bst_lbst_val = 1; - break; - case 1500: /* 1.5 uH */ - bst_lbst_val = 2; - break; - case 2200: /* 2.2 uH */ - bst_lbst_val = 3; - break; - default: - dev_err(dev, "Invalid boost inductor value: %d nH\n", boost_ind); - return -EINVAL; - } - - switch (boost_cap) { - case 0 ... 19: - bst_cbst_range = 0; - break; - case 20 ... 50: - bst_cbst_range = 1; - break; - case 51 ... 100: - bst_cbst_range = 2; - break; - case 101 ... 200: - bst_cbst_range = 3; - break; - default: /* 201 uF and greater */ - bst_cbst_range = 4; - } - - ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_COEFF, - CS35L41_BST_K1_MASK | CS35L41_BST_K2_MASK, - cs35l41_bst_k1_table[bst_lbst_val][bst_cbst_range] - << CS35L41_BST_K1_SHIFT | - cs35l41_bst_k2_table[bst_lbst_val][bst_cbst_range] - << CS35L41_BST_K2_SHIFT); - if (ret) { - dev_err(dev, "Failed to write boost coefficients: %d\n", ret); - return ret; - } - - ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_SLOPE_LBST, - CS35L41_BST_SLOPE_MASK | CS35L41_BST_LBST_VAL_MASK, - cs35l41_bst_slope_table[bst_lbst_val] - << CS35L41_BST_SLOPE_SHIFT | - bst_lbst_val << CS35L41_BST_LBST_VAL_SHIFT); - if (ret) { - dev_err(dev, "Failed to write boost slope/inductor value: %d\n", ret); - return ret; - } - - if (boost_ipk < 1600 || boost_ipk > 4500) { - dev_err(dev, "Invalid boost inductor peak current: %d mA\n", - boost_ipk); - return -EINVAL; - } - bst_ipk_scaled = ((boost_ipk - 1600) / 50) + 0x10; - - ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_PEAK_CUR, - CS35L41_BST_IPK_MASK, - bst_ipk_scaled << CS35L41_BST_IPK_SHIFT); - if (ret) { - dev_err(dev, "Failed to write boost inductor peak current: %d\n", ret); - return ret; - } - - return 0; -} - static int cs35l41_set_pdata(struct cs35l41_private *cs35l41) { int ret; @@ -1082,9 +982,8 @@ static int cs35l41_set_pdata(struct cs35l41_private *cs35l41) /* Required */ if (cs35l41->pdata.bst_ipk && cs35l41->pdata.bst_ind && cs35l41->pdata.bst_cap) { - ret = cs35l41_boost_config(cs35l41, cs35l41->pdata.bst_ind, - cs35l41->pdata.bst_cap, - cs35l41->pdata.bst_ipk); + ret = cs35l41_boost_config(cs35l41->dev, cs35l41->regmap, cs35l41->pdata.bst_ind, + cs35l41->pdata.bst_cap, cs35l41->pdata.bst_ipk); if (ret) { dev_err(cs35l41->dev, "Error in Boost DT config: %d\n", ret); return ret; -- cgit v1.2.3 From d278dc9151a034674b31ffeda24cdfb0073570f3 Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Thu, 23 Dec 2021 17:23:49 +0530 Subject: ALSA: hda/tegra: Fix Tegra194 HDA reset failure HDA regression is recently reported on Tegra194 based platforms. This happens because "hda2codec_2x" reset does not really exist in Tegra194 and it causes probe failure. All the HDA based audio tests fail at the moment. This underlying issue is exposed by commit c045ceb5a145 ("reset: tegra-bpmp: Handle errors in BPMP response") which now checks return code of BPMP command response. Fix this issue by skipping unavailable reset on Tegra194. Cc: stable@vger.kernel.org Signed-off-by: Sameer Pujar Reviewed-by: Dmitry Osipenko Link: https://lore.kernel.org/r/1640260431-11613-2-git-send-email-spujar@nvidia.com Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_tegra.c | 43 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index ea700395bef4..773f4903550a 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -68,14 +68,20 @@ */ #define TEGRA194_NUM_SDO_LINES 4 +struct hda_tegra_soc { + bool has_hda2codec_2x_reset; +}; + struct hda_tegra { struct azx chip; struct device *dev; - struct reset_control *reset; + struct reset_control_bulk_data resets[3]; struct clk_bulk_data clocks[3]; + unsigned int nresets; unsigned int nclocks; void __iomem *regs; struct work_struct probe_work; + const struct hda_tegra_soc *soc; }; #ifdef CONFIG_PM @@ -170,7 +176,7 @@ static int __maybe_unused hda_tegra_runtime_resume(struct device *dev) int rc; if (!chip->running) { - rc = reset_control_assert(hda->reset); + rc = reset_control_bulk_assert(hda->nresets, hda->resets); if (rc) return rc; } @@ -187,7 +193,7 @@ static int __maybe_unused hda_tegra_runtime_resume(struct device *dev) } else { usleep_range(10, 100); - rc = reset_control_deassert(hda->reset); + rc = reset_control_bulk_deassert(hda->nresets, hda->resets); if (rc) return rc; } @@ -427,9 +433,17 @@ static int hda_tegra_create(struct snd_card *card, return 0; } +static const struct hda_tegra_soc tegra30_data = { + .has_hda2codec_2x_reset = true, +}; + +static const struct hda_tegra_soc tegra194_data = { + .has_hda2codec_2x_reset = false, +}; + static const struct of_device_id hda_tegra_match[] = { - { .compatible = "nvidia,tegra30-hda" }, - { .compatible = "nvidia,tegra194-hda" }, + { .compatible = "nvidia,tegra30-hda", .data = &tegra30_data }, + { .compatible = "nvidia,tegra194-hda", .data = &tegra194_data }, {}, }; MODULE_DEVICE_TABLE(of, hda_tegra_match); @@ -449,6 +463,8 @@ static int hda_tegra_probe(struct platform_device *pdev) hda->dev = &pdev->dev; chip = &hda->chip; + hda->soc = of_device_get_match_data(&pdev->dev); + err = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, THIS_MODULE, 0, &card); if (err < 0) { @@ -456,11 +472,20 @@ static int hda_tegra_probe(struct platform_device *pdev) return err; } - hda->reset = devm_reset_control_array_get_exclusive(&pdev->dev); - if (IS_ERR(hda->reset)) { - err = PTR_ERR(hda->reset); + hda->resets[hda->nresets++].id = "hda"; + hda->resets[hda->nresets++].id = "hda2hdmi"; + /* + * "hda2codec_2x" reset is not present on Tegra194. Though DT would + * be updated to reflect this, but to have backward compatibility + * below is necessary. + */ + if (hda->soc->has_hda2codec_2x_reset) + hda->resets[hda->nresets++].id = "hda2codec_2x"; + + err = devm_reset_control_bulk_get_exclusive(&pdev->dev, hda->nresets, + hda->resets); + if (err) goto out_free; - } hda->clocks[hda->nclocks++].id = "hda"; hda->clocks[hda->nclocks++].id = "hda2hdmi"; -- cgit v1.2.3 From 6088ddfb6d8f004184a55857b6c64d8cf5f9a8d8 Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Thu, 23 Dec 2021 17:23:50 +0530 Subject: dt-bindings: sound: tegra: Add minItems for resets Tegra194 HDA has only two resets unlike the previous generations of Tegra SoCs. To take care of this set minItems field to two. Signed-off-by: Sameer Pujar Link: https://lore.kernel.org/r/1640260431-11613-3-git-send-email-spujar@nvidia.com Signed-off-by: Takashi Iwai --- Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.yaml index b55775e21de6..2c913aa44fee 100644 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.yaml +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.yaml @@ -50,9 +50,11 @@ properties: - const: hda2codec_2x resets: + minItems: 2 maxItems: 3 reset-names: + minItems: 2 items: - const: hda - const: hda2hdmi -- cgit v1.2.3 From 146b3a77af8091cabbd1decc51d67799e69682d2 Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Thu, 23 Dec 2021 17:23:51 +0530 Subject: arm64: tegra: Remove non existent Tegra194 reset Tegra194 does not really have "hda2codec_2x" related reset. Hence drop this entry to reflect actual HW. Fixes: 4878cc0c9fab ("arm64: tegra: Add HDA controller on Tegra194") Signed-off-by: Sameer Pujar Link: https://lore.kernel.org/r/1640260431-11613-4-git-send-email-spujar@nvidia.com Signed-off-by: Takashi Iwai --- arch/arm64/boot/dts/nvidia/tegra194.dtsi | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/nvidia/tegra194.dtsi b/arch/arm64/boot/dts/nvidia/tegra194.dtsi index 851e049b3519..dcc0e55d6bdb 100644 --- a/arch/arm64/boot/dts/nvidia/tegra194.dtsi +++ b/arch/arm64/boot/dts/nvidia/tegra194.dtsi @@ -976,9 +976,8 @@ <&bpmp TEGRA194_CLK_HDA2CODEC_2X>; clock-names = "hda", "hda2hdmi", "hda2codec_2x"; resets = <&bpmp TEGRA194_RESET_HDA>, - <&bpmp TEGRA194_RESET_HDA2HDMICODEC>, - <&bpmp TEGRA194_RESET_HDA2CODEC_2X>; - reset-names = "hda", "hda2hdmi", "hda2codec_2x"; + <&bpmp TEGRA194_RESET_HDA2HDMICODEC>; + reset-names = "hda", "hda2hdmi"; power-domains = <&bpmp TEGRA194_POWER_DOMAIN_DISP>; interconnects = <&mc TEGRA194_MEMORY_CLIENT_HDAR &emc>, <&mc TEGRA194_MEMORY_CLIENT_HDAW &emc>; -- cgit v1.2.3 From c1933008679586b20437280463110c967d66f865 Mon Sep 17 00:00:00 2001 From: Christian Lachner Date: Mon, 3 Jan 2022 15:05:17 +0100 Subject: ALSA: hda/realtek - Fix silent output on Gigabyte X570 Aorus Master after reboot from Windows This patch addresses an issue where after rebooting from Windows into Linux there would be no audio output. It turns out that the Realtek Audio driver on Windows changes some coeffs which are not being reset/reinitialized when rebooting the machine. As a result, there is no audio output until these coeffs are being reset to their initial state. This patch takes care of that by setting known-good (initial) values to the coeffs. We initially relied upon alc1220_fixup_clevo_p950() to fix some pins in the connection list. However, it also sets coef 0x7 which does not need to be touched. Furthermore, to prevent mixing device-specific quirks I introduced a new alc1220_fixup_gb_x570() which is heavily based on alc1220_fixup_clevo_p950() but does not set coeff 0x7 and fixes the coeffs that are actually needed instead. This new alc1220_fixup_gb_x570() is believed to also work for other boards, like the Gigabyte X570 Aorus Extreme and the newer Gigabyte Aorus X570S Master. However, as there is no way for me to test these I initially only enable this new behaviour for the mainboard I have which is the Gigabyte X570(non-S) Aorus Master. I tested this patch on the 5.15 branch as well as on master and it is working well for me. BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=205275 Signed-off-by: Christian Lachner Fixes: 0d45e86d2267d ("ALSA: hda/realtek - Fix silent output on Gigabyte X570 Aorus Master") Cc: Link: https://lore.kernel.org/r/20220103140517.30273-2-gladiac@gmail.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 2f1727faec69..2eea70605fd3 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1924,6 +1924,7 @@ enum { ALC887_FIXUP_ASUS_BASS, ALC887_FIXUP_BASS_CHMAP, ALC1220_FIXUP_GB_DUAL_CODECS, + ALC1220_FIXUP_GB_X570, ALC1220_FIXUP_CLEVO_P950, ALC1220_FIXUP_CLEVO_PB51ED, ALC1220_FIXUP_CLEVO_PB51ED_PINS, @@ -2113,6 +2114,29 @@ static void alc1220_fixup_gb_dual_codecs(struct hda_codec *codec, } } +static void alc1220_fixup_gb_x570(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + static const hda_nid_t conn1[] = { 0x0c }; + static const struct coef_fw gb_x570_coefs[] = { + WRITE_COEF(0x1a, 0x01c1), + WRITE_COEF(0x1b, 0x0202), + WRITE_COEF(0x43, 0x3005), + {} + }; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1); + snd_hda_override_conn_list(codec, 0x1b, ARRAY_SIZE(conn1), conn1); + break; + case HDA_FIXUP_ACT_INIT: + alc_process_coef_fw(codec, gb_x570_coefs); + break; + } +} + static void alc1220_fixup_clevo_p950(struct hda_codec *codec, const struct hda_fixup *fix, int action) @@ -2415,6 +2439,10 @@ static const struct hda_fixup alc882_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc1220_fixup_gb_dual_codecs, }, + [ALC1220_FIXUP_GB_X570] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc1220_fixup_gb_x570, + }, [ALC1220_FIXUP_CLEVO_P950] = { .type = HDA_FIXUP_FUNC, .v.func = alc1220_fixup_clevo_p950, @@ -2517,7 +2545,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x13fe, 0x1009, "Advantech MIT-W101", ALC886_FIXUP_EAPD), SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte EP45-DS3/Z87X-UD3H", ALC889_FIXUP_FRONT_HP_NO_PRESENCE), SND_PCI_QUIRK(0x1458, 0xa0b8, "Gigabyte AZ370-Gaming", ALC1220_FIXUP_GB_DUAL_CODECS), - SND_PCI_QUIRK(0x1458, 0xa0cd, "Gigabyte X570 Aorus Master", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1458, 0xa0cd, "Gigabyte X570 Aorus Master", ALC1220_FIXUP_GB_X570), SND_PCI_QUIRK(0x1458, 0xa0ce, "Gigabyte X570 Aorus Xtreme", ALC1220_FIXUP_CLEVO_P950), SND_PCI_QUIRK(0x1462, 0x11f7, "MSI-GE63", ALC1220_FIXUP_CLEVO_P950), SND_PCI_QUIRK(0x1462, 0x1228, "MSI-GP63", ALC1220_FIXUP_CLEVO_P950), -- cgit v1.2.3 From 9f3d45318dd9e739ed62e4218839a7a824d3cced Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Tue, 4 Jan 2022 13:22:16 +0000 Subject: ASoC: fsl_mqs: fix MODULE_ALIAS modprobe can't handle spaces in aliases. Fixes: 9e28f6532c61 ("ASoC: fsl_mqs: Add MQS component driver") Signed-off-by: Alyssa Ross Link: https://lore.kernel.org/r/20220104132218.1690103-1-hi@alyssa.is Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_mqs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/fsl/fsl_mqs.c b/sound/soc/fsl/fsl_mqs.c index 27b4536dce44..ceaecbe3a25e 100644 --- a/sound/soc/fsl/fsl_mqs.c +++ b/sound/soc/fsl/fsl_mqs.c @@ -337,4 +337,4 @@ module_platform_driver(fsl_mqs_driver); MODULE_AUTHOR("Shengjiu Wang "); MODULE_DESCRIPTION("MQS codec driver"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform: fsl-mqs"); +MODULE_ALIAS("platform:fsl-mqs"); -- cgit v1.2.3 From 8cd07657177006b67cc1610e4466cc75ad781c05 Mon Sep 17 00:00:00 2001 From: "Christian A. Ehrhardt" Date: Fri, 31 Dec 2021 14:12:21 +0100 Subject: ALSA: hda/cs8409: Increase delay during jack detection Commit c8b4f0865e82 reduced delays related to cs42l42 jack detection. However, the change was too aggressive. As a result internal speakers on DELL Inspirion 3501 are not detected. Increase the delay in cs42l42_run_jack_detect() a bit. Fixes: c8b4f0865e82 ("ALSA: hda/cs8409: Remove unnecessary delays") Signed-off-by: Christian A. Ehrhardt Link: https://lore.kernel.org/r/20211231131221.itwotyfk5qomn7n6@cae.in-ulm.de Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_cs8409.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c index 31ff11ab868e..07eab788b145 100644 --- a/sound/pci/hda/patch_cs8409.c +++ b/sound/pci/hda/patch_cs8409.c @@ -628,8 +628,8 @@ static void cs42l42_run_jack_detect(struct sub_codec *cs42l42) cs8409_i2c_write(cs42l42, 0x1b74, 0x07); cs8409_i2c_write(cs42l42, 0x131b, 0xFD); cs8409_i2c_write(cs42l42, 0x1120, 0x80); - /* Wait ~100us*/ - usleep_range(100, 200); + /* Wait ~20ms*/ + usleep_range(20000, 25000); cs8409_i2c_write(cs42l42, 0x111f, 0x77); cs8409_i2c_write(cs42l42, 0x1120, 0xc0); } -- cgit v1.2.3 From 57f234248ff925d88caedf4019ec84e6ecb83909 Mon Sep 17 00:00:00 2001 From: "Christian A. Ehrhardt" Date: Fri, 31 Dec 2021 14:44:32 +0100 Subject: ALSA: hda/cs8409: Fix Jack detection after resume The suspend code unconditionally sets ->hp_jack_in and ->mic_jack_in to zero but without reporting this status change to the HDA core. To compensate for this, always assume a status change on the first unsol event after boot or resume. Fixes: 424e531b47f8 ("ALSA: hda/cs8409: Ensure Type Detection is only run on startup when necessary") Signed-off-by: Christian A. Ehrhardt Link: https://lore.kernel.org/r/20211231134432.atwmuzeceqiklcoa@cae.in-ulm.de Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_cs8409-tables.c | 3 +++ sound/pci/hda/patch_cs8409.c | 5 ++++- sound/pci/hda/patch_cs8409.h | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_cs8409-tables.c b/sound/pci/hda/patch_cs8409-tables.c index 0fb0a428428b..df0b4522babf 100644 --- a/sound/pci/hda/patch_cs8409-tables.c +++ b/sound/pci/hda/patch_cs8409-tables.c @@ -252,6 +252,7 @@ struct sub_codec cs8409_cs42l42_codec = { .init_seq_num = ARRAY_SIZE(cs42l42_init_reg_seq), .hp_jack_in = 0, .mic_jack_in = 0, + .force_status_change = 1, .paged = 1, .suspended = 1, .no_type_dect = 0, @@ -443,6 +444,7 @@ struct sub_codec dolphin_cs42l42_0 = { .init_seq_num = ARRAY_SIZE(dolphin_c0_init_reg_seq), .hp_jack_in = 0, .mic_jack_in = 0, + .force_status_change = 1, .paged = 1, .suspended = 1, .no_type_dect = 0, @@ -456,6 +458,7 @@ struct sub_codec dolphin_cs42l42_1 = { .init_seq_num = ARRAY_SIZE(dolphin_c1_init_reg_seq), .hp_jack_in = 0, .mic_jack_in = 0, + .force_status_change = 1, .paged = 1, .suspended = 1, .no_type_dect = 1, diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c index 07eab788b145..9319ca879d01 100644 --- a/sound/pci/hda/patch_cs8409.c +++ b/sound/pci/hda/patch_cs8409.c @@ -636,7 +636,9 @@ static void cs42l42_run_jack_detect(struct sub_codec *cs42l42) static int cs42l42_handle_tip_sense(struct sub_codec *cs42l42, unsigned int reg_ts_status) { - int status_changed = 0; + int status_changed = cs42l42->force_status_change; + + cs42l42->force_status_change = 0; /* TIP_SENSE INSERT/REMOVE */ switch (reg_ts_status) { @@ -786,6 +788,7 @@ static void cs42l42_suspend(struct sub_codec *cs42l42) cs42l42->last_page = 0; cs42l42->hp_jack_in = 0; cs42l42->mic_jack_in = 0; + cs42l42->force_status_change = 1; /* Put CS42L42 into Reset */ gpio_data = snd_hda_codec_read(codec, CS8409_PIN_AFG, 0, AC_VERB_GET_GPIO_DATA, 0); diff --git a/sound/pci/hda/patch_cs8409.h b/sound/pci/hda/patch_cs8409.h index ade2b838590c..d0b725c7285b 100644 --- a/sound/pci/hda/patch_cs8409.h +++ b/sound/pci/hda/patch_cs8409.h @@ -305,6 +305,7 @@ struct sub_codec { unsigned int hp_jack_in:1; unsigned int mic_jack_in:1; + unsigned int force_status_change:1; unsigned int suspended:1; unsigned int paged:1; unsigned int last_page; -- cgit v1.2.3 From dcf821319474edde7e85b95608a4539703a2b67d Mon Sep 17 00:00:00 2001 From: David Rhodes Date: Wed, 5 Jan 2022 11:30:19 +0000 Subject: ASoC: cs35l41: Add cs35l51/53 IDs Add IDs for the CS35L51/53 variants, the functionality is shared with CS35L41. Signed-off-by: David Rhodes Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20220105113026.18955-2-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l41-i2c.c | 2 ++ sound/soc/codecs/cs35l41-spi.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/sound/soc/codecs/cs35l41-i2c.c b/sound/soc/codecs/cs35l41-i2c.c index de5c8612f030..eb8dfb6d9c95 100644 --- a/sound/soc/codecs/cs35l41-i2c.c +++ b/sound/soc/codecs/cs35l41-i2c.c @@ -22,6 +22,8 @@ static const struct i2c_device_id cs35l41_id_i2c[] = { { "cs35l40", 0 }, { "cs35l41", 0 }, + { "cs35l51", 0 }, + { "cs35l53", 0 }, {} }; diff --git a/sound/soc/codecs/cs35l41-spi.c b/sound/soc/codecs/cs35l41-spi.c index c157153f28d8..86bbe2fba956 100644 --- a/sound/soc/codecs/cs35l41-spi.c +++ b/sound/soc/codecs/cs35l41-spi.c @@ -20,6 +20,8 @@ static const struct spi_device_id cs35l41_id_spi[] = { { "cs35l40", 0 }, { "cs35l41", 0 }, + { "cs35l51", 0 }, + { "cs35l53", 0 }, {} }; -- cgit v1.2.3 From 4e7c3cd87db8d9350062a25a8476f90fd1cbc4c9 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 5 Jan 2022 11:30:20 +0000 Subject: ASoC: cs35l41: Remove incorrect comment The IRQ is not used for the PDN_DONE bit, this is polled during the DAPM sequence, remove the misleading comment. Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20220105113026.18955-3-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l41.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index d9e6e84e64d0..980294c1bcdb 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -1338,8 +1338,6 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, ret = devm_request_threaded_irq(cs35l41->dev, cs35l41->irq, NULL, cs35l41_irq, IRQF_ONESHOT | IRQF_SHARED | irq_pol, "cs35l41", cs35l41); - - /* CS35L41 needs INT for PDN_DONE */ if (ret != 0) { dev_err(cs35l41->dev, "Failed to request IRQ: %d\n", ret); goto err; -- cgit v1.2.3 From 56852cf4b2179fb90068a49538501f31c2de18ea Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 5 Jan 2022 11:30:21 +0000 Subject: ASoC: cs35l41: Correct DSP power down The wm_adsp_event should be called before the early_event on power down, event stops the core running and early_event then powers down the core. Additionally, the core should only be stopped if it was actually running in the first place. Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20220105113026.18955-4-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l41.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index 980294c1bcdb..05839fabf97b 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -181,17 +181,21 @@ static SOC_ENUM_SINGLE_DECL(pcm_sft_ramp, static int cs35l41_dsp_preload_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component); int ret; switch (event) { case SND_SOC_DAPM_PRE_PMU: return wm_adsp_early_event(w, kcontrol, event); case SND_SOC_DAPM_PRE_PMD: - ret = wm_adsp_early_event(w, kcontrol, event); - if (ret) - return ret; + if (cs35l41->dsp.cs_dsp.running) { + ret = wm_adsp_event(w, kcontrol, event); + if (ret) + return ret; + } - return wm_adsp_event(w, kcontrol, event); + return wm_adsp_early_event(w, kcontrol, event); default: return 0; } -- cgit v1.2.3 From 5f2f539901b0d9bda722637521a11b7f7cf753f1 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 5 Jan 2022 11:30:22 +0000 Subject: ASoC: cs35l41: Correct handling of some registers in the cache It makes no sense to cache the test/user key registers, since they require values written at specific times, mark them volatile. It is probably best if they can't be accessed from user-space either, so mark them precious as well. The interrupt force, edge, polarity and debounce are all settings applied to the IRQ rather than status bits and as such should not be volatile. The OTP trim values will require re-application in the event of a cache sync and as such should not be volatile. The OTPID however should be volatile. The DSP scratch registers are used to read back an error/debug code from the DSP on shutdown, as such these should be marked volatile. Finally, add some missing defaults, add TST_FS_MON0, and allow the DSP core control register to be cached. Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20220105113026.18955-5-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l41-lib.c | 81 ++++++++++++------------------------------ 1 file changed, 22 insertions(+), 59 deletions(-) diff --git a/sound/soc/codecs/cs35l41-lib.c b/sound/soc/codecs/cs35l41-lib.c index d026c5e3a378..639dcd25b17e 100644 --- a/sound/soc/codecs/cs35l41-lib.c +++ b/sound/soc/codecs/cs35l41-lib.c @@ -20,6 +20,11 @@ static const struct reg_default cs35l41_reg[] = { { CS35L41_PWR_CTRL2, 0x00000000 }, { CS35L41_PWR_CTRL3, 0x01000010 }, { CS35L41_GPIO_PAD_CONTROL, 0x00000000 }, + { CS35L41_GLOBAL_CLK_CTRL, 0x00000003 }, + { CS35L41_TST_FS_MON0, 0x00020016 }, + { CS35L41_BSTCVRT_COEFF, 0x00002424 }, + { CS35L41_BSTCVRT_SLOPE_LBST, 0x00007500 }, + { CS35L41_BSTCVRT_PEAK_CUR, 0x0000004A }, { CS35L41_SP_ENABLES, 0x00000000 }, { CS35L41_SP_RATE_CTRL, 0x00000028 }, { CS35L41_SP_FORMAT, 0x18180200 }, @@ -48,11 +53,16 @@ static const struct reg_default cs35l41_reg[] = { { CS35L41_WKFET_CFG, 0x00000111 }, { CS35L41_NG_CFG, 0x00000033 }, { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, + { CS35L41_IRQ1_MASK1, 0xFFFFFFFF }, + { CS35L41_IRQ1_MASK2, 0xFFFFFFFF }, + { CS35L41_IRQ1_MASK3, 0xFFFF87FF }, + { CS35L41_IRQ1_MASK4, 0xFEFFFFFF }, { CS35L41_GPIO1_CTRL1, 0xE1000001 }, { CS35L41_GPIO2_CTRL1, 0xE1000001 }, { CS35L41_MIXER_NGATE_CFG, 0x00000000 }, { CS35L41_MIXER_NGATE_CH1_CFG, 0x00000303 }, { CS35L41_MIXER_NGATE_CH2_CFG, 0x00000303 }, + { CS35L41_DSP1_CCM_CORE_CTRL, 0x00000101 }, }; static bool cs35l41_readable_reg(struct device *dev, unsigned int reg) @@ -84,6 +94,7 @@ static bool cs35l41_readable_reg(struct device *dev, unsigned int reg) case CS35L41_DSP_CLK_CTRL: case CS35L41_GLOBAL_CLK_CTRL: case CS35L41_DATA_FS_SEL: + case CS35L41_TST_FS_MON0: case CS35L41_MDSYNC_EN: case CS35L41_MDSYNC_TX_ID: case CS35L41_MDSYNC_PWR_CTRL: @@ -342,7 +353,10 @@ static bool cs35l41_readable_reg(struct device *dev, unsigned int reg) static bool cs35l41_precious_reg(struct device *dev, unsigned int reg) { switch (reg) { + case CS35L41_TEST_KEY_CTL: + case CS35L41_USER_KEY_CTL: case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31: + case CS35L41_TST_FS_MON0: case CS35L41_DSP1_XMEM_PACK_0 ... CS35L41_DSP1_XMEM_PACK_3068: case CS35L41_DSP1_YMEM_PACK_0 ... CS35L41_DSP1_YMEM_PACK_1532: case CS35L41_DSP1_PMEM_0 ... CS35L41_DSP1_PMEM_5114: @@ -359,6 +373,9 @@ static bool cs35l41_volatile_reg(struct device *dev, unsigned int reg) case CS35L41_SFT_RESET: case CS35L41_FABID: case CS35L41_REVID: + case CS35L41_OTPID: + case CS35L41_TEST_KEY_CTL: + case CS35L41_USER_KEY_CTL: case CS35L41_DTEMP_EN: case CS35L41_IRQ1_STATUS: case CS35L41_IRQ1_STATUS1: @@ -369,17 +386,6 @@ static bool cs35l41_volatile_reg(struct device *dev, unsigned int reg) case CS35L41_IRQ1_RAW_STATUS2: case CS35L41_IRQ1_RAW_STATUS3: case CS35L41_IRQ1_RAW_STATUS4: - case CS35L41_IRQ1_FRC1: - case CS35L41_IRQ1_FRC2: - case CS35L41_IRQ1_FRC3: - case CS35L41_IRQ1_FRC4: - case CS35L41_IRQ1_EDGE1: - case CS35L41_IRQ1_EDGE4: - case CS35L41_IRQ1_POL1: - case CS35L41_IRQ1_POL2: - case CS35L41_IRQ1_POL3: - case CS35L41_IRQ1_POL4: - case CS35L41_IRQ1_DB3: case CS35L41_IRQ2_STATUS: case CS35L41_IRQ2_STATUS1: case CS35L41_IRQ2_STATUS2: @@ -389,54 +395,7 @@ static bool cs35l41_volatile_reg(struct device *dev, unsigned int reg) case CS35L41_IRQ2_RAW_STATUS2: case CS35L41_IRQ2_RAW_STATUS3: case CS35L41_IRQ2_RAW_STATUS4: - case CS35L41_IRQ2_FRC1: - case CS35L41_IRQ2_FRC2: - case CS35L41_IRQ2_FRC3: - case CS35L41_IRQ2_FRC4: - case CS35L41_IRQ2_EDGE1: - case CS35L41_IRQ2_EDGE4: - case CS35L41_IRQ2_POL1: - case CS35L41_IRQ2_POL2: - case CS35L41_IRQ2_POL3: - case CS35L41_IRQ2_POL4: - case CS35L41_IRQ2_DB3: case CS35L41_GPIO_STATUS1: - case CS35L41_OTP_TRIM_1: - case CS35L41_OTP_TRIM_2: - case CS35L41_OTP_TRIM_3: - case CS35L41_OTP_TRIM_4: - case CS35L41_OTP_TRIM_5: - case CS35L41_OTP_TRIM_6: - case CS35L41_OTP_TRIM_7: - case CS35L41_OTP_TRIM_8: - case CS35L41_OTP_TRIM_9: - case CS35L41_OTP_TRIM_10: - case CS35L41_OTP_TRIM_11: - case CS35L41_OTP_TRIM_12: - case CS35L41_OTP_TRIM_13: - case CS35L41_OTP_TRIM_14: - case CS35L41_OTP_TRIM_15: - case CS35L41_OTP_TRIM_16: - case CS35L41_OTP_TRIM_17: - case CS35L41_OTP_TRIM_18: - case CS35L41_OTP_TRIM_19: - case CS35L41_OTP_TRIM_20: - case CS35L41_OTP_TRIM_21: - case CS35L41_OTP_TRIM_22: - case CS35L41_OTP_TRIM_23: - case CS35L41_OTP_TRIM_24: - case CS35L41_OTP_TRIM_25: - case CS35L41_OTP_TRIM_26: - case CS35L41_OTP_TRIM_27: - case CS35L41_OTP_TRIM_28: - case CS35L41_OTP_TRIM_29: - case CS35L41_OTP_TRIM_30: - case CS35L41_OTP_TRIM_31: - case CS35L41_OTP_TRIM_32: - case CS35L41_OTP_TRIM_33: - case CS35L41_OTP_TRIM_34: - case CS35L41_OTP_TRIM_35: - case CS35L41_OTP_TRIM_36: case CS35L41_DSP_MBOX_1 ... CS35L41_DSP_VIRT2_MBOX_8: case CS35L41_DSP1_XMEM_PACK_0 ... CS35L41_DSP1_XMEM_PACK_3068: case CS35L41_DSP1_XMEM_UNPACK32_0 ... CS35L41_DSP1_XMEM_UNPACK32_2046: @@ -445,7 +404,11 @@ static bool cs35l41_volatile_reg(struct device *dev, unsigned int reg) case CS35L41_DSP1_YMEM_UNPACK32_0 ... CS35L41_DSP1_YMEM_UNPACK32_1022: case CS35L41_DSP1_YMEM_UNPACK24_0 ... CS35L41_DSP1_YMEM_UNPACK24_2045: case CS35L41_DSP1_PMEM_0 ... CS35L41_DSP1_PMEM_5114: - case CS35L41_DSP1_CCM_CORE_CTRL ... CS35L41_DSP1_WDT_STATUS: + case CS35L41_DSP1_SCRATCH1: + case CS35L41_DSP1_SCRATCH2: + case CS35L41_DSP1_SCRATCH3: + case CS35L41_DSP1_SCRATCH4: + case CS35L41_DSP1_CCM_CLK_OVERRIDE ... CS35L41_DSP1_WDT_STATUS: case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31: return true; default: -- cgit v1.2.3 From 7aa1cc1091e0a424e9e7711ca381ebe98b6865bc Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 5 Jan 2022 11:30:23 +0000 Subject: firmware: cs_dsp: Clear core reset for cache If the Halo registers are kept in the register cache the HALO_CORE_RESET bit will be retained as 1 after reset is triggered in cs_dsp_halo_start_core. This will cause subsequent writes to reset the core which is not desired. Apart from this bit the rest of the register bits are cacheable, so for safety sake clear the bit to ensure the cache is consistent. Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20220105113026.18955-6-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/firmware/cirrus/cs_dsp.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index 5af8171d6ced..e48108e694f8 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -2744,10 +2744,16 @@ EXPORT_SYMBOL_GPL(cs_dsp_stop); static int cs_dsp_halo_start_core(struct cs_dsp *dsp) { - return regmap_update_bits(dsp->regmap, - dsp->base + HALO_CCM_CORE_CONTROL, - HALO_CORE_RESET | HALO_CORE_EN, - HALO_CORE_RESET | HALO_CORE_EN); + int ret; + + ret = regmap_update_bits(dsp->regmap, dsp->base + HALO_CCM_CORE_CONTROL, + HALO_CORE_RESET | HALO_CORE_EN, + HALO_CORE_RESET | HALO_CORE_EN); + if (ret) + return ret; + + return regmap_update_bits(dsp->regmap, dsp->base + HALO_CCM_CORE_CONTROL, + HALO_CORE_RESET, 0); } static void cs_dsp_halo_stop_core(struct cs_dsp *dsp) -- cgit v1.2.3 From ba235634b138cd9d012dbe983e7920481211e132 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 5 Jan 2022 11:30:24 +0000 Subject: ASoC: wm_adsp: Add support for "toggle" preloaders In the case a device can support retaining the firmware memory across low power states it is useful for the preloader widget to only power up whilst actually loading/unloading the core, as opposed to the normal operation where the widget is powered for the entire time a firmware is preloaded onto the core. Add support for this mode and a flag to enable it. Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20220105113026.18955-7-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 14 +++++++++++--- sound/soc/codecs/wm_adsp.h | 8 ++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index c3112bf23866..f3672e3d1703 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -896,11 +896,12 @@ int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, struct wm_adsp *dsp = &dsps[mc->shift - 1]; char preload[32]; - snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->cs_dsp.name); + if (dsp->preloaded == ucontrol->value.integer.value[0]) + return 0; - dsp->preloaded = ucontrol->value.integer.value[0]; + snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->cs_dsp.name); - if (ucontrol->value.integer.value[0]) + if (ucontrol->value.integer.value[0] || dsp->toggle_preload) snd_soc_component_force_enable_pin(component, preload); else snd_soc_component_disable_pin(component, preload); @@ -909,6 +910,13 @@ int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, flush_work(&dsp->boot_work); + dsp->preloaded = ucontrol->value.integer.value[0]; + + if (dsp->toggle_preload) { + snd_soc_component_disable_pin(component, preload); + snd_soc_dapm_sync(dapm); + } + return 0; } EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put); diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index 0e2f113bd342..7f4fabbc6ad3 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -41,6 +41,14 @@ struct wm_adsp { struct list_head compr_list; struct list_head buffer_list; + + /* + * Flag indicating the preloader widget only needs power toggled + * on state change rather than held on for the duration of the + * preload, useful for devices that can retain firmware memory + * across power down. + */ + bool toggle_preload; }; #define WM_ADSP1(wname, num) \ -- cgit v1.2.3 From a319cb32e7cfd2703db3a883ce260a7b06729895 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 4 Jan 2022 15:06:13 -0300 Subject: ASoC: cs4265: Add a remove() function When the reset_gpio GPIO is used, it is better to put the codec back into reset state when the driver unbinds. Add a remove() function to accomplish that. Suggested-by: Charles Keepax Signed-off-by: Fabio Estevam Acked-by: Charles Keepax Link: https://lore.kernel.org/r/20220104180613.639317-1-festevam@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs4265.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c index b89002189a2b..4aaee1873a11 100644 --- a/sound/soc/codecs/cs4265.c +++ b/sound/soc/codecs/cs4265.c @@ -626,6 +626,16 @@ static int cs4265_i2c_probe(struct i2c_client *i2c_client, ARRAY_SIZE(cs4265_dai)); } +static int cs4265_i2c_remove(struct i2c_client *i2c) +{ + struct cs4265_private *cs4265 = i2c_get_clientdata(i2c); + + if (cs4265->reset_gpio) + gpiod_set_value_cansleep(cs4265->reset_gpio, 0); + + return 0; +} + static const struct of_device_id cs4265_of_match[] = { { .compatible = "cirrus,cs4265", }, { } @@ -645,6 +655,7 @@ static struct i2c_driver cs4265_i2c_driver = { }, .id_table = cs4265_id, .probe = cs4265_i2c_probe, + .remove = cs4265_i2c_remove, }; module_i2c_driver(cs4265_i2c_driver); -- cgit v1.2.3 From 8f4c90427a8f0ca0fcdd89d8966fcdab35fb2d4c Mon Sep 17 00:00:00 2001 From: Baole Fang Date: Wed, 5 Jan 2022 22:08:54 +0800 Subject: ALSA: hda/realtek: Add quirk for Legion Y9000X 2020 Legion Y9000X 2020 has a speaker, but the speaker doesn't work. This can be fixed by applying alc285_fixup_ideapad_s740_coef to fix the speaker's coefficients. Besides, to support the transition between the speaker and the headphone, alc287_fixup_legion_15imhg05_speakers needs to be run. Signed-off-by: Baole Fang Cc: Link: https://lore.kernel.org/r/20220105140856.4855-1-fbl718@163.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 98736f2d452c..760ac207cb43 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -6812,6 +6812,8 @@ enum { ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE, ALC233_FIXUP_NO_AUDIO_JACK, ALC256_FIXUP_MIC_NO_PRESENCE_AND_RESUME, + ALC285_FIXUP_LEGION_Y9000X_SPEAKERS, + ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE, }; static const struct hda_fixup alc269_fixups[] = { @@ -8408,6 +8410,18 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF, }, + [ALC285_FIXUP_LEGION_Y9000X_SPEAKERS] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_ideapad_s740_coef, + .chained = true, + .chain_id = ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE, + }, + [ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc287_fixup_legion_15imhg05_speakers, + .chained = true, + .chain_id = ALC269_FIXUP_THINKPAD_ACPI, + }, [ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS] = { .type = HDA_FIXUP_VERBS, //.v.verbs = legion_15imhg05_coefs, @@ -8952,6 +8966,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x31af, "ThinkCentre Station", ALC623_FIXUP_LENOVO_THINKSTATION_P340), SND_PCI_QUIRK(0x17aa, 0x3818, "Lenovo C940", ALC298_FIXUP_LENOVO_SPK_VOLUME), SND_PCI_QUIRK(0x17aa, 0x3827, "Ideapad S740", ALC285_FIXUP_IDEAPAD_S740_COEF), + SND_PCI_QUIRK(0x17aa, 0x3824, "Legion Y9000X 2020", ALC285_FIXUP_LEGION_Y9000X_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3843, "Yoga 9i", ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP), SND_PCI_QUIRK(0x17aa, 0x3813, "Legion 7i 15IMHG05", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3852, "Lenovo Yoga 7 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), -- cgit v1.2.3 From 2aac550da3257ab46e8c7944365eb4a79ccbb3a1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 5 Jan 2022 17:03:21 +0100 Subject: ALSA: hda/realtek: Re-order quirk entries for Lenovo The recent few quirk entries for Lenovo haven't been put in the right order. Let's arrange the table again. Fixes: ad7cc2d41b7a ("ALSA: hda/realtek: Quirks to enable speaker output...") Fixes: 6dc86976220c ("ALSA: hda/realtek: Add speaker fixup for some Yoga 15ITL5 devices") Fixes: 8f4c90427a8f ("ALSA: hda/realtek: Add quirk for Legion Y9000X 2020") Cc: Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 760ac207cb43..57fb3aa95426 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -8964,15 +8964,15 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3176, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x17aa, 0x3178, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x17aa, 0x31af, "ThinkCentre Station", ALC623_FIXUP_LENOVO_THINKSTATION_P340), + SND_PCI_QUIRK(0x17aa, 0x3813, "Legion 7i 15IMHG05", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3818, "Lenovo C940", ALC298_FIXUP_LENOVO_SPK_VOLUME), - SND_PCI_QUIRK(0x17aa, 0x3827, "Ideapad S740", ALC285_FIXUP_IDEAPAD_S740_COEF), + SND_PCI_QUIRK(0x17aa, 0x3819, "Lenovo 13s Gen2 ITL", ALC287_FIXUP_13S_GEN2_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3824, "Legion Y9000X 2020", ALC285_FIXUP_LEGION_Y9000X_SPEAKERS), + SND_PCI_QUIRK(0x17aa, 0x3827, "Ideapad S740", ALC285_FIXUP_IDEAPAD_S740_COEF), SND_PCI_QUIRK(0x17aa, 0x3843, "Yoga 9i", ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP), - SND_PCI_QUIRK(0x17aa, 0x3813, "Legion 7i 15IMHG05", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS), + SND_PCI_QUIRK(0x17aa, 0x384a, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3852, "Lenovo Yoga 7 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3853, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x384a, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x3819, "Lenovo 13s Gen2 ITL", ALC287_FIXUP_13S_GEN2_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI), SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC), SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo B50-70", ALC269_FIXUP_DMIC_THINKPAD_ACPI), -- cgit v1.2.3 From 7b2f3eb492dac7665c75df067e4d8e4869589f4a Mon Sep 17 00:00:00 2001 From: Lucas Tanure Date: Fri, 17 Dec 2021 11:57:05 +0000 Subject: ALSA: hda: cs35l41: Add support for CS35L41 in HDA systems Add support for CS35L41 using a new separated driver that can be used in all upcoming designs Signed-off-by: Lucas Tanure Link: https://lore.kernel.org/r/20211217115708.882525-8-tanureal@opensource.cirrus.com Signed-off-by: Takashi Iwai --- MAINTAINERS | 2 + sound/pci/hda/Kconfig | 29 +++ sound/pci/hda/Makefile | 10 + sound/pci/hda/cs35l41_hda.c | 527 ++++++++++++++++++++++++++++++++++++++++ sound/pci/hda/cs35l41_hda.h | 69 ++++++ sound/pci/hda/cs35l41_hda_i2c.c | 66 +++++ sound/pci/hda/cs35l41_hda_spi.c | 63 +++++ sound/pci/hda/hda_component.h | 20 ++ 8 files changed, 786 insertions(+) create mode 100644 sound/pci/hda/cs35l41_hda.c create mode 100644 sound/pci/hda/cs35l41_hda.h create mode 100644 sound/pci/hda/cs35l41_hda_i2c.c create mode 100644 sound/pci/hda/cs35l41_hda_spi.c create mode 100644 sound/pci/hda/hda_component.h diff --git a/MAINTAINERS b/MAINTAINERS index 74d7d20b9d19..e8d4805e093e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4511,10 +4511,12 @@ F: drivers/media/cec/i2c/ch7322.c CIRRUS LOGIC AUDIO CODEC DRIVERS M: James Schulman M: David Rhodes +M: Lucas Tanure L: alsa-devel@alsa-project.org (moderated for non-subscribers) L: patches@opensource.cirrus.com S: Maintained F: Documentation/devicetree/bindings/sound/cirrus,cs* +F: sound/pci/hda/cs* F: sound/soc/codecs/cs* CIRRUS LOGIC DSP FIRMWARE DRIVER diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index ab9d2746e804..84cefc006f29 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -91,6 +91,35 @@ config SND_HDA_PATCH_LOADER start up. The "patch" file can be specified via patch module option, such as patch=hda-init. +config SND_HDA_SCODEC_CS35L41 + tristate + +config SND_HDA_SCODEC_CS35L41_I2C + tristate "Build CS35L41 HD-audio side codec support for I2C Bus" + depends on ACPI + select SND_HDA_GENERIC + select SND_SOC_CS35L41_LIB + select SND_HDA_SCODEC_CS35L41 + help + Say Y or M here to include CS35L41 I2C HD-audio side codec support + in snd-hda-intel driver, such as ALC287. + +comment "Set to Y if you want auto-loading the side codec driver" + depends on SND_HDA=y && SND_HDA_SCODEC_CS35L41_I2C=m + +config SND_HDA_SCODEC_CS35L41_SPI + tristate "Build CS35L41 HD-audio codec support for SPI Bus" + depends on ACPI + select SND_HDA_GENERIC + select SND_SOC_CS35L41_LIB + select SND_HDA_SCODEC_CS35L41 + help + Say Y or M here to include CS35L41 SPI HD-audio side codec support + in snd-hda-intel driver, such as ALC287. + +comment "Set to Y if you want auto-loading the side codec driver" + depends on SND_HDA=y && SND_HDA_SCODEC_CS35L41_SPI=m + config SND_HDA_CODEC_REALTEK tristate "Build Realtek HD-audio codec support" select SND_HDA_GENERIC diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index b8fa682ce66a..3e7bc608d45f 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -27,6 +27,11 @@ snd-hda-codec-conexant-objs := patch_conexant.o snd-hda-codec-via-objs := patch_via.o snd-hda-codec-hdmi-objs := patch_hdmi.o hda_eld.o +# side codecs +snd-hda-scodec-cs35l41-objs := cs35l41_hda.o +snd-hda-scodec-cs35l41-i2c-objs := cs35l41_hda_i2c.o +snd-hda-scodec-cs35l41-spi-objs := cs35l41_hda_spi.o + # common driver obj-$(CONFIG_SND_HDA) := snd-hda-codec.o @@ -45,6 +50,11 @@ obj-$(CONFIG_SND_HDA_CODEC_CONEXANT) += snd-hda-codec-conexant.o obj-$(CONFIG_SND_HDA_CODEC_VIA) += snd-hda-codec-via.o obj-$(CONFIG_SND_HDA_CODEC_HDMI) += snd-hda-codec-hdmi.o +# side codecs +obj-$(CONFIG_SND_HDA_SCODEC_CS35L41) += snd-hda-scodec-cs35l41.o +obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_I2C) += snd-hda-scodec-cs35l41-i2c.o +obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_SPI) += snd-hda-scodec-cs35l41-spi.o + # this must be the last entry after codec drivers; # otherwise the codec patches won't be hooked before the PCI probe # when built in kernel diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c new file mode 100644 index 000000000000..aa5bb6977792 --- /dev/null +++ b/sound/pci/hda/cs35l41_hda.c @@ -0,0 +1,527 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// cs35l41.c -- CS35l41 ALSA HDA audio driver +// +// Copyright 2021 Cirrus Logic, Inc. +// +// Author: Lucas Tanure + +#include +#include +#include +#include "hda_local.h" +#include "hda_auto_parser.h" +#include "hda_jack.h" +#include "hda_generic.h" +#include "hda_component.h" +#include "cs35l41_hda.h" + +static const struct reg_sequence cs35l41_hda_config[] = { + { CS35L41_PLL_CLK_CTRL, 0x00000430 }, //3200000Hz, BCLK Input, PLL_REFCLK_EN = 1 + { CS35L41_GLOBAL_CLK_CTRL, 0x00000003 }, //GLOBAL_FS = 48 kHz + { CS35L41_SP_ENABLES, 0x00010000 }, //ASP_RX1_EN = 1 + { CS35L41_SP_RATE_CTRL, 0x00000021 }, //ASP_BCLK_FREQ = 3.072 MHz + { CS35L41_SP_FORMAT, 0x20200200 }, //24 bits, I2S, BCLK Slave, FSYNC Slave + { CS35L41_DAC_PCM1_SRC, 0x00000008 }, //DACPCM1_SRC = ASPRX1 + { CS35L41_AMP_DIG_VOL_CTRL, 0x00000000 }, //AMP_VOL_PCM 0.0 dB + { CS35L41_AMP_GAIN_CTRL, 0x00000084 }, //AMP_GAIN_PCM 4.5 dB + { CS35L41_PWR_CTRL2, 0x00000001 }, //AMP_EN = 1 +}; + +static const struct reg_sequence cs35l41_hda_start_bst[] = { + { CS35L41_PWR_CTRL2, 0x00000021 }, //BST_EN = 10, AMP_EN = 1 + { CS35L41_PWR_CTRL1, 0x00000001, 3000}, // set GLOBAL_EN = 1 +}; + +static const struct reg_sequence cs35l41_hda_stop_bst[] = { + { CS35L41_PWR_CTRL1, 0x00000000, 3000}, // set GLOBAL_EN = 0 +}; + +// only on amps where GPIO1 is used to control ext. VSPK switch +static const struct reg_sequence cs35l41_start_ext_vspk[] = { + { 0x00000040, 0x00000055 }, + { 0x00000040, 0x000000AA }, + { 0x00007438, 0x00585941 }, + { 0x00007414, 0x08C82222 }, + { 0x0000742C, 0x00000009 }, + { 0x00011008, 0x00008001 }, + { 0x0000742C, 0x0000000F }, + { 0x0000742C, 0x00000079 }, + { 0x00007438, 0x00585941 }, + { CS35L41_PWR_CTRL1, 0x00000001, 3000}, // set GLOBAL_EN = 1 + { 0x0000742C, 0x000000F9 }, + { 0x00007438, 0x00580941 }, + { 0x00000040, 0x000000CC }, + { 0x00000040, 0x00000033 }, +}; + +//only on amps where GPIO1 is used to control ext. VSPK switch +static const struct reg_sequence cs35l41_stop_ext_vspk[] = { + { 0x00000040, 0x00000055 }, + { 0x00000040, 0x000000AA }, + { 0x00007438, 0x00585941 }, + { 0x00002014, 0x00000000, 3000}, //set GLOBAL_EN = 0 + { 0x0000742C, 0x00000009 }, + { 0x00007438, 0x00580941 }, + { 0x00011008, 0x00000001 }, + { 0x0000393C, 0x000000C0, 6000}, + { 0x0000393C, 0x00000000 }, + { 0x00007414, 0x00C82222 }, + { 0x0000742C, 0x00000000 }, + { 0x00000040, 0x000000CC }, + { 0x00000040, 0x00000033 }, +}; + +static const struct reg_sequence cs35l41_safe_to_active[] = { + { 0x00000040, 0x00000055 }, + { 0x00000040, 0x000000AA }, + { 0x0000742C, 0x0000000F }, + { 0x0000742C, 0x00000079 }, + { 0x00007438, 0x00585941 }, + { CS35L41_PWR_CTRL1, 0x00000001, 2000 }, //GLOBAL_EN = 1 + { 0x0000742C, 0x000000F9 }, + { 0x00007438, 0x00580941 }, + { 0x00000040, 0x000000CC }, + { 0x00000040, 0x00000033 }, +}; + +static const struct reg_sequence cs35l41_active_to_safe[] = { + { 0x00000040, 0x00000055 }, + { 0x00000040, 0x000000AA }, + { 0x00007438, 0x00585941 }, + { CS35L41_AMP_DIG_VOL_CTRL, 0x0000A678 }, //AMP_VOL_PCM Mute + { CS35L41_PWR_CTRL2, 0x00000000 }, //AMP_EN = 0 + { CS35L41_PWR_CTRL1, 0x00000000 }, + { 0x0000742C, 0x00000009, 2000 }, + { 0x00007438, 0x00580941 }, + { 0x00000040, 0x000000CC }, + { 0x00000040, 0x00000033 }, +}; + +static const struct reg_sequence cs35l41_reset_to_safe[] = { + { 0x00000040, 0x00000055 }, + { 0x00000040, 0x000000AA }, + { 0x00007438, 0x00585941 }, + { 0x00007414, 0x08C82222 }, + { 0x0000742C, 0x00000009 }, + { 0x00000040, 0x000000CC }, + { 0x00000040, 0x00000033 }, +}; + +static const struct cs35l41_hda_reg_sequence cs35l41_hda_reg_seq_no_bst = { + .probe = cs35l41_reset_to_safe, + .num_probe = ARRAY_SIZE(cs35l41_reset_to_safe), + .open = cs35l41_hda_config, + .num_open = ARRAY_SIZE(cs35l41_hda_config), + .prepare = cs35l41_safe_to_active, + .num_prepare = ARRAY_SIZE(cs35l41_safe_to_active), + .cleanup = cs35l41_active_to_safe, + .num_cleanup = ARRAY_SIZE(cs35l41_active_to_safe), +}; + +static const struct cs35l41_hda_reg_sequence cs35l41_hda_reg_seq_ext_bst = { + .open = cs35l41_hda_config, + .num_open = ARRAY_SIZE(cs35l41_hda_config), + .prepare = cs35l41_start_ext_vspk, + .num_prepare = ARRAY_SIZE(cs35l41_start_ext_vspk), + .cleanup = cs35l41_stop_ext_vspk, + .num_cleanup = ARRAY_SIZE(cs35l41_stop_ext_vspk), +}; + +static const struct cs35l41_hda_reg_sequence cs35l41_hda_reg_seq_int_bst = { + .open = cs35l41_hda_config, + .num_open = ARRAY_SIZE(cs35l41_hda_config), + .prepare = cs35l41_hda_start_bst, + .num_prepare = ARRAY_SIZE(cs35l41_hda_start_bst), + .cleanup = cs35l41_hda_stop_bst, + .num_cleanup = ARRAY_SIZE(cs35l41_hda_stop_bst), +}; + +static void cs35l41_hda_playback_hook(struct device *dev, int action) +{ + struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); + const struct cs35l41_hda_reg_sequence *reg_seq = cs35l41->reg_seq; + struct regmap *reg = cs35l41->regmap; + int ret = 0; + + switch (action) { + case HDA_GEN_PCM_ACT_OPEN: + if (reg_seq->open) + ret = regmap_multi_reg_write(reg, reg_seq->open, reg_seq->num_open); + break; + case HDA_GEN_PCM_ACT_PREPARE: + if (reg_seq->prepare) + ret = regmap_multi_reg_write(reg, reg_seq->prepare, reg_seq->num_prepare); + break; + case HDA_GEN_PCM_ACT_CLEANUP: + if (reg_seq->cleanup) + ret = regmap_multi_reg_write(reg, reg_seq->cleanup, reg_seq->num_cleanup); + break; + case HDA_GEN_PCM_ACT_CLOSE: + if (reg_seq->close) + ret = regmap_multi_reg_write(reg, reg_seq->close, reg_seq->num_close); + break; + } + + if (ret) + dev_warn(cs35l41->dev, "Failed to apply multi reg write: %d\n", ret); + +} + +static int cs35l41_hda_channel_map(struct device *dev, unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); + + return cs35l41_set_channels(cs35l41->dev, cs35l41->regmap, tx_num, tx_slot, rx_num, + rx_slot); +} + +static int cs35l41_hda_bind(struct device *dev, struct device *master, void *master_data) +{ + struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); + struct hda_component *comps = master_data; + + if (comps && cs35l41->index >= 0 && cs35l41->index < HDA_MAX_COMPONENTS) + comps = &comps[cs35l41->index]; + else + return -EINVAL; + + if (!comps->dev) { + comps->dev = dev; + strscpy(comps->name, dev_name(dev), sizeof(comps->name)); + comps->playback_hook = cs35l41_hda_playback_hook; + comps->set_channel_map = cs35l41_hda_channel_map; + return 0; + } + + return -EBUSY; +} + +static void cs35l41_hda_unbind(struct device *dev, struct device *master, void *master_data) +{ + struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); + struct hda_component *comps = master_data; + + if (comps[cs35l41->index].dev == dev) + memset(&comps[cs35l41->index], 0, sizeof(*comps)); +} + +static const struct component_ops cs35l41_hda_comp_ops = { + .bind = cs35l41_hda_bind, + .unbind = cs35l41_hda_unbind, +}; + +static int cs35l41_hda_apply_properties(struct cs35l41_hda *cs35l41, + const struct cs35l41_hda_hw_config *hw_cfg) +{ + bool internal_boost = false; + int ret; + + if (!hw_cfg) { + cs35l41->reg_seq = &cs35l41_hda_reg_seq_no_bst; + return 0; + } + + if (hw_cfg->bst_ind || hw_cfg->bst_cap || hw_cfg->bst_ipk) + internal_boost = true; + + switch (hw_cfg->gpio1_func) { + case CS35l41_VSPK_SWITCH: + regmap_update_bits(cs35l41->regmap, CS35L41_GPIO_PAD_CONTROL, + CS35L41_GPIO1_CTRL_MASK, 1 << CS35L41_GPIO1_CTRL_SHIFT); + break; + case CS35l41_SYNC: + regmap_update_bits(cs35l41->regmap, CS35L41_GPIO_PAD_CONTROL, + CS35L41_GPIO1_CTRL_MASK, 2 << CS35L41_GPIO1_CTRL_SHIFT); + break; + } + + switch (hw_cfg->gpio2_func) { + case CS35L41_INTERRUPT: + regmap_update_bits(cs35l41->regmap, CS35L41_GPIO_PAD_CONTROL, + CS35L41_GPIO2_CTRL_MASK, 2 << CS35L41_GPIO2_CTRL_SHIFT); + break; + } + + if (internal_boost) { + cs35l41->reg_seq = &cs35l41_hda_reg_seq_int_bst; + if (!(hw_cfg->bst_ind && hw_cfg->bst_cap && hw_cfg->bst_ipk)) + return -EINVAL; + ret = cs35l41_boost_config(cs35l41->dev, cs35l41->regmap, + hw_cfg->bst_ind, hw_cfg->bst_cap, hw_cfg->bst_ipk); + if (ret) + return ret; + } else { + cs35l41->reg_seq = &cs35l41_hda_reg_seq_ext_bst; + } + + ret = cs35l41_hda_channel_map(cs35l41->dev, 0, NULL, 1, (unsigned int *)&hw_cfg->spk_pos); + if (ret) + return ret; + + return 0; +} + +static struct cs35l41_hda_hw_config *cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, + const char *hid, int id) +{ + struct cs35l41_hda_hw_config *hw_cfg; + u32 values[HDA_MAX_COMPONENTS]; + struct acpi_device *adev; + struct device *acpi_dev; + char *property; + size_t nval; + int i, ret; + + adev = acpi_dev_get_first_match_dev(hid, NULL, -1); + if (!adev) { + dev_err(cs35l41->dev, "Failed to find an ACPI device for %s\n", hid); + return ERR_PTR(-ENODEV); + } + + acpi_dev = get_device(acpi_get_first_physical_node(adev)); + acpi_dev_put(adev); + + property = "cirrus,dev-index"; + ret = device_property_count_u32(acpi_dev, property); + if (ret <= 0) + goto no_acpi_dsd; + + if (ret > ARRAY_SIZE(values)) { + ret = -EINVAL; + goto err; + } + nval = ret; + + ret = device_property_read_u32_array(acpi_dev, property, values, nval); + if (ret) + goto err; + + cs35l41->index = -1; + for (i = 0; i < nval; i++) { + if (values[i] == id) { + cs35l41->index = i; + break; + } + } + if (cs35l41->index == -1) { + dev_err(cs35l41->dev, "No index found in %s\n", property); + ret = -ENODEV; + goto err; + } + + /* No devm_ version as CLSA0100, in no_acpi_dsd case, can't use devm version */ + cs35l41->reset_gpio = fwnode_gpiod_get_index(&adev->fwnode, "reset", cs35l41->index, + GPIOD_OUT_LOW, "cs35l41-reset"); + + hw_cfg = kzalloc(sizeof(*hw_cfg), GFP_KERNEL); + if (!hw_cfg) { + ret = -ENOMEM; + goto err; + } + + property = "cirrus,speaker-position"; + ret = device_property_read_u32_array(acpi_dev, property, values, nval); + if (ret) + goto err_free; + hw_cfg->spk_pos = values[cs35l41->index]; + + property = "cirrus,gpio1-func"; + ret = device_property_read_u32_array(acpi_dev, property, values, nval); + if (ret) + goto err_free; + hw_cfg->gpio1_func = values[cs35l41->index]; + + property = "cirrus,gpio2-func"; + ret = device_property_read_u32_array(acpi_dev, property, values, nval); + if (ret) + goto err_free; + hw_cfg->gpio2_func = values[cs35l41->index]; + + property = "cirrus,boost-peak-milliamp"; + ret = device_property_read_u32_array(acpi_dev, property, values, nval); + if (ret == 0) + hw_cfg->bst_ipk = values[cs35l41->index]; + + property = "cirrus,boost-ind-nanohenry"; + ret = device_property_read_u32_array(acpi_dev, property, values, nval); + if (ret == 0) + hw_cfg->bst_ind = values[cs35l41->index]; + + property = "cirrus,boost-cap-microfarad"; + ret = device_property_read_u32_array(acpi_dev, property, values, nval); + if (ret == 0) + hw_cfg->bst_cap = values[cs35l41->index]; + + put_device(acpi_dev); + + return hw_cfg; + +err_free: + kfree(hw_cfg); +err: + put_device(acpi_dev); + dev_err(cs35l41->dev, "Failed property %s: %d\n", property, ret); + + return ERR_PTR(ret); + +no_acpi_dsd: + /* + * Device CLSA0100 doesn't have _DSD so a gpiod_get by the label reset won't work. + * And devices created by i2c-multi-instantiate don't have their device struct pointing to + * the correct fwnode, so acpi_dev must be used here + * And devm functions expect that the device requesting the resource has the correct + * fwnode + */ + if (strncmp(hid, "CLSA0100", 8) != 0) + return ERR_PTR(-EINVAL); + + /* check I2C address to assign the index */ + cs35l41->index = id == 0x40 ? 0 : 1; + cs35l41->reset_gpio = gpiod_get_index(acpi_dev, NULL, 0, GPIOD_OUT_HIGH); + cs35l41->vspk_always_on = true; + put_device(acpi_dev); + + return NULL; +} + +int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int irq, + struct regmap *regmap) +{ + unsigned int int_sts, regid, reg_revid, mtl_revid, chipid, int_status; + struct cs35l41_hda_hw_config *acpi_hw_cfg; + struct cs35l41_hda *cs35l41; + int ret; + + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + cs35l41 = devm_kzalloc(dev, sizeof(*cs35l41), GFP_KERNEL); + if (!cs35l41) + return -ENOMEM; + + cs35l41->dev = dev; + cs35l41->irq = irq; + cs35l41->regmap = regmap; + dev_set_drvdata(dev, cs35l41); + + acpi_hw_cfg = cs35l41_hda_read_acpi(cs35l41, device_name, id); + if (IS_ERR(acpi_hw_cfg)) + return PTR_ERR(acpi_hw_cfg); + + if (IS_ERR(cs35l41->reset_gpio)) { + ret = PTR_ERR(cs35l41->reset_gpio); + cs35l41->reset_gpio = NULL; + if (ret == -EBUSY) { + dev_info(cs35l41->dev, "Reset line busy, assuming shared reset\n"); + } else { + if (ret != -EPROBE_DEFER) + dev_err(cs35l41->dev, "Failed to get reset GPIO: %d\n", ret); + goto err; + } + } + if (cs35l41->reset_gpio) { + usleep_range(2000, 2100); + gpiod_set_value_cansleep(cs35l41->reset_gpio, 1); + } + + usleep_range(2000, 2100); + + ret = regmap_read_poll_timeout(cs35l41->regmap, CS35L41_IRQ1_STATUS4, int_status, + int_status & CS35L41_OTP_BOOT_DONE, 1000, 100000); + if (ret) { + dev_err(cs35l41->dev, "Failed waiting for OTP_BOOT_DONE: %d\n", ret); + goto err; + } + + ret = regmap_read(cs35l41->regmap, CS35L41_IRQ1_STATUS3, &int_sts); + if (ret || (int_sts & CS35L41_OTP_BOOT_ERR)) { + dev_err(cs35l41->dev, "OTP Boot error\n"); + ret = -EIO; + goto err; + } + + ret = regmap_read(cs35l41->regmap, CS35L41_DEVID, ®id); + if (ret) { + dev_err(cs35l41->dev, "Get Device ID failed: %d\n", ret); + goto err; + } + + ret = regmap_read(cs35l41->regmap, CS35L41_REVID, ®_revid); + if (ret) { + dev_err(cs35l41->dev, "Get Revision ID failed: %d\n", ret); + goto err; + } + + mtl_revid = reg_revid & CS35L41_MTLREVID_MASK; + + chipid = (mtl_revid % 2) ? CS35L41R_CHIP_ID : CS35L41_CHIP_ID; + if (regid != chipid) { + dev_err(cs35l41->dev, "CS35L41 Device ID (%X). Expected ID %X\n", regid, chipid); + ret = -ENODEV; + goto err; + } + + ret = cs35l41_register_errata_patch(cs35l41->dev, cs35l41->regmap, reg_revid); + if (ret) + goto err; + + ret = cs35l41_otp_unpack(cs35l41->dev, cs35l41->regmap); + if (ret) { + dev_err(cs35l41->dev, "OTP Unpack failed: %d\n", ret); + goto err; + } + + ret = cs35l41_hda_apply_properties(cs35l41, acpi_hw_cfg); + if (ret) + goto err; + kfree(acpi_hw_cfg); + + if (cs35l41->reg_seq->probe) { + ret = regmap_register_patch(cs35l41->regmap, cs35l41->reg_seq->probe, + cs35l41->reg_seq->num_probe); + if (ret) { + dev_err(cs35l41->dev, "Fail to apply probe reg patch: %d\n", ret); + goto err; + } + } + + ret = component_add(cs35l41->dev, &cs35l41_hda_comp_ops); + if (ret) { + dev_err(cs35l41->dev, "Register component failed: %d\n", ret); + goto err; + } + + dev_info(cs35l41->dev, "Cirrus Logic CS35L41 (%x), Revision: %02X\n", regid, reg_revid); + + return 0; + +err: + kfree(acpi_hw_cfg); + if (!cs35l41->vspk_always_on) + gpiod_set_value_cansleep(cs35l41->reset_gpio, 0); + gpiod_put(cs35l41->reset_gpio); + + return ret; +} +EXPORT_SYMBOL_GPL(cs35l41_hda_probe); + +int cs35l41_hda_remove(struct device *dev) +{ + struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); + + component_del(cs35l41->dev, &cs35l41_hda_comp_ops); + + if (!cs35l41->vspk_always_on) + gpiod_set_value_cansleep(cs35l41->reset_gpio, 0); + gpiod_put(cs35l41->reset_gpio); + + return 0; +} +EXPORT_SYMBOL_GPL(cs35l41_hda_remove); + + +MODULE_DESCRIPTION("CS35L41 HDA Driver"); +MODULE_AUTHOR("Lucas Tanure, Cirrus Logic Inc, "); +MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/cs35l41_hda.h b/sound/pci/hda/cs35l41_hda.h new file mode 100644 index 000000000000..76c69a8a22f6 --- /dev/null +++ b/sound/pci/hda/cs35l41_hda.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * cs35l41_hda.h -- CS35L41 ALSA HDA audio driver + * + * Copyright 2021 Cirrus Logic, Inc. + * + * Author: Lucas Tanure + */ + +#ifndef __CS35L41_HDA_H__ +#define __CS35L41_HDA_H__ + +#include +#include +#include +#include + +enum cs35l41_hda_spk_pos { + CS35l41_LEFT, + CS35l41_RIGHT, +}; + +enum cs35l41_hda_gpio_function { + CS35L41_NOT_USED, + CS35l41_VSPK_SWITCH, + CS35L41_INTERRUPT, + CS35l41_SYNC, +}; + +struct cs35l41_hda_reg_sequence { + const struct reg_sequence *probe; + unsigned int num_probe; + const struct reg_sequence *open; + unsigned int num_open; + const struct reg_sequence *prepare; + unsigned int num_prepare; + const struct reg_sequence *cleanup; + unsigned int num_cleanup; + const struct reg_sequence *close; + unsigned int num_close; +}; + +struct cs35l41_hda_hw_config { + unsigned int spk_pos; + unsigned int gpio1_func; + unsigned int gpio2_func; + int bst_ind; + int bst_ipk; + int bst_cap; +}; + +struct cs35l41_hda { + struct device *dev; + struct regmap *regmap; + struct gpio_desc *reset_gpio; + const struct cs35l41_hda_reg_sequence *reg_seq; + + int irq; + int index; + + /* Don't put the AMP in reset of VSPK can not be turned off */ + bool vspk_always_on; +}; + +int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int irq, + struct regmap *regmap); +int cs35l41_hda_remove(struct device *dev); + +#endif /*__CS35L41_HDA_H__*/ diff --git a/sound/pci/hda/cs35l41_hda_i2c.c b/sound/pci/hda/cs35l41_hda_i2c.c new file mode 100644 index 000000000000..4a9462fb5c14 --- /dev/null +++ b/sound/pci/hda/cs35l41_hda_i2c.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// cs35l41.c -- CS35l41 HDA I2C driver +// +// Copyright 2021 Cirrus Logic, Inc. +// +// Author: Lucas Tanure + +#include +#include +#include + +#include "cs35l41_hda.h" + +static int cs35l41_hda_i2c_probe(struct i2c_client *clt, const struct i2c_device_id *id) +{ + const char *device_name; + + /* Compare against the device name so it works for I2C, normal ACPI + * and for ACPI by i2c-multi-instantiate matching cases + */ + if (strstr(dev_name(&clt->dev), "CLSA0100")) + device_name = "CLSA0100"; + else if (strstr(dev_name(&clt->dev), "CSC3551")) + device_name = "CSC3551"; + else + return -ENODEV; + + return cs35l41_hda_probe(&clt->dev, device_name, clt->addr, clt->irq, + devm_regmap_init_i2c(clt, &cs35l41_regmap_i2c)); +} + +static int cs35l41_hda_i2c_remove(struct i2c_client *clt) +{ + return cs35l41_hda_remove(&clt->dev); +} + +static const struct i2c_device_id cs35l41_hda_i2c_id[] = { + { "cs35l41-hda", 0 }, + {} +}; + +#ifdef CONFIG_ACPI +static const struct acpi_device_id cs35l41_acpi_hda_match[] = { + {"CLSA0100", 0 }, + {"CSC3551", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_hda_match); +#endif + +static struct i2c_driver cs35l41_i2c_driver = { + .driver = { + .name = "cs35l41-hda", + .acpi_match_table = ACPI_PTR(cs35l41_acpi_hda_match), + }, + .id_table = cs35l41_hda_i2c_id, + .probe = cs35l41_hda_i2c_probe, + .remove = cs35l41_hda_i2c_remove, +}; + +module_i2c_driver(cs35l41_i2c_driver); + +MODULE_DESCRIPTION("HDA CS35L41 driver"); +MODULE_AUTHOR("Lucas Tanure "); +MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/cs35l41_hda_spi.c b/sound/pci/hda/cs35l41_hda_spi.c new file mode 100644 index 000000000000..77426e96c58f --- /dev/null +++ b/sound/pci/hda/cs35l41_hda_spi.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// cs35l41.c -- CS35l41 HDA SPI driver +// +// Copyright 2021 Cirrus Logic, Inc. +// +// Author: Lucas Tanure + +#include +#include +#include + +#include "cs35l41_hda.h" + +static int cs35l41_hda_spi_probe(struct spi_device *spi) +{ + const char *device_name; + + /* Compare against the device name so it works for SPI, normal ACPI + * and for ACPI by spi-multi-instantiate matching cases + */ + if (strstr(dev_name(&spi->dev), "CSC3551")) + device_name = "CSC3551"; + else + return -ENODEV; + + return cs35l41_hda_probe(&spi->dev, device_name, spi->chip_select, spi->irq, + devm_regmap_init_spi(spi, &cs35l41_regmap_spi)); +} + +static int cs35l41_hda_spi_remove(struct spi_device *spi) +{ + return cs35l41_hda_remove(&spi->dev); +} + +static const struct spi_device_id cs35l41_hda_spi_id[] = { + { "cs35l41-hda", 0 }, + {} +}; + +#ifdef CONFIG_ACPI +static const struct acpi_device_id cs35l41_acpi_hda_match[] = { + { "CSC3551", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_hda_match); +#endif + +static struct spi_driver cs35l41_spi_driver = { + .driver = { + .name = "cs35l41_hda", + .acpi_match_table = ACPI_PTR(cs35l41_acpi_hda_match), + }, + .id_table = cs35l41_hda_spi_id, + .probe = cs35l41_hda_spi_probe, + .remove = cs35l41_hda_spi_remove, +}; + +module_spi_driver(cs35l41_spi_driver); + +MODULE_DESCRIPTION("HDA CS35L41 driver"); +MODULE_AUTHOR("Lucas Tanure "); +MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/hda_component.h b/sound/pci/hda/hda_component.h new file mode 100644 index 000000000000..2e52be6db9c2 --- /dev/null +++ b/sound/pci/hda/hda_component.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * HD audio Component Binding Interface + * + * Copyright (C) 2021 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#include + +#define HDA_MAX_COMPONENTS 4 +#define HDA_MAX_NAME_SIZE 50 + +struct hda_component { + struct device *dev; + char name[HDA_MAX_NAME_SIZE]; + void (*playback_hook)(struct device *dev, int action); + int (*set_channel_map)(struct device *dev, unsigned int rx_num, unsigned int *rx_slot, + unsigned int tx_num, unsigned int *tx_slot); +}; -- cgit v1.2.3 From d3dca026375f2be550041b75833f2e3238738a70 Mon Sep 17 00:00:00 2001 From: Lucas Tanure Date: Fri, 17 Dec 2021 11:57:07 +0000 Subject: ALSA: hda/realtek: Add support for Legion 7 16ACHg6 laptop Add Support for CS35L41 using the component binding method [ corrected the quirk entry position by tiwai ] Signed-off-by: Lucas Tanure Link: https://lore.kernel.org/r/20211217115708.882525-10-tanureal@opensource.cirrus.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 107 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 57fb3aa95426..395483b9753b 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -25,6 +25,7 @@ #include "hda_auto_parser.h" #include "hda_jack.h" #include "hda_generic.h" +#include "hda_component.h" /* keep halting ALC5505 DSP, for power saving */ #define HALT_REALTEK_ALC5505 @@ -126,6 +127,10 @@ struct alc_spec { unsigned int coef0; struct input_dev *kb_dev; u8 alc_mute_keycode_map[1]; + + /* component binding */ + struct component_match *match; + struct hda_component comps[HDA_MAX_COMPONENTS]; }; /* @@ -6525,6 +6530,102 @@ static void alc287_fixup_legion_15imhg05_speakers(struct hda_codec *codec, } } +static int comp_match_dev_name(struct device *dev, void *data) +{ + return strcmp(dev_name(dev), data) == 0; +} + +static int find_comp_by_dev_name(struct alc_spec *spec, const char *name) +{ + int i; + + for (i = 0; i < HDA_MAX_COMPONENTS; i++) { + if (strcmp(spec->comps[i].name, name) == 0) + return i; + } + + return -ENODEV; +} + +static int comp_bind(struct device *dev) +{ + struct hda_codec *cdc = dev_to_hda_codec(dev); + struct alc_spec *spec = cdc->spec; + + return component_bind_all(dev, spec->comps); +} + +static void comp_unbind(struct device *dev) +{ + struct hda_codec *cdc = dev_to_hda_codec(dev); + struct alc_spec *spec = cdc->spec; + + component_unbind_all(dev, spec->comps); +} + +static const struct component_master_ops comp_master_ops = { + .bind = comp_bind, + .unbind = comp_unbind, +}; + +static void comp_generic_playback_hook(struct hda_pcm_stream *hinfo, struct hda_codec *cdc, + struct snd_pcm_substream *sub, int action) +{ + struct alc_spec *spec = cdc->spec; + int i; + + for (i = 0; i < HDA_MAX_COMPONENTS; i++) { + if (spec->comps[i].dev) + spec->comps[i].playback_hook(spec->comps[i].dev, action); + } +} + +static void alc287_legion_16achg6_playback_hook(struct hda_pcm_stream *hinfo, struct hda_codec *cdc, + struct snd_pcm_substream *sub, int action) +{ + struct alc_spec *spec = cdc->spec; + unsigned int rx_slot; + int i; + + switch (action) { + case HDA_GEN_PCM_ACT_PREPARE: + rx_slot = 0; + i = find_comp_by_dev_name(spec, "i2c-CLSA0100:00-cs35l41-hda.0"); + if (i >= 0) + spec->comps[i].set_channel_map(spec->comps[i].dev, 0, NULL, 1, &rx_slot); + + rx_slot = 1; + i = find_comp_by_dev_name(spec, "i2c-CLSA0100:00-cs35l41-hda.1"); + if (i >= 0) + spec->comps[i].set_channel_map(spec->comps[i].dev, 0, NULL, 1, &rx_slot); + break; + } + + comp_generic_playback_hook(hinfo, cdc, sub, action); +} + +static void alc287_fixup_legion_16achg6_speakers(struct hda_codec *cdc, const struct hda_fixup *fix, + int action) +{ + struct device *dev = hda_codec_dev(cdc); + struct alc_spec *spec = cdc->spec; + int ret; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + component_match_add(dev, &spec->match, comp_match_dev_name, + "i2c-CLSA0100:00-cs35l41-hda.0"); + component_match_add(dev, &spec->match, comp_match_dev_name, + "i2c-CLSA0100:00-cs35l41-hda.1"); + ret = component_master_add_with_match(dev, &comp_master_ops, spec->match); + if (ret) + codec_err(cdc, "Fail to register component aggregator %d\n", ret); + else + spec->gen.pcm_playback_hook = alc287_legion_16achg6_playback_hook; + break; + } +} + /* for alc295_fixup_hp_top_speakers */ #include "hp_x360_helper.c" @@ -6814,6 +6915,7 @@ enum { ALC256_FIXUP_MIC_NO_PRESENCE_AND_RESUME, ALC285_FIXUP_LEGION_Y9000X_SPEAKERS, ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE, + ALC287_FIXUP_LEGION_16ACHG6, }; static const struct hda_fixup alc269_fixups[] = { @@ -8556,6 +8658,10 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC }, + [ALC287_FIXUP_LEGION_16ACHG6] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc287_fixup_legion_16achg6_speakers, + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -8970,6 +9076,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3824, "Legion Y9000X 2020", ALC285_FIXUP_LEGION_Y9000X_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3827, "Ideapad S740", ALC285_FIXUP_IDEAPAD_S740_COEF), SND_PCI_QUIRK(0x17aa, 0x3843, "Yoga 9i", ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP), + SND_PCI_QUIRK(0x17aa, 0x3847, "Legion 7 16ACHG6", ALC287_FIXUP_LEGION_16ACHG6), SND_PCI_QUIRK(0x17aa, 0x384a, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3852, "Lenovo Yoga 7 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3853, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), -- cgit v1.2.3 From ae7abe36e352eddf8e30d3b1ea3fb402514ba13b Mon Sep 17 00:00:00 2001 From: Stefan Binding Date: Fri, 17 Dec 2021 11:57:08 +0000 Subject: ALSA: hda/realtek: Add CS35L41 support for Thinkpad laptops Add support for two CS35L41 using I2C bus and the component binding method [ Fix the entries to be sorted order by tiwai ] Signed-off-by: Stefan Binding Signed-off-by: Lucas Tanure Link: https://lore.kernel.org/r/20211217115708.882525-11-tanureal@opensource.cirrus.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 395483b9753b..2e20bbfe5357 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -6580,6 +6580,37 @@ static void comp_generic_playback_hook(struct hda_pcm_stream *hinfo, struct hda_ } } +static void cs35l41_generic_fixup(struct hda_codec *cdc, int action, const char *bus, + const char *hid, int count) +{ + struct device *dev = hda_codec_dev(cdc); + struct alc_spec *spec = cdc->spec; + char *name; + int ret, i; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + for (i = 0; i < count; i++) { + name = devm_kasprintf(dev, GFP_KERNEL, + "%s-%s:00-cs35l41-hda.%d", bus, hid, i); + if (!name) + return; + component_match_add(dev, &spec->match, comp_match_dev_name, name); + } + ret = component_master_add_with_match(dev, &comp_master_ops, spec->match); + if (ret) + codec_err(cdc, "Fail to register component aggregator %d\n", ret); + else + spec->gen.pcm_playback_hook = comp_generic_playback_hook; + break; + } +} + +static void cs35l41_fixup_i2c_two(struct hda_codec *cdc, const struct hda_fixup *fix, int action) +{ + cs35l41_generic_fixup(cdc, action, "i2c", "CSC3551", 2); +} + static void alc287_legion_16achg6_playback_hook(struct hda_pcm_stream *hinfo, struct hda_codec *cdc, struct snd_pcm_substream *sub, int action) { @@ -6916,6 +6947,7 @@ enum { ALC285_FIXUP_LEGION_Y9000X_SPEAKERS, ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE, ALC287_FIXUP_LEGION_16ACHG6, + ALC287_FIXUP_CS35L41_I2C_2, }; static const struct hda_fixup alc269_fixups[] = { @@ -8662,6 +8694,10 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc287_fixup_legion_16achg6_speakers, }, + [ALC287_FIXUP_CS35L41_I2C_2] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs35l41_fixup_i2c_two, + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -9059,6 +9095,9 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x22be, "Thinkpad X1 Carbon 8th", ALC285_FIXUP_THINKPAD_HEADSET_JACK), SND_PCI_QUIRK(0x17aa, 0x22c1, "Thinkpad P1 Gen 3", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK), SND_PCI_QUIRK(0x17aa, 0x22c2, "Thinkpad X1 Extreme Gen 3", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK), + SND_PCI_QUIRK(0x17aa, 0x22f1, "Thinkpad", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x22f2, "Thinkpad", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x22f3, "Thinkpad", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), SND_PCI_QUIRK(0x17aa, 0x310c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION), -- cgit v1.2.3 From e4c35e75209bda13e57c9bc8d280366c2b9275a5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 5 Jan 2022 17:24:09 +0100 Subject: ASoC: ak4375: Fix unused function error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A randconfig caught a compile warning that is now treated as a fatal error: sound/soc/codecs/ak4375.c:415:13: error: ‘ak4375_power_off’ defined but not used [-Werror=unused-function] where ak4375_power_off() is used only from the PM handler. As both suspend and resumes are already marked with __maybe_unused, let's rip off the superfluous ifdef CONFIG_PM, so that the error above can be avoided. Fixes: 53778b8292b5 ("ASoC: Add AK4375 support") Signed-off-by: Takashi Iwai Link: https://lore.kernel.org/r/20220105162409.20635-1-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/codecs/ak4375.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/codecs/ak4375.c b/sound/soc/codecs/ak4375.c index 22cda0699341..9a7b662016b9 100644 --- a/sound/soc/codecs/ak4375.c +++ b/sound/soc/codecs/ak4375.c @@ -438,7 +438,6 @@ static int ak4375_power_on(struct ak4375_priv *ak4375) return 0; } -#ifdef CONFIG_PM static int __maybe_unused ak4375_runtime_suspend(struct device *dev) { struct ak4375_priv *ak4375 = dev_get_drvdata(dev); @@ -463,7 +462,6 @@ static int __maybe_unused ak4375_runtime_resume(struct device *dev) return regcache_sync(ak4375->regmap); } -#endif /* CONFIG_PM */ static const struct snd_soc_component_driver soc_codec_dev_ak4375 = { .controls = ak4375_snd_controls, -- cgit v1.2.3 From b81e9e5c723de936652653241d3dc4f33ae05e8c Mon Sep 17 00:00:00 2001 From: Bart Kroon Date: Mon, 13 Dec 2021 19:20:43 +0100 Subject: ALSA: hda: ALC287: Add Lenovo IdeaPad Slim 9i 14ITL5 speaker quirk The speaker fixup that is used for the Yoga 7 14ITL5 also applies to the IdeaPad Slim 9i 14ITL5. The attached patch applies the quirk to initialise the amplifier on the IdeaPad Slim 9i as well. This is validated to work on my laptop. [ corrected the quirk entry position by tiwai ] Signed-off-by: Bart Kroon Cc: Link: https://lore.kernel.org/r/JAG24R.7NLJGWBF4G8U@tarmack.eu Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 2e20bbfe5357..eef973661b0a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9114,6 +9114,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3819, "Lenovo 13s Gen2 ITL", ALC287_FIXUP_13S_GEN2_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3824, "Legion Y9000X 2020", ALC285_FIXUP_LEGION_Y9000X_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3827, "Ideapad S740", ALC285_FIXUP_IDEAPAD_S740_COEF), + SND_PCI_QUIRK(0x17aa, 0x3834, "Lenovo IdeaPad Slim 9i 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3843, "Yoga 9i", ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP), SND_PCI_QUIRK(0x17aa, 0x3847, "Legion 7 16ACHG6", ALC287_FIXUP_LEGION_16ACHG6), SND_PCI_QUIRK(0x17aa, 0x384a, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), -- cgit v1.2.3 From 3349b3d0c63b8b6fcca58156d72407f0b2e101ac Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Tue, 4 Jan 2022 18:40:33 +0800 Subject: ASoC: imx-card: Need special setting for ak4497 on i.MX8MQ The SAI on i.MX8MQ don't support one2one ratio for mclk:bclk, so the mclk frequency exceeds the supported range of codec for the case that sample rate is larger than 705kHZ and format is S32_LE. Update the supported width for such case. Fixes: aa736700f42f ("ASoC: imx-card: Add imx-card machine driver") Signed-off-by: Shengjiu Wang Link: https://lore.kernel.org/r/1641292835-19085-2-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/imx-card.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/sound/soc/fsl/imx-card.c b/sound/soc/fsl/imx-card.c index e758c4f1b0bc..e0f3aa6d9501 100644 --- a/sound/soc/fsl/imx-card.c +++ b/sound/soc/fsl/imx-card.c @@ -553,8 +553,23 @@ static int imx_card_parse_of(struct imx_card_data *data) link_data->cpu_sysclk_id = FSL_SAI_CLK_MAST1; /* sai may support mclk/bclk = 1 */ - if (of_find_property(np, "fsl,mclk-equal-bclk", NULL)) + if (of_find_property(np, "fsl,mclk-equal-bclk", NULL)) { link_data->one2one_ratio = true; + } else { + int i; + + /* + * i.MX8MQ don't support one2one ratio, then + * with ak4497 only 16bit case is supported. + */ + for (i = 0; i < ARRAY_SIZE(ak4497_fs_mul); i++) { + if (ak4497_fs_mul[i].rmin == 705600 && + ak4497_fs_mul[i].rmax == 768000) { + ak4497_fs_mul[i].wmin = 32; + ak4497_fs_mul[i].wmax = 32; + } + } + } } link->cpus->of_node = args.np; -- cgit v1.2.3 From f331ae5fa59fbfb748317b290648fc3f1a50d932 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Tue, 4 Jan 2022 18:40:34 +0800 Subject: ASoC: imx-card: Fix mclk calculation issue for akcodec Transfer the refined slots and slot_width to akcodec_get_mclk_rate() for mclk calculation, otherwise the mclk frequency does not match with the slots and slot_width for S16_LE format, because the default slot_width is 32. Fixes: aa736700f42f ("ASoC: imx-card: Add imx-card machine driver") Signed-off-by: Shengjiu Wang Link: https://lore.kernel.org/r/1641292835-19085-3-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/imx-card.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/fsl/imx-card.c b/sound/soc/fsl/imx-card.c index e0f3aa6d9501..f9196fb7e833 100644 --- a/sound/soc/fsl/imx-card.c +++ b/sound/soc/fsl/imx-card.c @@ -247,13 +247,14 @@ static bool codec_is_akcodec(unsigned int type) } static unsigned long akcodec_get_mclk_rate(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) + struct snd_pcm_hw_params *params, + int slots, int slot_width) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct imx_card_data *data = snd_soc_card_get_drvdata(rtd->card); const struct imx_card_plat_data *plat_data = data->plat_data; struct dai_link_data *link_data = &data->link_data[rtd->num]; - unsigned int width = link_data->slots * link_data->slot_width; + unsigned int width = slots * slot_width; unsigned int rate = params_rate(params); int i; @@ -349,7 +350,7 @@ static int imx_aif_hw_params(struct snd_pcm_substream *substream, /* Set MCLK freq */ if (codec_is_akcodec(plat_data->type)) - mclk_freq = akcodec_get_mclk_rate(substream, params); + mclk_freq = akcodec_get_mclk_rate(substream, params, slots, slot_width); else mclk_freq = params_rate(params) * slots * slot_width; /* Use the maximum freq from DSD512 (512*44100 = 22579200) */ -- cgit v1.2.3 From 3969341813eb56d2dfc39bb64229359a6ae3c195 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Tue, 4 Jan 2022 18:40:35 +0800 Subject: ASoC: imx-card: improve the sound quality for low rate According to RM, on auto mode: For codec AK4458 and AK4497, the lowest ratio of MLCK/FS is 256 if sample rate is 8kHz-48kHz, For codec AK5558, the lowest ratio of MLCK/FS is 512 if sample rate is 8kHz-48kHz. With these setting the sound quality for 8kHz-48kHz can be improved. Fixes: aa736700f42f ("ASoC: imx-card: Add imx-card machine driver") Signed-off-by: Shengjiu Wang Link: https://lore.kernel.org/r/1641292835-19085-4-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/imx-card.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/fsl/imx-card.c b/sound/soc/fsl/imx-card.c index f9196fb7e833..6f8efd838fcc 100644 --- a/sound/soc/fsl/imx-card.c +++ b/sound/soc/fsl/imx-card.c @@ -120,7 +120,7 @@ struct imx_card_data { static struct imx_akcodec_fs_mul ak4458_fs_mul[] = { /* Normal, < 32kHz */ - { .rmin = 8000, .rmax = 24000, .wmin = 1024, .wmax = 1024, }, + { .rmin = 8000, .rmax = 24000, .wmin = 256, .wmax = 1024, }, /* Normal, 32kHz */ { .rmin = 32000, .rmax = 32000, .wmin = 256, .wmax = 1024, }, /* Normal */ @@ -151,8 +151,8 @@ static struct imx_akcodec_fs_mul ak4497_fs_mul[] = { * Table 7 - mapping multiplier and speed mode * Tables 8 & 9 - mapping speed mode and LRCK fs */ - { .rmin = 8000, .rmax = 32000, .wmin = 1024, .wmax = 1024, }, /* Normal, <= 32kHz */ - { .rmin = 44100, .rmax = 48000, .wmin = 512, .wmax = 512, }, /* Normal */ + { .rmin = 8000, .rmax = 32000, .wmin = 256, .wmax = 1024, }, /* Normal, <= 32kHz */ + { .rmin = 44100, .rmax = 48000, .wmin = 256, .wmax = 512, }, /* Normal */ { .rmin = 88200, .rmax = 96000, .wmin = 256, .wmax = 256, }, /* Double */ { .rmin = 176400, .rmax = 192000, .wmin = 128, .wmax = 128, }, /* Quad */ { .rmin = 352800, .rmax = 384000, .wmin = 128, .wmax = 128, }, /* Oct */ @@ -164,7 +164,7 @@ static struct imx_akcodec_fs_mul ak4497_fs_mul[] = { * (Table 4 from datasheet) */ static struct imx_akcodec_fs_mul ak5558_fs_mul[] = { - { .rmin = 8000, .rmax = 32000, .wmin = 1024, .wmax = 1024, }, + { .rmin = 8000, .rmax = 32000, .wmin = 512, .wmax = 1024, }, { .rmin = 44100, .rmax = 48000, .wmin = 512, .wmax = 512, }, { .rmin = 88200, .rmax = 96000, .wmin = 256, .wmax = 256, }, { .rmin = 176400, .rmax = 192000, .wmin = 128, .wmax = 128, }, -- cgit v1.2.3 From a2d6d84db2e7bcc831aed90f33334c70a1b060a3 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 6 Jan 2022 12:01:23 +0100 Subject: ASoC: rt5640: Fix possible NULL pointer deref on resume Commit 2b9c8d2b3c89 ("ASoC: rt5640: Add the HDA header support") adds re-queuing of the jack_work on resume when rt5640->jd_src != 0. But the jack_work will unconditionally deref rt5640->jack and that might be NULL. E.g. the sound/soc/intel/boards/bytcr_rt5640.c machine driver call snd_soc_component_set_jack(codec, NULL, NULL) from pre_suspend to disable the IRQ to avoid spurious wakeups, so when rt5640_resume() runs rt5640->jack will be NULL in this case. Make the queueing of the work conditional on rt5640->jack instead of on rt5640->jd_src to fix this. Fixes: 2b9c8d2b3c89 ("ASoC: rt5640: Add the HDA header support") Cc: Oder Chiou Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20220106110128.66049-2-hdegoede@redhat.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5640.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index f3659b14c74e..ceb2d50b17aa 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -2737,7 +2737,7 @@ static int rt5640_resume(struct snd_soc_component *component) regcache_cache_only(rt5640->regmap, false); regcache_sync(rt5640->regmap); - if (rt5640->jd_src) { + if (rt5640->jack) { if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) snd_soc_component_update_bits(component, RT5640_DUMMY2, 0x1100, 0x1100); -- cgit v1.2.3 From a3b1aaf7aef9fa945810de3fd7c15b2e93ecdbfd Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 6 Jan 2022 12:01:24 +0100 Subject: ASoC: rt5640: Change jack_work to a delayed_work Change jack_work from a struct work_struct to a struct delayed_work, this is a preparation patch for adding support for boards where an external GPIO is used for jack-detect, rather then one of the JD pins of the codec. Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20220106110128.66049-3-hdegoede@redhat.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5640.c | 16 ++++++++-------- sound/soc/codecs/rt5640.h | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index ceb2d50b17aa..a1e4e3ac99f1 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -2298,7 +2298,7 @@ EXPORT_SYMBOL_GPL(rt5640_detect_headset); static void rt5640_jack_work(struct work_struct *work) { struct rt5640_priv *rt5640 = - container_of(work, struct rt5640_priv, jack_work); + container_of(work, struct rt5640_priv, jack_work.work); struct snd_soc_component *component = rt5640->component; int status; @@ -2381,7 +2381,7 @@ static void rt5640_jack_work(struct work_struct *work) * disabled the OVCD IRQ, the IRQ pin will stay high and as * we react to edges, we miss the unplug event -> recheck. */ - queue_work(system_long_wq, &rt5640->jack_work); + queue_delayed_work(system_long_wq, &rt5640->jack_work, 0); } } @@ -2390,7 +2390,7 @@ static irqreturn_t rt5640_irq(int irq, void *data) struct rt5640_priv *rt5640 = data; if (rt5640->jack) - queue_work(system_long_wq, &rt5640->jack_work); + queue_delayed_work(system_long_wq, &rt5640->jack_work, 0); return IRQ_HANDLED; } @@ -2399,7 +2399,7 @@ static void rt5640_cancel_work(void *data) { struct rt5640_priv *rt5640 = data; - cancel_work_sync(&rt5640->jack_work); + cancel_delayed_work_sync(&rt5640->jack_work); cancel_delayed_work_sync(&rt5640->bp_work); } @@ -2508,7 +2508,7 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component, } /* sync initial jack state */ - queue_work(system_long_wq, &rt5640->jack_work); + queue_delayed_work(system_long_wq, &rt5640->jack_work, 0); } static void rt5640_enable_hda_jack_detect( @@ -2546,7 +2546,7 @@ static void rt5640_enable_hda_jack_detect( } /* sync initial jack state */ - queue_work(system_long_wq, &rt5640->jack_work); + queue_delayed_work(system_long_wq, &rt5640->jack_work, 0); } static int rt5640_set_jack(struct snd_soc_component *component, @@ -2745,7 +2745,7 @@ static int rt5640_resume(struct snd_soc_component *component) snd_soc_component_write(component, RT5640_DUMMY2, 0x4001); - queue_work(system_long_wq, &rt5640->jack_work); + queue_delayed_work(system_long_wq, &rt5640->jack_work, 0); } return 0; @@ -2950,7 +2950,7 @@ static int rt5640_i2c_probe(struct i2c_client *i2c, rt5640->hp_mute = true; rt5640->irq = i2c->irq; INIT_DELAYED_WORK(&rt5640->bp_work, rt5640_button_press_work); - INIT_WORK(&rt5640->jack_work, rt5640_jack_work); + INIT_DELAYED_WORK(&rt5640->jack_work, rt5640_jack_work); /* Make sure work is stopped on probe-error / remove */ ret = devm_add_action_or_reset(&i2c->dev, rt5640_cancel_work, rt5640); diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h index 2c28f83e338a..7ab930def8dd 100644 --- a/sound/soc/codecs/rt5640.h +++ b/sound/soc/codecs/rt5640.h @@ -2145,7 +2145,7 @@ struct rt5640_priv { int release_count; int poll_count; struct delayed_work bp_work; - struct work_struct jack_work; + struct delayed_work jack_work; struct snd_soc_jack *jack; unsigned int jd_src; bool jd_inverted; -- cgit v1.2.3 From b35a9ab4904973a68b4473c2985b8ac0b6d57089 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 6 Jan 2022 12:01:25 +0100 Subject: ASoC: rt5640: Allow snd_soc_component_set_jack() to override the codec IRQ On some boards where the firmware/fwnode information is in essence read-only (x86 + ACPI boards) the i2c_client for the codec may contain the wrong IRQ or no IRQ at all. Since we only request the IRQ once snd_soc_component_set_jack() gets called, allow machine drivers to override the IRQ with the proper one through the data parameter to snd_soc_component_set_jack(). Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20220106110128.66049-4-hdegoede@redhat.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5640.c | 8 ++++++-- sound/soc/codecs/rt5640.h | 4 ++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index a1e4e3ac99f1..fabc6e44b4a6 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -2452,7 +2452,8 @@ static void rt5640_disable_jack_detect(struct snd_soc_component *component) } static void rt5640_enable_jack_detect(struct snd_soc_component *component, - struct snd_soc_jack *jack) + struct snd_soc_jack *jack, + struct rt5640_set_jack_data *jack_data) { struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); int ret; @@ -2496,6 +2497,9 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component, rt5640_enable_micbias1_ovcd_irq(component); } + if (jack_data && jack_data->codec_irq_override) + rt5640->irq = jack_data->codec_irq_override; + ret = request_irq(rt5640->irq, rt5640_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rt5640", rt5640); @@ -2558,7 +2562,7 @@ static int rt5640_set_jack(struct snd_soc_component *component, if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) rt5640_enable_hda_jack_detect(component, jack); else - rt5640_enable_jack_detect(component, jack); + rt5640_enable_jack_detect(component, jack, data); } else { rt5640_disable_jack_detect(component); } diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h index 7ab930def8dd..2f4da5a8ecb2 100644 --- a/sound/soc/codecs/rt5640.h +++ b/sound/soc/codecs/rt5640.h @@ -2153,6 +2153,10 @@ struct rt5640_priv { unsigned int ovcd_sf; }; +struct rt5640_set_jack_data { + int codec_irq_override; +}; + int rt5640_dmic_enable(struct snd_soc_component *component, bool dmic1_data_pin, bool dmic2_data_pin); int rt5640_sel_asrc_clk_src(struct snd_soc_component *component, -- cgit v1.2.3 From 701d636a224a77a4371f57ca2d4322ab0401a866 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 6 Jan 2022 12:01:26 +0100 Subject: ASoC: rt5640: Add support for boards with an external jack-detect GPIO Some boards have the codec IRQ hooked-up as normally, so the driver can still do things like headset vs headphones and button-press detection, but instead of using one of the JD pins of the codec, an external GPIO is used to report the jack-presence switch status of the jack. Add support for this. Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20220106110128.66049-5-hdegoede@redhat.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5640.c | 45 +++++++++++++++++++++++++++++++++++++++++---- sound/soc/codecs/rt5640.h | 5 +++++ 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index fabc6e44b4a6..e7a82565b905 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -2160,7 +2160,11 @@ static bool rt5640_jack_inserted(struct snd_soc_component *component) struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); int val; - val = snd_soc_component_read(component, RT5640_INT_IRQ_ST); + if (rt5640->jd_gpio) + val = gpiod_get_value(rt5640->jd_gpio) ? RT5640_JD_STATUS : 0; + else + val = snd_soc_component_read(component, RT5640_INT_IRQ_ST); + dev_dbg(component->dev, "irq status %#04x\n", val); if (rt5640->jd_inverted) @@ -2395,6 +2399,16 @@ static irqreturn_t rt5640_irq(int irq, void *data) return IRQ_HANDLED; } +static irqreturn_t rt5640_jd_gpio_irq(int irq, void *data) +{ + struct rt5640_priv *rt5640 = data; + + queue_delayed_work(system_long_wq, &rt5640->jack_work, + msecs_to_jiffies(JACK_SETTLE_TIME)); + + return IRQ_HANDLED; +} + static void rt5640_cancel_work(void *data) { struct rt5640_priv *rt5640 = data; @@ -2439,7 +2453,12 @@ static void rt5640_disable_jack_detect(struct snd_soc_component *component) if (!rt5640->jack) return; - free_irq(rt5640->irq, rt5640); + if (rt5640->jd_gpio_irq_requested) + free_irq(rt5640->jd_gpio_irq, rt5640); + + if (rt5640->irq_requested) + free_irq(rt5640->irq, rt5640); + rt5640_cancel_work(rt5640); if (rt5640->jack->status & SND_JACK_MICROPHONE) { @@ -2448,6 +2467,9 @@ static void rt5640_disable_jack_detect(struct snd_soc_component *component) snd_soc_jack_report(rt5640->jack, 0, SND_JACK_BTN_0); } + rt5640->jd_gpio_irq_requested = false; + rt5640->irq_requested = false; + rt5640->jd_gpio = NULL; rt5640->jack = NULL; } @@ -2500,16 +2522,31 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component, if (jack_data && jack_data->codec_irq_override) rt5640->irq = jack_data->codec_irq_override; + if (jack_data && jack_data->jd_gpio) { + rt5640->jd_gpio = jack_data->jd_gpio; + rt5640->jd_gpio_irq = gpiod_to_irq(rt5640->jd_gpio); + + ret = request_irq(rt5640->jd_gpio_irq, rt5640_jd_gpio_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "rt5640-jd-gpio", rt5640); + if (ret) { + dev_warn(component->dev, "Failed to request jd GPIO IRQ %d: %d\n", + rt5640->jd_gpio_irq, ret); + rt5640_disable_jack_detect(component); + return; + } + rt5640->jd_gpio_irq_requested = true; + } + ret = request_irq(rt5640->irq, rt5640_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rt5640", rt5640); if (ret) { dev_warn(component->dev, "Failed to reguest IRQ %d: %d\n", rt5640->irq, ret); - rt5640->irq = -ENXIO; - /* Undo above settings */ rt5640_disable_jack_detect(component); return; } + rt5640->irq_requested = true; /* sync initial jack state */ queue_delayed_work(system_long_wq, &rt5640->jack_work, 0); diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h index 2f4da5a8ecb2..9e49b9a0ccaa 100644 --- a/sound/soc/codecs/rt5640.h +++ b/sound/soc/codecs/rt5640.h @@ -2124,6 +2124,7 @@ struct rt5640_priv { int ldo1_en; /* GPIO for LDO1_EN */ int irq; + int jd_gpio_irq; int sysclk; int sysclk_src; int lrck[RT5640_AIFS]; @@ -2136,6 +2137,8 @@ struct rt5640_priv { bool hp_mute; bool asrc_en; + bool irq_requested; + bool jd_gpio_irq_requested; /* Jack and button detect data */ bool ovcd_irq_enabled; @@ -2147,6 +2150,7 @@ struct rt5640_priv { struct delayed_work bp_work; struct delayed_work jack_work; struct snd_soc_jack *jack; + struct gpio_desc *jd_gpio; unsigned int jd_src; bool jd_inverted; unsigned int ovcd_th; @@ -2155,6 +2159,7 @@ struct rt5640_priv { struct rt5640_set_jack_data { int codec_irq_override; + struct gpio_desc *jd_gpio; }; int rt5640_dmic_enable(struct snd_soc_component *component, -- cgit v1.2.3 From 45ed0166c39f878162872babc88830d91426beb5 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 6 Jan 2022 12:01:27 +0100 Subject: ASoC: Intel: bytcr_rt5640: Support retrieving the codec IRQ from the AMCR0F28 ACPI dev Some X86 tablets, which ship with Android as factory installed OS, specify codec IRQs/GPIOS in a special Android AMCR0F28 ACPI device. Add support for retrieving the codec IRQ from this ACPI device instead of from the 10EC5640 device describing the codec itself and enable this on Asus MemoPad 7 ME176C tablets. This fixes jack-detect not working on these tablets. Cc: Stephan Gerhold Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20220106110128.66049-6-hdegoede@redhat.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/bytcr_rt5640.c | 43 ++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index a0c5f0e9c22a..f37ab44ae957 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -79,6 +79,7 @@ enum { #define BYT_RT5640_LINEOUT_AS_HP2 BIT(26) #define BYT_RT5640_HSMIC2_ON_IN1 BIT(27) #define BYT_RT5640_JD_HP_ELITEP_1000G2 BIT(28) +#define BYT_RT5640_USE_AMCR0F28 BIT(29) #define BYTCR_INPUT_DEFAULTS \ (BYT_RT5640_IN3_MAP | \ @@ -93,6 +94,7 @@ enum { struct byt_rt5640_private { struct snd_soc_jack jack; struct snd_soc_jack jack2; + struct rt5640_set_jack_data jack_data; struct gpio_desc *hsmic_detect; struct clk *mclk; struct device *codec_dev; @@ -597,7 +599,8 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { BYT_RT5640_OVCD_TH_2000UA | BYT_RT5640_OVCD_SF_0P75 | BYT_RT5640_SSP0_AIF1 | - BYT_RT5640_MCLK_EN), + BYT_RT5640_MCLK_EN | + BYT_RT5640_USE_AMCR0F28), }, { .matches = { @@ -1109,6 +1112,32 @@ static int byt_rt5640_add_codec_device_props(struct device *i2c_dev, return ret; } +/* Some Android devs specify IRQs/GPIOS in a special AMCR0F28 ACPI device */ +static int byt_rt5640_get_amcr0f28_settings(struct snd_soc_card *card) +{ + struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card); + struct rt5640_set_jack_data *data = &priv->jack_data; + struct acpi_device *adev; + int ret = 0; + + adev = acpi_dev_get_first_match_dev("AMCR0F28", "1", -1); + if (!adev) { + dev_err(card->dev, "error cannot find AMCR0F28 adev\n"); + return -ENOENT; + } + + data->codec_irq_override = acpi_dev_gpio_irq_get(adev, 0); + if (data->codec_irq_override < 0) { + ret = data->codec_irq_override; + dev_err(card->dev, "error %d getting codec IRQ\n", ret); + goto put_adev; + } + +put_adev: + acpi_dev_put(adev); + return ret; +} + static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) { struct snd_soc_card *card = runtime->card; @@ -1244,7 +1273,14 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) } snd_jack_set_key(priv->jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); - snd_soc_component_set_jack(component, &priv->jack, NULL); + + if (byt_rt5640_quirk & BYT_RT5640_USE_AMCR0F28) { + ret = byt_rt5640_get_amcr0f28_settings(card); + if (ret) + return ret; + } + + snd_soc_component_set_jack(component, &priv->jack, &priv->jack_data); } if (byt_rt5640_quirk & BYT_RT5640_JD_HP_ELITEP_1000G2) { @@ -1448,7 +1484,8 @@ static int byt_rt5640_resume(struct snd_soc_card *card) for_each_card_components(card, component) { if (!strcmp(component->name, byt_rt5640_codec_name)) { dev_dbg(component->dev, "re-enabling jack detect after resume\n"); - snd_soc_component_set_jack(component, &priv->jack, NULL); + snd_soc_component_set_jack(component, &priv->jack, + &priv->jack_data); break; } } -- cgit v1.2.3 From 44125fd5315154c6b8326b5c27646af3b33ba25c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 6 Jan 2022 12:01:28 +0100 Subject: ASoC: Intel: bytcr_rt5640: Add support for external GPIO jack-detect Some boards have the codec IRQ hooked-up as normally, so the driver can still do things like headset vs headphones and button-press detection, but instead of using one of the JD pins of the codec, an external GPIO is used to report the jack-presence switch status of the jack. Add support for boards which have this setup and which specify which external GPIO to use in the special Android AMCR0F28 ACPI device. And add a quirk for the Asus TF103C tablet which uses this setup. Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20220106110128.66049-7-hdegoede@redhat.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/bytcr_rt5640.c | 43 ++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index f37ab44ae957..2ace32c03ec9 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -40,6 +40,8 @@ enum { BYT_RT5640_NO_INTERNAL_MIC_MAP, }; +#define RT5640_JD_SRC_EXT_GPIO 0x0f + enum { BYT_RT5640_JD_SRC_GPIO1 = (RT5640_JD_SRC_GPIO1 << 4), BYT_RT5640_JD_SRC_JD1_IN4P = (RT5640_JD_SRC_JD1_IN4P << 4), @@ -47,6 +49,7 @@ enum { BYT_RT5640_JD_SRC_GPIO2 = (RT5640_JD_SRC_GPIO2 << 4), BYT_RT5640_JD_SRC_GPIO3 = (RT5640_JD_SRC_GPIO3 << 4), BYT_RT5640_JD_SRC_GPIO4 = (RT5640_JD_SRC_GPIO4 << 4), + BYT_RT5640_JD_SRC_EXT_GPIO = (RT5640_JD_SRC_EXT_GPIO << 4) }; enum { @@ -627,6 +630,19 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { BYT_RT5640_SSP0_AIF2 | BYT_RT5640_MCLK_EN), }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "TF103C"), + }, + .driver_data = (void *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_JD_SRC_EXT_GPIO | + BYT_RT5640_OVCD_TH_2000UA | + BYT_RT5640_OVCD_SF_0P75 | + BYT_RT5640_SSP0_AIF1 | + BYT_RT5640_MCLK_EN | + BYT_RT5640_USE_AMCR0F28), + }, { /* Chuwi Vi8 (CWI506) */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Insyde"), @@ -1083,9 +1099,11 @@ static int byt_rt5640_add_codec_device_props(struct device *i2c_dev, } if (BYT_RT5640_JDSRC(byt_rt5640_quirk)) { - props[cnt++] = PROPERTY_ENTRY_U32( - "realtek,jack-detect-source", - BYT_RT5640_JDSRC(byt_rt5640_quirk)); + if (BYT_RT5640_JDSRC(byt_rt5640_quirk) != RT5640_JD_SRC_EXT_GPIO) { + props[cnt++] = PROPERTY_ENTRY_U32( + "realtek,jack-detect-source", + BYT_RT5640_JDSRC(byt_rt5640_quirk)); + } props[cnt++] = PROPERTY_ENTRY_U32( "realtek,over-current-threshold-microamp", @@ -1113,6 +1131,13 @@ static int byt_rt5640_add_codec_device_props(struct device *i2c_dev, } /* Some Android devs specify IRQs/GPIOS in a special AMCR0F28 ACPI device */ +static const struct acpi_gpio_params amcr0f28_jd_gpio = { 1, 0, false }; + +static const struct acpi_gpio_mapping amcr0f28_gpios[] = { + { "rt5640-jd-gpios", &amcr0f28_jd_gpio, 1 }, + { } +}; + static int byt_rt5640_get_amcr0f28_settings(struct snd_soc_card *card) { struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card); @@ -1133,6 +1158,18 @@ static int byt_rt5640_get_amcr0f28_settings(struct snd_soc_card *card) goto put_adev; } + if (BYT_RT5640_JDSRC(byt_rt5640_quirk) == RT5640_JD_SRC_EXT_GPIO) { + acpi_dev_add_driver_gpios(adev, amcr0f28_gpios); + data->jd_gpio = devm_fwnode_gpiod_get(card->dev, acpi_fwnode_handle(adev), + "rt5640-jd", GPIOD_IN, "rt5640-jd"); + acpi_dev_remove_driver_gpios(adev); + + if (IS_ERR(data->jd_gpio)) { + ret = PTR_ERR(data->jd_gpio); + dev_err(card->dev, "error %d getting jd GPIO\n", ret); + } + } + put_adev: acpi_dev_put(adev); return ret; -- cgit v1.2.3 From 320386343451ab6a3577e0ee200dac56a6182944 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 5 Jan 2022 19:08:03 +0800 Subject: ASoC: fsl_asrc: refine the check of available clock divider According to RM, the clock divider range is from 1 to 8, clock prescaling ratio may be any power of 2 from 1 to 128. So the supported divider is not all the value between 1 and 1024, just limited value in that range. Create table for the supported divder and add function to check the clock divider is available by comparing with the table. Fixes: d0250cf4f2ab ("ASoC: fsl_asrc: Add an option to select internal ratio mode") Signed-off-by: Shengjiu Wang Link: https://lore.kernel.org/r/1641380883-20709-1-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_asrc.c | 69 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 11 deletions(-) diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index 24b41881a68f..d7d1536a4f37 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -19,6 +19,7 @@ #include "fsl_asrc.h" #define IDEAL_RATIO_DECIMAL_DEPTH 26 +#define DIVIDER_NUM 64 #define pair_err(fmt, ...) \ dev_err(&asrc->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__) @@ -101,6 +102,55 @@ static unsigned char clk_map_imx8qxp[2][ASRC_CLK_MAP_LEN] = { }, }; +/* + * According to RM, the divider range is 1 ~ 8, + * prescaler is power of 2 from 1 ~ 128. + */ +static int asrc_clk_divider[DIVIDER_NUM] = { + 1, 2, 4, 8, 16, 32, 64, 128, /* divider = 1 */ + 2, 4, 8, 16, 32, 64, 128, 256, /* divider = 2 */ + 3, 6, 12, 24, 48, 96, 192, 384, /* divider = 3 */ + 4, 8, 16, 32, 64, 128, 256, 512, /* divider = 4 */ + 5, 10, 20, 40, 80, 160, 320, 640, /* divider = 5 */ + 6, 12, 24, 48, 96, 192, 384, 768, /* divider = 6 */ + 7, 14, 28, 56, 112, 224, 448, 896, /* divider = 7 */ + 8, 16, 32, 64, 128, 256, 512, 1024, /* divider = 8 */ +}; + +/* + * Check if the divider is available for internal ratio mode + */ +static bool fsl_asrc_divider_avail(int clk_rate, int rate, int *div) +{ + u32 rem, i; + u64 n; + + if (div) + *div = 0; + + if (clk_rate == 0 || rate == 0) + return false; + + n = clk_rate; + rem = do_div(n, rate); + + if (div) + *div = n; + + if (rem != 0) + return false; + + for (i = 0; i < DIVIDER_NUM; i++) { + if (n == asrc_clk_divider[i]) + break; + } + + if (i == DIVIDER_NUM) + return false; + + return true; +} + /** * fsl_asrc_sel_proc - Select the pre-processing and post-processing options * @inrate: input sample rate @@ -330,12 +380,12 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate) enum asrc_word_width input_word_width; enum asrc_word_width output_word_width; u32 inrate, outrate, indiv, outdiv; - u32 clk_index[2], div[2], rem[2]; + u32 clk_index[2], div[2]; u64 clk_rate; int in, out, channels; int pre_proc, post_proc; struct clk *clk; - bool ideal; + bool ideal, div_avail; if (!config) { pair_err("invalid pair config\n"); @@ -415,8 +465,7 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate) clk = asrc_priv->asrck_clk[clk_index[ideal ? OUT : IN]]; clk_rate = clk_get_rate(clk); - rem[IN] = do_div(clk_rate, inrate); - div[IN] = (u32)clk_rate; + div_avail = fsl_asrc_divider_avail(clk_rate, inrate, &div[IN]); /* * The divider range is [1, 1024], defined by the hardware. For non- @@ -425,7 +474,7 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate) * only result in different converting speeds. So remainder does not * matter, as long as we keep the divider within its valid range. */ - if (div[IN] == 0 || (!ideal && (div[IN] > 1024 || rem[IN] != 0))) { + if (div[IN] == 0 || (!ideal && !div_avail)) { pair_err("failed to support input sample rate %dHz by asrck_%x\n", inrate, clk_index[ideal ? OUT : IN]); return -EINVAL; @@ -436,13 +485,12 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate) clk = asrc_priv->asrck_clk[clk_index[OUT]]; clk_rate = clk_get_rate(clk); if (ideal && use_ideal_rate) - rem[OUT] = do_div(clk_rate, IDEAL_RATIO_RATE); + div_avail = fsl_asrc_divider_avail(clk_rate, IDEAL_RATIO_RATE, &div[OUT]); else - rem[OUT] = do_div(clk_rate, outrate); - div[OUT] = clk_rate; + div_avail = fsl_asrc_divider_avail(clk_rate, outrate, &div[OUT]); /* Output divider has the same limitation as the input one */ - if (div[OUT] == 0 || (!ideal && (div[OUT] > 1024 || rem[OUT] != 0))) { + if (div[OUT] == 0 || (!ideal && !div_avail)) { pair_err("failed to support output sample rate %dHz by asrck_%x\n", outrate, clk_index[OUT]); return -EINVAL; @@ -621,8 +669,7 @@ static void fsl_asrc_select_clk(struct fsl_asrc_priv *asrc_priv, clk_index = asrc_priv->clk_map[j][i]; clk_rate = clk_get_rate(asrc_priv->asrck_clk[clk_index]); /* Only match a perfect clock source with no remainder */ - if (clk_rate != 0 && (clk_rate / rate[j]) <= 1024 && - (clk_rate % rate[j]) == 0) + if (fsl_asrc_divider_avail(clk_rate, rate[j], NULL)) break; } -- cgit v1.2.3 From 00ac838924f73b51e82994c7fc870f0a994e4d34 Mon Sep 17 00:00:00 2001 From: Qinghua Jin Date: Thu, 6 Jan 2022 17:28:47 +0800 Subject: ASoC: topology: Fix typo change 'postion' to 'position' Signed-off-by: Qinghua Jin Link: https://lore.kernel.org/r/20220106092847.357035-1-qhjin.dev@gmail.com Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index f5b9e66ac3b8..2630df024dff 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -56,7 +56,7 @@ struct soc_tplg { const struct firmware *fw; /* runtime FW parsing */ - const u8 *pos; /* read postion */ + const u8 *pos; /* read position */ const u8 *hdr_pos; /* header position */ unsigned int pass; /* pass number */ -- cgit v1.2.3 From 7560ee032b3f87fa5c4f03e99a064c6cd326951e Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Thu, 6 Jan 2022 13:41:45 +0100 Subject: ALSA: seq: virmidi: Add a drain operation If a driver does not supply a drain operation for outputs, a default code path will execute msleep(50). Especially for a virtual midi device this severely limmits the throughput. This implementation for the virtual midi driver simply flushes the output workqueue. Signed-off-by: Stefan Sauer Link: https://lore.kernel.org/r/20220106124145.17254-1-st_kost@gmx.de Signed-off-by: Takashi Iwai --- sound/core/seq/seq_virmidi.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c index 4abc38c70cae..f5cae49500c8 100644 --- a/sound/core/seq/seq_virmidi.c +++ b/sound/core/seq/seq_virmidi.c @@ -262,6 +262,16 @@ static int snd_virmidi_output_close(struct snd_rawmidi_substream *substream) return 0; } +/* + * drain output work queue + */ +static void snd_virmidi_output_drain(struct snd_rawmidi_substream *substream) +{ + struct snd_virmidi *vmidi = substream->runtime->private_data; + + flush_work(&vmidi->output_work); +} + /* * subscribe callback - allow output to rawmidi device */ @@ -336,6 +346,7 @@ static const struct snd_rawmidi_ops snd_virmidi_output_ops = { .open = snd_virmidi_output_open, .close = snd_virmidi_output_close, .trigger = snd_virmidi_output_trigger, + .drain = snd_virmidi_output_drain, }; /* -- cgit v1.2.3 From 4b46daf028e2f4a051047791b8a3bfc9bc37d684 Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Thu, 6 Jan 2022 13:38:21 +0100 Subject: ALSA: virmidi: Remove duplicated code seq_virmidi.c: snd_virmidi_new() is already setting seq_mode to SNDRV_VIRMIDI_SEQ_DISPATCH. Signed-off-by: Stefan Sauer Link: https://lore.kernel.org/r/20220106123821.16691-1-st_kost@gmx.de Signed-off-by: Takashi Iwai --- sound/drivers/virmidi.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/sound/drivers/virmidi.c b/sound/drivers/virmidi.c index 7f7eed6faaae..58012de90c38 100644 --- a/sound/drivers/virmidi.c +++ b/sound/drivers/virmidi.c @@ -90,15 +90,12 @@ static int snd_virmidi_probe(struct platform_device *devptr) } for (idx = 0; idx < midi_devs[dev]; idx++) { struct snd_rawmidi *rmidi; - struct snd_virmidi_dev *rdev; err = snd_virmidi_new(card, idx, &rmidi); if (err < 0) return err; - rdev = rmidi->private_data; vmidi->midi[idx] = rmidi; strcpy(rmidi->name, "Virtual Raw MIDI"); - rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH; } strcpy(card->driver, "VirMIDI"); -- cgit v1.2.3 From 7112550890d7e415188a3351ec0a140be60f6deb Mon Sep 17 00:00:00 2001 From: Ajit Kumar Pandey Date: Thu, 6 Jan 2022 20:35:21 +0530 Subject: ASoC: amd: acp: acp-mach: Change default RT1019 amp dev id RT1019 components was initially registered with i2c1 and i2c2 but now changed to i2c0 and i2c1 in most of our AMD platforms. Change default rt1019 components to 10EC1019:00 and 10EC1019:01 which is aligned with most of AMD machines. Any exception to rt1019 device ids in near future board design can be handled using dmi based quirk for that machine. Signed-off-by: Ajit Kumar Pandey Link: https://lore.kernel.org/r/20220106150525.396170-1-AjitKumar.Pandey@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-mach-common.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c index 03d8d1af14b3..c9caade5cb74 100644 --- a/sound/soc/amd/acp/acp-mach-common.c +++ b/sound/soc/amd/acp/acp-mach-common.c @@ -293,8 +293,8 @@ static const struct snd_soc_ops acp_card_rt5682s_ops = { /* Declare RT1019 codec components */ SND_SOC_DAILINK_DEF(rt1019, - DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC1019:01", "rt1019-aif"), - COMP_CODEC("i2c-10EC1019:02", "rt1019-aif"))); + DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC1019:00", "rt1019-aif"), + COMP_CODEC("i2c-10EC1019:01", "rt1019-aif"))); static const struct snd_soc_dapm_route rt1019_map_lr[] = { { "Left Spk", NULL, "Left SPO" }, @@ -303,11 +303,11 @@ static const struct snd_soc_dapm_route rt1019_map_lr[] = { static struct snd_soc_codec_conf rt1019_conf[] = { { - .dlc = COMP_CODEC_CONF("i2c-10EC1019:01"), + .dlc = COMP_CODEC_CONF("i2c-10EC1019:00"), .name_prefix = "Left", }, { - .dlc = COMP_CODEC_CONF("i2c-10EC1019:02"), + .dlc = COMP_CODEC_CONF("i2c-10EC1019:01"), .name_prefix = "Right", }, }; -- cgit v1.2.3 From 3b247eeaecfefe35ecca1578b0ed48be65bc6ca3 Mon Sep 17 00:00:00 2001 From: Yassine Oudjana Date: Tue, 4 Jan 2022 03:35:36 +0000 Subject: ASoC: wcd9335: Keep a RX port value for each SLIM RX mux Currently, rx_port_value is a single unsigned int that gets overwritten when slim_rx_mux_put() is called for any RX mux, then the same value is read when slim_rx_mux_get() is called for any of them. This results in slim_rx_mux_get() reporting the last value set by slim_rx_mux_put() regardless of which SLIM RX mux is in question. Turn rx_port_value into an array and store a separate value for each SLIM RX mux. Signed-off-by: Yassine Oudjana Link: https://lore.kernel.org/r/20220104033356.343685-1-y.oudjana@protonmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/wcd9335.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index bc5d68c53e5a..1e60db4056ad 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -341,7 +341,7 @@ struct wcd9335_codec { int reset_gpio; struct regulator_bulk_data supplies[WCD9335_MAX_SUPPLY]; - unsigned int rx_port_value; + unsigned int rx_port_value[WCD9335_RX_MAX]; unsigned int tx_port_value; int hph_l_gain; int hph_r_gain; @@ -1269,10 +1269,11 @@ static const struct snd_kcontrol_new sb_tx8_mux = static int slim_rx_mux_get(struct snd_kcontrol *kc, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kc); - struct wcd9335_codec *wcd = dev_get_drvdata(dapm->dev); + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kc); + struct wcd9335_codec *wcd = dev_get_drvdata(w->dapm->dev); + u32 port_id = w->shift; - ucontrol->value.enumerated.item[0] = wcd->rx_port_value; + ucontrol->value.enumerated.item[0] = wcd->rx_port_value[port_id]; return 0; } @@ -1286,9 +1287,9 @@ static int slim_rx_mux_put(struct snd_kcontrol *kc, struct snd_soc_dapm_update *update = NULL; u32 port_id = w->shift; - wcd->rx_port_value = ucontrol->value.enumerated.item[0]; + wcd->rx_port_value[port_id] = ucontrol->value.enumerated.item[0]; - switch (wcd->rx_port_value) { + switch (wcd->rx_port_value[port_id]) { case 0: list_del_init(&wcd->rx_chs[port_id].list); break; @@ -1309,11 +1310,11 @@ static int slim_rx_mux_put(struct snd_kcontrol *kc, &wcd->dai[AIF4_PB].slim_ch_list); break; default: - dev_err(wcd->dev, "Unknown AIF %d\n", wcd->rx_port_value); + dev_err(wcd->dev, "Unknown AIF %d\n", wcd->rx_port_value[port_id]); goto err; } - snd_soc_dapm_mux_update_power(w->dapm, kc, wcd->rx_port_value, + snd_soc_dapm_mux_update_power(w->dapm, kc, wcd->rx_port_value[port_id], e, update); return 0; -- cgit v1.2.3 From 1b1f98dd70dcfd25476adabcbe72484312e879f7 Mon Sep 17 00:00:00 2001 From: Jiasheng Jiang Date: Fri, 7 Jan 2022 10:08:51 +0800 Subject: ALSA: intel_hdmi: Check for error num after setting mask To maintain the consistency of the code, it should be better to add the sanity check after calling dma_set_mask_and_coherent(), like tegra_pcm_dma_allocate() in `sound/soc/tegra/tegra_pcm.c`. Signed-off-by: Jiasheng Jiang Link: https://lore.kernel.org/r/20220107020851.3095591-1-jiasheng@iscas.ac.cn Signed-off-by: Takashi Iwai --- sound/x86/intel_hdmi_audio.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/x86/intel_hdmi_audio.c b/sound/x86/intel_hdmi_audio.c index 378826312abe..1c94eaff1931 100644 --- a/sound/x86/intel_hdmi_audio.c +++ b/sound/x86/intel_hdmi_audio.c @@ -1750,7 +1750,9 @@ static int hdmi_lpe_audio_probe(struct platform_device *pdev) card_ctx->irq = irq; /* only 32bit addressable */ - dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; init_channel_allocations(); -- cgit v1.2.3 From d92321bbe46b0ecae0941461379d39599610d869 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 7 Jan 2022 16:06:35 +0000 Subject: ASoC: cs35l41: Update handling of test key registers In preparation for the addition of PM runtime support move the test key out of the register patches themselves. This is necessary to allow the test key to be held during cache synchronisation, which is required by the OTP settings which were unpacked from the device and written by the driver. Also whilst at it, the driver uses a mixture of accessing the test key register by name and by address, consistently use the name. Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20220107160636.6555-2-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/cs35l41.h | 2 ++ sound/soc/codecs/cs35l41-lib.c | 67 +++++++++++++++++++++--------------------- sound/soc/codecs/cs35l41.c | 32 +++++++++++--------- 3 files changed, 54 insertions(+), 47 deletions(-) diff --git a/include/sound/cs35l41.h b/include/sound/cs35l41.h index 29a527457b48..56289b67b9a0 100644 --- a/include/sound/cs35l41.h +++ b/include/sound/cs35l41.h @@ -762,6 +762,8 @@ struct cs35l41_otp_map_element_t { extern struct regmap_config cs35l41_regmap_i2c; extern struct regmap_config cs35l41_regmap_spi; +int cs35l41_test_key_unlock(struct device *dev, struct regmap *regmap); +int cs35l41_test_key_lock(struct device *dev, struct regmap *regmap); int cs35l41_otp_unpack(struct device *dev, struct regmap *regmap); int cs35l41_register_errata_patch(struct device *dev, struct regmap *reg, unsigned int reg_revid); int cs35l41_set_channels(struct device *dev, struct regmap *reg, diff --git a/sound/soc/codecs/cs35l41-lib.c b/sound/soc/codecs/cs35l41-lib.c index 639dcd25b17e..ecaf67fd7653 100644 --- a/sound/soc/codecs/cs35l41-lib.c +++ b/sound/soc/codecs/cs35l41-lib.c @@ -623,8 +623,6 @@ static const struct cs35l41_otp_packed_element_t otp_map_2[CS35L41_NUM_OTP_ELEM] }; static const struct reg_sequence cs35l41_reva0_errata_patch[] = { - { 0x00000040, 0x00005555 }, - { 0x00000040, 0x0000AAAA }, { 0x00003854, 0x05180240 }, { CS35L41_VIMON_SPKMON_RESYNC, 0x00000000 }, { 0x00004310, 0x00000000 }, @@ -637,38 +635,28 @@ static const struct reg_sequence cs35l41_reva0_errata_patch[] = { { CS35L41_IRQ2_DB3, 0x00000000 }, { CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 }, { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 }, - { 0x00000040, 0x0000CCCC }, - { 0x00000040, 0x00003333 }, { CS35L41_PWR_CTRL2, 0x00000000 }, { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, }; static const struct reg_sequence cs35l41_revb0_errata_patch[] = { - { 0x00000040, 0x00005555 }, - { 0x00000040, 0x0000AAAA }, { CS35L41_VIMON_SPKMON_RESYNC, 0x00000000 }, { 0x00004310, 0x00000000 }, { CS35L41_VPVBST_FS_SEL, 0x00000000 }, { CS35L41_BSTCVRT_DCM_CTRL, 0x00000051 }, { CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 }, { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 }, - { 0x00000040, 0x0000CCCC }, - { 0x00000040, 0x00003333 }, { CS35L41_PWR_CTRL2, 0x00000000 }, { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, }; static const struct reg_sequence cs35l41_revb2_errata_patch[] = { - { 0x00000040, 0x00005555 }, - { 0x00000040, 0x0000AAAA }, { CS35L41_VIMON_SPKMON_RESYNC, 0x00000000 }, { 0x00004310, 0x00000000 }, { CS35L41_VPVBST_FS_SEL, 0x00000000 }, { CS35L41_BSTCVRT_DCM_CTRL, 0x00000051 }, { CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 }, { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 }, - { 0x00000040, 0x0000CCCC }, - { 0x00000040, 0x00003333 }, { CS35L41_PWR_CTRL2, 0x00000000 }, { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, }; @@ -756,6 +744,39 @@ static const struct cs35l41_otp_map_element_t *cs35l41_find_otp_map(u32 otp_id) return NULL; } +int cs35l41_test_key_unlock(struct device *dev, struct regmap *regmap) +{ + static const struct reg_sequence unlock[] = { + { CS35L41_TEST_KEY_CTL, 0x00000055 }, + { CS35L41_TEST_KEY_CTL, 0x000000AA }, + }; + int ret; + + ret = regmap_multi_reg_write(regmap, unlock, ARRAY_SIZE(unlock)); + if (ret) + dev_err(dev, "Failed to unlock test key: %d\n", ret); + + return ret; +} +EXPORT_SYMBOL_GPL(cs35l41_test_key_unlock); + +int cs35l41_test_key_lock(struct device *dev, struct regmap *regmap) +{ + static const struct reg_sequence unlock[] = { + { CS35L41_TEST_KEY_CTL, 0x000000CC }, + { CS35L41_TEST_KEY_CTL, 0x00000033 }, + }; + int ret; + + ret = regmap_multi_reg_write(regmap, unlock, ARRAY_SIZE(unlock)); + if (ret) + dev_err(dev, "Failed to lock test key: %d\n", ret); + + return ret; +} +EXPORT_SYMBOL_GPL(cs35l41_test_key_lock); + +/* Must be called with the TEST_KEY unlocked */ int cs35l41_otp_unpack(struct device *dev, struct regmap *regmap) { const struct cs35l41_otp_map_element_t *otp_map_match; @@ -794,17 +815,6 @@ int cs35l41_otp_unpack(struct device *dev, struct regmap *regmap) bit_offset = otp_map_match->bit_offset; word_offset = otp_map_match->word_offset; - ret = regmap_write(regmap, CS35L41_TEST_KEY_CTL, 0x00000055); - if (ret) { - dev_err(dev, "Write Unlock key failed 1/2: %d\n", ret); - goto err_otp_unpack; - } - ret = regmap_write(regmap, CS35L41_TEST_KEY_CTL, 0x000000AA); - if (ret) { - dev_err(dev, "Write Unlock key failed 2/2: %d\n", ret); - goto err_otp_unpack; - } - for (i = 0; i < otp_map_match->num_elements; i++) { dev_dbg(dev, "bitoffset= %d, word_offset=%d, bit_sum mod 32=%d\n", bit_offset, word_offset, bit_sum % 32); @@ -840,16 +850,6 @@ int cs35l41_otp_unpack(struct device *dev, struct regmap *regmap) } } - ret = regmap_write(regmap, CS35L41_TEST_KEY_CTL, 0x000000CC); - if (ret) { - dev_err(dev, "Write Lock key failed 1/2: %d\n", ret); - goto err_otp_unpack; - } - ret = regmap_write(regmap, CS35L41_TEST_KEY_CTL, 0x00000033); - if (ret) { - dev_err(dev, "Write Lock key failed 2/2: %d\n", ret); - goto err_otp_unpack; - } ret = 0; err_otp_unpack: @@ -859,6 +859,7 @@ err_otp_unpack: } EXPORT_SYMBOL_GPL(cs35l41_otp_unpack); +/* Must be called with the TEST_KEY unlocked */ int cs35l41_register_errata_patch(struct device *dev, struct regmap *reg, unsigned int reg_revid) { char *rev; diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index 05839fabf97b..e1b9fd8ee996 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -534,19 +534,19 @@ static irqreturn_t cs35l41_irq(int irq, void *data) } static const struct reg_sequence cs35l41_pup_patch[] = { - { 0x00000040, 0x00000055 }, - { 0x00000040, 0x000000AA }, + { CS35L41_TEST_KEY_CTL, 0x00000055 }, + { CS35L41_TEST_KEY_CTL, 0x000000AA }, { 0x00002084, 0x002F1AA0 }, - { 0x00000040, 0x000000CC }, - { 0x00000040, 0x00000033 }, + { CS35L41_TEST_KEY_CTL, 0x000000CC }, + { CS35L41_TEST_KEY_CTL, 0x00000033 }, }; static const struct reg_sequence cs35l41_pdn_patch[] = { - { 0x00000040, 0x00000055 }, - { 0x00000040, 0x000000AA }, + { CS35L41_TEST_KEY_CTL, 0x00000055 }, + { CS35L41_TEST_KEY_CTL, 0x000000AA }, { 0x00002084, 0x002F1AA3 }, - { 0x00000040, 0x000000CC }, - { 0x00000040, 0x00000033 }, + { CS35L41_TEST_KEY_CTL, 0x000000CC }, + { CS35L41_TEST_KEY_CTL, 0x00000033 }, }; static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w, @@ -1329,10 +1329,20 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, goto err; } + cs35l41_test_key_unlock(cs35l41->dev, cs35l41->regmap); + ret = cs35l41_register_errata_patch(cs35l41->dev, cs35l41->regmap, reg_revid); if (ret) goto err; + ret = cs35l41_otp_unpack(cs35l41->dev, cs35l41->regmap); + if (ret < 0) { + dev_err(cs35l41->dev, "OTP Unpack failed: %d\n", ret); + goto err; + } + + cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap); + irq_pol = cs35l41_irq_gpio_config(cs35l41); /* Set interrupt masks for critical errors */ @@ -1347,12 +1357,6 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, goto err; } - ret = cs35l41_otp_unpack(cs35l41->dev, cs35l41->regmap); - if (ret < 0) { - dev_err(cs35l41->dev, "OTP Unpack failed: %d\n", ret); - goto err; - } - ret = cs35l41_set_pdata(cs35l41); if (ret < 0) { dev_err(cs35l41->dev, "Set pdata failed: %d\n", ret); -- cgit v1.2.3 From f517ba4924ad026f2583553db02f3c8bc69de88b Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 7 Jan 2022 16:06:36 +0000 Subject: ASoC: cs35l41: Add support for hibernate memory retention mode The cs35l41 supports a low power DSP memory retention mode. Add support for entering this mode when then device is not in use. Co-authored-by: David Rhodes Signed-off-by: David Rhodes Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20220107160636.6555-3-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/cs35l41.h | 5 + sound/soc/codecs/cs35l41-i2c.c | 1 + sound/soc/codecs/cs35l41-lib.c | 6 ++ sound/soc/codecs/cs35l41-spi.c | 1 + sound/soc/codecs/cs35l41.c | 201 ++++++++++++++++++++++++++++++++++++++++- sound/soc/codecs/cs35l41.h | 4 + 6 files changed, 214 insertions(+), 4 deletions(-) diff --git a/include/sound/cs35l41.h b/include/sound/cs35l41.h index 56289b67b9a0..bf7f9a9aeba0 100644 --- a/include/sound/cs35l41.h +++ b/include/sound/cs35l41.h @@ -40,6 +40,9 @@ #define CS35L41_PROTECT_REL_ERR_IGN 0x00002034 #define CS35L41_GPIO_PAD_CONTROL 0x0000242C #define CS35L41_JTAG_CONTROL 0x00002438 +#define CS35L41_PWRMGT_CTL 0x00002900 +#define CS35L41_WAKESRC_CTL 0x00002904 +#define CS35L41_PWRMGT_STS 0x00002908 #define CS35L41_PLL_CLK_CTRL 0x00002C04 #define CS35L41_DSP_CLK_CTRL 0x00002C08 #define CS35L41_GLOBAL_CLK_CTRL 0x00002C0C @@ -635,6 +638,8 @@ #define CS35L41_INPUT_DSP_TX1 0x32 #define CS35L41_INPUT_DSP_TX2 0x33 +#define CS35L41_WR_PEND_STS_MASK 0x2 + #define CS35L41_PLL_CLK_SEL_MASK 0x07 #define CS35L41_PLL_CLK_SEL_SHIFT 0 #define CS35L41_PLL_CLK_EN_MASK 0x10 diff --git a/sound/soc/codecs/cs35l41-i2c.c b/sound/soc/codecs/cs35l41-i2c.c index eb8dfb6d9c95..faad5c638cb8 100644 --- a/sound/soc/codecs/cs35l41-i2c.c +++ b/sound/soc/codecs/cs35l41-i2c.c @@ -86,6 +86,7 @@ MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_match); static struct i2c_driver cs35l41_i2c_driver = { .driver = { .name = "cs35l41", + .pm = &cs35l41_pm_ops, .of_match_table = of_match_ptr(cs35l41_of_match), .acpi_match_table = ACPI_PTR(cs35l41_acpi_match), }, diff --git a/sound/soc/codecs/cs35l41-lib.c b/sound/soc/codecs/cs35l41-lib.c index ecaf67fd7653..e5a56bcbb223 100644 --- a/sound/soc/codecs/cs35l41-lib.c +++ b/sound/soc/codecs/cs35l41-lib.c @@ -90,6 +90,9 @@ static bool cs35l41_readable_reg(struct device *dev, unsigned int reg) case CS35L41_PROTECT_REL_ERR_IGN: case CS35L41_GPIO_PAD_CONTROL: case CS35L41_JTAG_CONTROL: + case CS35L41_PWRMGT_CTL: + case CS35L41_WAKESRC_CTL: + case CS35L41_PWRMGT_STS: case CS35L41_PLL_CLK_CTRL: case CS35L41_DSP_CLK_CTRL: case CS35L41_GLOBAL_CLK_CTRL: @@ -376,6 +379,9 @@ static bool cs35l41_volatile_reg(struct device *dev, unsigned int reg) case CS35L41_OTPID: case CS35L41_TEST_KEY_CTL: case CS35L41_USER_KEY_CTL: + case CS35L41_PWRMGT_CTL: + case CS35L41_WAKESRC_CTL: + case CS35L41_PWRMGT_STS: case CS35L41_DTEMP_EN: case CS35L41_IRQ1_STATUS: case CS35L41_IRQ1_STATUS1: diff --git a/sound/soc/codecs/cs35l41-spi.c b/sound/soc/codecs/cs35l41-spi.c index 86bbe2fba956..6dfd5459aa20 100644 --- a/sound/soc/codecs/cs35l41-spi.c +++ b/sound/soc/codecs/cs35l41-spi.c @@ -84,6 +84,7 @@ MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_match); static struct spi_driver cs35l41_spi_driver = { .driver = { .name = "cs35l41", + .pm = &cs35l41_pm_ops, .of_match_table = of_match_ptr(cs35l41_of_match), .acpi_match_table = ACPI_PTR(cs35l41_acpi_match), }, diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index e1b9fd8ee996..77a017694645 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -187,8 +188,14 @@ static int cs35l41_dsp_preload_ev(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: + if (cs35l41->dsp.cs_dsp.booted) + return 0; + return wm_adsp_early_event(w, kcontrol, event); case SND_SOC_DAPM_PRE_PMD: + if (cs35l41->dsp.preloaded) + return 0; + if (cs35l41->dsp.cs_dsp.running) { ret = wm_adsp_event(w, kcontrol, event); if (ret) @@ -209,6 +216,7 @@ static bool cs35l41_check_cspl_mbox_sts(enum cs35l41_cspl_mbox_cmd cmd, case CSPL_MBOX_CMD_UNKNOWN_CMD: return true; case CSPL_MBOX_CMD_PAUSE: + case CSPL_MBOX_CMD_OUT_OF_HIBERNATE: return (sts == CSPL_MBOX_STS_PAUSED); case CSPL_MBOX_CMD_RESUME: return (sts == CSPL_MBOX_STS_RUNNING); @@ -230,7 +238,8 @@ static int cs35l41_set_cspl_mbox_cmd(struct cs35l41_private *cs35l41, // Set mailbox cmd ret = regmap_write(cs35l41->regmap, CS35L41_DSP_VIRT1_MBOX_1, cmd); if (ret < 0) { - dev_err(cs35l41->dev, "Failed to write MBOX: %d\n", ret); + if (cmd != CSPL_MBOX_CMD_OUT_OF_HIBERNATE) + dev_err(cs35l41->dev, "Failed to write MBOX: %d\n", ret); return ret; } @@ -413,6 +422,8 @@ static irqreturn_t cs35l41_irq(int irq, void *data) int ret = IRQ_NONE; unsigned int i; + pm_runtime_get_sync(cs35l41->dev); + for (i = 0; i < ARRAY_SIZE(status); i++) { regmap_read(cs35l41->regmap, CS35L41_IRQ1_STATUS1 + (i * CS35L41_REGSTRIDE), @@ -425,7 +436,7 @@ static irqreturn_t cs35l41_irq(int irq, void *data) /* Check to see if unmasked bits are active */ if (!(status[0] & ~masks[0]) && !(status[1] & ~masks[1]) && !(status[2] & ~masks[2]) && !(status[3] & ~masks[3])) - return IRQ_NONE; + goto done; if (status[3] & CS35L41_OTP_BOOT_DONE) { regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_MASK4, @@ -530,6 +541,10 @@ static irqreturn_t cs35l41_irq(int irq, void *data) ret = IRQ_HANDLED; } +done: + pm_runtime_mark_last_busy(cs35l41->dev); + pm_runtime_put_autosuspend(cs35l41->dev); + return ret; } @@ -1180,6 +1195,7 @@ static int cs35l41_dsp_init(struct cs35l41_private *cs35l41) dsp->cs_dsp.type = WMFW_HALO; dsp->cs_dsp.rev = 0; dsp->fw = 9; /* 9 is WM_ADSP_FW_SPK_PROT in wm_adsp.c */ + dsp->toggle_preload = true; dsp->cs_dsp.dev = cs35l41->dev; dsp->cs_dsp.regmap = cs35l41->regmap; dsp->cs_dsp.base = CS35L41_DSP1_CTRL_BASE; @@ -1367,20 +1383,32 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, if (ret < 0) goto err; + pm_runtime_set_autosuspend_delay(cs35l41->dev, 3000); + pm_runtime_use_autosuspend(cs35l41->dev); + pm_runtime_mark_last_busy(cs35l41->dev); + pm_runtime_set_active(cs35l41->dev); + pm_runtime_get_noresume(cs35l41->dev); + pm_runtime_enable(cs35l41->dev); + ret = devm_snd_soc_register_component(cs35l41->dev, &soc_component_dev_cs35l41, cs35l41_dai, ARRAY_SIZE(cs35l41_dai)); if (ret < 0) { dev_err(cs35l41->dev, "Register codec failed: %d\n", ret); - goto err_dsp; + goto err_pm; } + pm_runtime_put_autosuspend(cs35l41->dev); + dev_info(cs35l41->dev, "Cirrus Logic CS35L41 (%x), Revision: %02X\n", regid, reg_revid); return 0; -err_dsp: +err_pm: + pm_runtime_disable(cs35l41->dev); + pm_runtime_put_noidle(cs35l41->dev); + wm_adsp2_remove(&cs35l41->dsp); err: regulator_bulk_disable(CS35L41_NUM_SUPPLIES, cs35l41->supplies); @@ -1392,13 +1420,178 @@ EXPORT_SYMBOL_GPL(cs35l41_probe); void cs35l41_remove(struct cs35l41_private *cs35l41) { + pm_runtime_get_sync(cs35l41->dev); + pm_runtime_disable(cs35l41->dev); + regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1, 0xFFFFFFFF); wm_adsp2_remove(&cs35l41->dsp); + + pm_runtime_put_noidle(cs35l41->dev); + regulator_bulk_disable(CS35L41_NUM_SUPPLIES, cs35l41->supplies); gpiod_set_value_cansleep(cs35l41->reset_gpio, 0); } EXPORT_SYMBOL_GPL(cs35l41_remove); +static int __maybe_unused cs35l41_runtime_suspend(struct device *dev) +{ + struct cs35l41_private *cs35l41 = dev_get_drvdata(dev); + + dev_dbg(cs35l41->dev, "Runtime suspend\n"); + + if (!cs35l41->dsp.preloaded || !cs35l41->dsp.cs_dsp.running) + return 0; + + dev_dbg(cs35l41->dev, "Enter hibernate\n"); + + regmap_write(cs35l41->regmap, CS35L41_WAKESRC_CTL, 0x0088); + regmap_write(cs35l41->regmap, CS35L41_WAKESRC_CTL, 0x0188); + + // Don't wait for ACK since bus activity would wake the device + regmap_write(cs35l41->regmap, CS35L41_DSP_VIRT1_MBOX_1, + CSPL_MBOX_CMD_HIBERNATE); + + regcache_cache_only(cs35l41->regmap, true); + regcache_mark_dirty(cs35l41->regmap); + + return 0; +} + +static void cs35l41_wait_for_pwrmgt_sts(struct cs35l41_private *cs35l41) +{ + const int pwrmgt_retries = 10; + unsigned int sts; + int i, ret; + + for (i = 0; i < pwrmgt_retries; i++) { + ret = regmap_read(cs35l41->regmap, CS35L41_PWRMGT_STS, &sts); + if (ret) + dev_err(cs35l41->dev, "Failed to read PWRMGT_STS: %d\n", ret); + else if (!(sts & CS35L41_WR_PEND_STS_MASK)) + return; + + udelay(20); + } + + dev_err(cs35l41->dev, "Timed out reading PWRMGT_STS\n"); +} + +static int cs35l41_exit_hibernate(struct cs35l41_private *cs35l41) +{ + const int wake_retries = 20; + const int sleep_retries = 5; + int ret, i, j; + + for (i = 0; i < sleep_retries; i++) { + dev_dbg(cs35l41->dev, "Exit hibernate\n"); + + for (j = 0; j < wake_retries; j++) { + ret = cs35l41_set_cspl_mbox_cmd(cs35l41, + CSPL_MBOX_CMD_OUT_OF_HIBERNATE); + if (!ret) + break; + + usleep_range(100, 200); + } + + if (j < wake_retries) { + dev_dbg(cs35l41->dev, "Wake success at cycle: %d\n", j); + return 0; + } + + dev_err(cs35l41->dev, "Wake failed, re-enter hibernate: %d\n", ret); + + cs35l41_wait_for_pwrmgt_sts(cs35l41); + regmap_write(cs35l41->regmap, CS35L41_WAKESRC_CTL, 0x0088); + + cs35l41_wait_for_pwrmgt_sts(cs35l41); + regmap_write(cs35l41->regmap, CS35L41_WAKESRC_CTL, 0x0188); + + cs35l41_wait_for_pwrmgt_sts(cs35l41); + regmap_write(cs35l41->regmap, CS35L41_PWRMGT_CTL, 0x3); + } + + dev_err(cs35l41->dev, "Timed out waking device\n"); + + return -ETIMEDOUT; +} + +static int __maybe_unused cs35l41_runtime_resume(struct device *dev) +{ + struct cs35l41_private *cs35l41 = dev_get_drvdata(dev); + int ret; + + dev_dbg(cs35l41->dev, "Runtime resume\n"); + + if (!cs35l41->dsp.preloaded || !cs35l41->dsp.cs_dsp.running) + return 0; + + regcache_cache_only(cs35l41->regmap, false); + + ret = cs35l41_exit_hibernate(cs35l41); + if (ret) + return ret; + + /* Test key needs to be unlocked to allow the OTP settings to re-apply */ + cs35l41_test_key_unlock(cs35l41->dev, cs35l41->regmap); + ret = regcache_sync(cs35l41->regmap); + cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap); + if (ret) { + dev_err(cs35l41->dev, "Failed to restore register cache: %d\n", ret); + return ret; + } + + return 0; +} + +static int __maybe_unused cs35l41_sys_suspend(struct device *dev) +{ + struct cs35l41_private *cs35l41 = dev_get_drvdata(dev); + + dev_dbg(cs35l41->dev, "System suspend, disabling IRQ\n"); + disable_irq(cs35l41->irq); + + return 0; +} + +static int __maybe_unused cs35l41_sys_suspend_noirq(struct device *dev) +{ + struct cs35l41_private *cs35l41 = dev_get_drvdata(dev); + + dev_dbg(cs35l41->dev, "Late system suspend, reenabling IRQ\n"); + enable_irq(cs35l41->irq); + + return 0; +} + +static int __maybe_unused cs35l41_sys_resume_noirq(struct device *dev) +{ + struct cs35l41_private *cs35l41 = dev_get_drvdata(dev); + + dev_dbg(cs35l41->dev, "Early system resume, disabling IRQ\n"); + disable_irq(cs35l41->irq); + + return 0; +} + +static int __maybe_unused cs35l41_sys_resume(struct device *dev) +{ + struct cs35l41_private *cs35l41 = dev_get_drvdata(dev); + + dev_dbg(cs35l41->dev, "System resume, reenabling IRQ\n"); + enable_irq(cs35l41->irq); + + return 0; +} + +const struct dev_pm_ops cs35l41_pm_ops = { + SET_RUNTIME_PM_OPS(cs35l41_runtime_suspend, cs35l41_runtime_resume, NULL) + + SET_SYSTEM_SLEEP_PM_OPS(cs35l41_sys_suspend, cs35l41_sys_resume) + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(cs35l41_sys_suspend_noirq, cs35l41_sys_resume_noirq) +}; +EXPORT_SYMBOL_GPL(cs35l41_pm_ops); + MODULE_DESCRIPTION("ASoC CS35L41 driver"); MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, "); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l41.h b/sound/soc/codecs/cs35l41.h index 26a08d58a8c3..88a3d6e3434f 100644 --- a/sound/soc/codecs/cs35l41.h +++ b/sound/soc/codecs/cs35l41.h @@ -21,6 +21,8 @@ #define CS35L41_RX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) #define CS35L41_TX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) +extern const struct dev_pm_ops cs35l41_pm_ops; + enum cs35l41_cspl_mbox_status { CSPL_MBOX_STS_RUNNING = 0, CSPL_MBOX_STS_PAUSED = 1, @@ -33,6 +35,8 @@ enum cs35l41_cspl_mbox_cmd { CSPL_MBOX_CMD_RESUME = 2, CSPL_MBOX_CMD_REINIT = 3, CSPL_MBOX_CMD_STOP_PRE_REINIT = 4, + CSPL_MBOX_CMD_HIBERNATE = 5, + CSPL_MBOX_CMD_OUT_OF_HIBERNATE = 6, CSPL_MBOX_CMD_UNKNOWN_CMD = -1, CSPL_MBOX_CMD_INVALID_SEQUENCE = -2, }; -- cgit v1.2.3 From 3e4518035a23e02ef818ea22570868a82956c6b0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 7 Jan 2022 10:26:47 +0100 Subject: ALSA: hda: Fix dependency on ASoC cs35l41 codec The recently added support for CS35L41 codec unconditionally selects CONFIG_SND_SOC_CS35L41_LIB, but this can't work unless the top-level CONFIG_SND_SOC is enabled. This patch adds the proper dependency. Fixes: 7b2f3eb492da ("ALSA: hda: cs35l41: Add support for CS35L41 in HDA systems") Link: https://lore.kernel.org/r/20220107092647.20258-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/hda/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 84cefc006f29..68effb74866c 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -97,6 +97,7 @@ config SND_HDA_SCODEC_CS35L41 config SND_HDA_SCODEC_CS35L41_I2C tristate "Build CS35L41 HD-audio side codec support for I2C Bus" depends on ACPI + depends on SND_SOC select SND_HDA_GENERIC select SND_SOC_CS35L41_LIB select SND_HDA_SCODEC_CS35L41 @@ -110,6 +111,7 @@ comment "Set to Y if you want auto-loading the side codec driver" config SND_HDA_SCODEC_CS35L41_SPI tristate "Build CS35L41 HD-audio codec support for SPI Bus" depends on ACPI + depends on SND_SOC select SND_HDA_GENERIC select SND_SOC_CS35L41_LIB select SND_HDA_SCODEC_CS35L41 -- cgit v1.2.3 From 2e88c6a805fc5311e27e0f6efe243842634052ab Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 9 Jan 2022 09:13:37 +0100 Subject: ALSA: hda: Fix dependencies of CS35L41 on SPI/I2C buses CS35L41 SPI and I2C drivers depend on those buses, hence they have to have dependencies in Kconfig; otherwise it may result in missing symbols. Fixes: 7b2f3eb492da ("ALSA: hda: cs35l41: Add support for CS35L41 in HDA systems") Reported-by: kernel test robot Link: https://lore.kernel.org/r/20220109081337.30623-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/hda/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 68effb74866c..febe1c2b7d9a 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -96,6 +96,7 @@ config SND_HDA_SCODEC_CS35L41 config SND_HDA_SCODEC_CS35L41_I2C tristate "Build CS35L41 HD-audio side codec support for I2C Bus" + depends on I2C depends on ACPI depends on SND_SOC select SND_HDA_GENERIC @@ -110,6 +111,7 @@ comment "Set to Y if you want auto-loading the side codec driver" config SND_HDA_SCODEC_CS35L41_SPI tristate "Build CS35L41 HD-audio codec support for SPI Bus" + depends on SPI_MASTER depends on ACPI depends on SND_SOC select SND_HDA_GENERIC -- cgit v1.2.3 From 10b1a5a99c6ac42be7a490676aec626fba28b048 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 11 Jan 2022 10:22:32 +0300 Subject: ALSA: hda: cs35l41: fix double free on error in probe() If we encounter an error after the kfree(acpi_hw_cfg); then the goto err; will result in a double free. Fixes: 7b2f3eb492da ("ALSA: hda: cs35l41: Add support for CS35L41 in HDA systems") Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/20220111072232.GG11243@kili Signed-off-by: Takashi Iwai --- sound/pci/hda/cs35l41_hda.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c index aa5bb6977792..30b40d865863 100644 --- a/sound/pci/hda/cs35l41_hda.c +++ b/sound/pci/hda/cs35l41_hda.c @@ -477,6 +477,7 @@ int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int i if (ret) goto err; kfree(acpi_hw_cfg); + acpi_hw_cfg = NULL; if (cs35l41->reg_seq->probe) { ret = regmap_register_patch(cs35l41->regmap, cs35l41->reg_seq->probe, -- cgit v1.2.3 From 19980aa10d2d944ed8fe345ce2eb87c2cb4bedf8 Mon Sep 17 00:00:00 2001 From: Brent Lu Date: Thu, 13 Jan 2022 18:52:19 +0800 Subject: ALSA: hda: intel-dsp-config: add JasperLake support Add rules to select SOF driver for Jasper Lake systems if digital microphone is present or the system is a Chromebook. Signed-off-by: Brent Lu Link: https://lore.kernel.org/r/20220113105220.1114694-2-brent.lu@intel.com Signed-off-by: Takashi Iwai --- sound/hda/intel-dsp-config.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/intel-dsp-config.c index c26229ed642f..3a3026fa4a17 100644 --- a/sound/hda/intel-dsp-config.c +++ b/sound/hda/intel-dsp-config.c @@ -309,13 +309,30 @@ static const struct config_entry config_table[] = { }, #endif -/* JasperLake */ +/* Jasper Lake */ #if IS_ENABLED(CONFIG_SND_SOC_SOF_JASPERLAKE) + { + .flags = FLAG_SOF, + .device = 0x4dc8, + .dmi_table = (const struct dmi_system_id []) { + { + .ident = "Google Chromebooks", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + } + }, + {} + } + }, { .flags = FLAG_SOF, .device = 0x4dc8, .codec_hid = "ESSX8336", }, + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, + .device = 0x4dc8, + }, #endif /* Tigerlake */ -- cgit v1.2.3 From 081c73701ef0c2a4f6a127da824a641ae6505fbe Mon Sep 17 00:00:00 2001 From: Brent Lu Date: Thu, 13 Jan 2022 18:52:20 +0800 Subject: ALSA: hda: intel-dsp-config: reorder the config table Entries without dmi_table nor codec_hid field need to be placed after entries with these two fields or they will be always selected. Signed-off-by: Brent Lu Link: https://lore.kernel.org/r/20220113105220.1114694-3-brent.lu@intel.com Signed-off-by: Takashi Iwai --- sound/hda/intel-dsp-config.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/intel-dsp-config.c index 3a3026fa4a17..4fb90ceb4053 100644 --- a/sound/hda/intel-dsp-config.c +++ b/sound/hda/intel-dsp-config.c @@ -249,13 +249,13 @@ static const struct config_entry config_table[] = { } }, { - .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, + .flags = FLAG_SOF, .device = 0x02c8, + .codec_hid = "ESSX8336", }, { - .flags = FLAG_SOF, + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, .device = 0x02c8, - .codec_hid = "ESSX8336", }, /* Cometlake-H */ { @@ -278,14 +278,14 @@ static const struct config_entry config_table[] = { } }, { - .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, - .device = 0x06c8, - }, - { .flags = FLAG_SOF, .device = 0x06c8, .codec_hid = "ESSX8336", }, + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, + .device = 0x06c8, + }, #endif /* Icelake */ @@ -351,17 +351,17 @@ static const struct config_entry config_table[] = { } }, { - .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, + .flags = FLAG_SOF, .device = 0xa0c8, + .codec_hid = "ESSX8336", }, { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, - .device = 0x43c8, + .device = 0xa0c8, }, { - .flags = FLAG_SOF, - .device = 0xa0c8, - .codec_hid = "ESSX8336", + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, + .device = 0x43c8, }, #endif -- cgit v1.2.3