From 11e828cd6b0f283ebe9dc6b4cffd38e3321e7725 Mon Sep 17 00:00:00 2001 From: Mert Seftali Date: Sun, 14 Jun 2026 14:40:19 +0200 Subject: ASoC: SOF: ipc4-topology: Return error for invalid number of formats When the number of input or output formats is zero, sof_ipc4_widget_setup_comp_src() and sof_ipc4_widget_setup_comp_asrc() print an error and jump to the cleanup label. At that point 'ret' is still 0, because the earlier sof_ipc4_get_audio_fmt() call succeeded, so the function returns success and the caller never finds out that the widget setup actually failed. Set ret to -EINVAL before the goto so the error gets reported. Fixes: 21a5adffad46 ("ASoC: SOF: ipc4-topology: Validate the number of in/out formats for src/asrc") Reported-by: kernel test robot Reported-by: Dan Carpenter Closes: https://lore.kernel.org/r/202606111431.Uky3T0tF-lkp@intel.com/ Signed-off-by: Mert Seftali Link: https://patch.msgid.link/20260614124019.19259-1-mertsftl@gmail.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-topology.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 95ad5266b0c6..8ac7dde32f77 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -1127,6 +1127,7 @@ static int sof_ipc4_widget_setup_comp_src(struct snd_sof_widget *swidget) "Invalid number of formats: input: %d, output: %d\n", src->available_fmt.num_input_formats, src->available_fmt.num_output_formats); + ret = -EINVAL; goto err; } @@ -1179,6 +1180,7 @@ static int sof_ipc4_widget_setup_comp_asrc(struct snd_sof_widget *swidget) "Invalid number of formats: input: %d, output: %d\n", asrc->available_fmt.num_input_formats, asrc->available_fmt.num_output_formats); + ret = -EINVAL; goto err; } -- cgit v1.2.3 From 6ed013a581d00f84bbd0c3c60f7a108bef93fc4e Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 12 Jun 2026 09:01:12 +0700 Subject: ASoC: meson: Use dev_err_probe() for device reset failures device_reset() may return -EPROBE_DEFER. Switch to dev_err_probe() so probe failures are reported consistently and deferred probing is handled properly. This matches the existing pattern used in aiu_probe(). No functional change intended. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260612020113.9557-1-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/meson/g12a-toacodec.c | 2 +- sound/soc/meson/g12a-tohdmitx.c | 2 +- sound/soc/meson/t9015.c | 6 ++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/sound/soc/meson/g12a-toacodec.c b/sound/soc/meson/g12a-toacodec.c index a95375b53f0a..21941ee552c5 100644 --- a/sound/soc/meson/g12a-toacodec.c +++ b/sound/soc/meson/g12a-toacodec.c @@ -312,7 +312,7 @@ static int g12a_toacodec_probe(struct platform_device *pdev) ret = device_reset(dev); if (ret) - return 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/g12a-tohdmitx.c b/sound/soc/meson/g12a-tohdmitx.c index d541ca4acfaf..967109ca2b57 100644 --- a/sound/soc/meson/g12a-tohdmitx.c +++ b/sound/soc/meson/g12a-tohdmitx.c @@ -251,7 +251,7 @@ static int g12a_tohdmitx_probe(struct platform_device *pdev) ret = device_reset(dev); if (ret) - return 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/t9015.c b/sound/soc/meson/t9015.c index da1a93946d67..f0b55aee5241 100644 --- a/sound/soc/meson/t9015.c +++ b/sound/soc/meson/t9015.c @@ -265,10 +265,8 @@ static int t9015_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(priv->avdd), "failed to AVDD\n"); ret = device_reset(dev); - if (ret) { - dev_err(dev, "reset failed\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)) { -- cgit v1.2.3 From ce3733792d38ade8df8ce288a96ff0c6ad858bf3 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 15 Jun 2026 14:44:56 +0800 Subject: ASoC: sma1303: remove fault-check sysfs group on remove sma1303_i2c_probe() creates a sysfs group that exposes the fault-check controls. The check_fault_status store callback can queue check_fault_work. sma1303_i2c_remove() only cancels the delayed work. It does not remove the sysfs group, so the controls can remain published after remove while their callbacks still use the driver data and can queue the work again. Remove the sysfs group before cancelling the delayed work. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260615064456.28615-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/sma1303.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/codecs/sma1303.c b/sound/soc/codecs/sma1303.c index c7aaf98ef71e..38677badfc18 100644 --- a/sound/soc/codecs/sma1303.c +++ b/sound/soc/codecs/sma1303.c @@ -1778,6 +1778,8 @@ static void sma1303_i2c_remove(struct i2c_client *client) struct sma1303_priv *sma1303 = (struct sma1303_priv *) i2c_get_clientdata(client); + if (sma1303->attr_grp) + sysfs_remove_group(sma1303->kobj, sma1303->attr_grp); cancel_delayed_work_sync(&sma1303->check_fault_work); } -- cgit v1.2.3 From ae375f8d063d71e598c0d7e14fc056154c84d7a7 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 15 Jun 2026 14:45:48 +0800 Subject: ASoC: tlv320aic26: remove keyclick sysfs file aic26_probe() creates the keyclick sysfs file on the component device. The file callback uses the ASoC component pointer stored in the driver private data. There is no matching remove callback, so the sysfs file can remain after the component is removed while its backing component state is gone. Add a component remove callback that removes the keyclick file. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260615064549.34110-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic26.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c index e5dfb3d752a3..84e954311c1b 100644 --- a/sound/soc/codecs/tlv320aic26.c +++ b/sound/soc/codecs/tlv320aic26.c @@ -320,8 +320,14 @@ static int aic26_probe(struct snd_soc_component *component) return 0; } +static void aic26_remove(struct snd_soc_component *component) +{ + device_remove_file(component->dev, &dev_attr_keyclick); +} + static const struct snd_soc_component_driver aic26_soc_component_dev = { .probe = aic26_probe, + .remove = aic26_remove, .controls = aic26_snd_controls, .num_controls = ARRAY_SIZE(aic26_snd_controls), .dapm_widgets = tlv320aic26_dapm_widgets, -- cgit v1.2.3 From e9f08e779976bbfef7d168c70350083878db7e2e Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 15 Jun 2026 21:44:39 +0800 Subject: ASoC: SOF: add Intel UAOL sof_ipc_dai_type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The type will be used for Intel USB Audio Offload Link (UAOL) DAI. Signed-off-by: Bard Liao Reviewed-by: Kai Vehmanen Reviewed-by: Péter Ujfalusi Link: https://patch.msgid.link/20260615134439.1044872-1-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- include/sound/sof/dai.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/sound/sof/dai.h b/include/sound/sof/dai.h index 36809f712723..0b6a6ba6489a 100644 --- a/include/sound/sof/dai.h +++ b/include/sound/sof/dai.h @@ -90,6 +90,7 @@ enum sof_ipc_dai_type { SOF_DAI_AMD_HS_VIRTUAL, /**< AMD ACP HS VIRTUAL */ SOF_DAI_IMX_MICFIL, /** < i.MX MICFIL PDM */ SOF_DAI_AMD_SDW, /**< AMD ACP SDW */ + SOF_DAI_INTEL_UAOL, /**< Intel UAOL */ }; /* general purpose DAI configuration */ -- cgit v1.2.3 From 012dfa0c45ecaadd48549938a9f35d1a329a2c5d Mon Sep 17 00:00:00 2001 From: Luca Leonardo Scorcia Date: Mon, 15 Jun 2026 20:57:50 +0200 Subject: ASoC: dt-bindings: mtk-btcvsd-snd: Convert to DT Schema Convert the mtk-btcvsd-snd.txt DT binding to DT Schema format. Signed-off-by: Luca Leonardo Scorcia Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260615185810.11804-1-l.scorcia@gmail.com Signed-off-by: Mark Brown --- .../bindings/sound/mediatek,mtk-btcvsd-snd.yaml | 59 ++++++++++++++++++++++ .../devicetree/bindings/sound/mtk-btcvsd-snd.txt | 24 --------- 2 files changed, 59 insertions(+), 24 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/mediatek,mtk-btcvsd-snd.yaml delete mode 100644 Documentation/devicetree/bindings/sound/mtk-btcvsd-snd.txt diff --git a/Documentation/devicetree/bindings/sound/mediatek,mtk-btcvsd-snd.yaml b/Documentation/devicetree/bindings/sound/mediatek,mtk-btcvsd-snd.yaml new file mode 100644 index 000000000000..1b7451655476 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mediatek,mtk-btcvsd-snd.yaml @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/mediatek,mtk-btcvsd-snd.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Mediatek ALSA BT SCO CVSD/MSBC + +maintainers: + - Luca Leonardo Scorcia + +properties: + compatible: + const: mediatek,mtk-btcvsd-snd + + reg: + items: + - description: PKV region + - description: SRAM_BANK2 region + + interrupts: + items: + - description: BT-SCO interrupt + + mediatek,infracfg: + $ref: /schemas/types.yaml#/definitions/phandle + description: The phandle of the infracfg controller + + mediatek,offset: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: Array of register offsets and masks + items: + - description: infra_misc_offset + - description: infra_conn_bt_cvsd_mask + - description: cvsd_mcu_read_offset + - description: cvsd_mcu_write_offset + - description: cvsd_packet_indicator_offset + +required: + - compatible + - reg + - interrupts + - mediatek,infracfg + - mediatek,offset + +additionalProperties: false + +examples: + - | + #include + + mtk-btcvsd-snd@18000000 { + compatible = "mediatek,mtk-btcvsd-snd"; + reg = <0x18000000 0x1000>, + <0x18080000 0x8000>; + interrupts = ; + mediatek,infracfg = <&infrasys>; + mediatek,offset = <0xf00 0x800 0xfd0 0xfd4 0xfd8>; + }; diff --git a/Documentation/devicetree/bindings/sound/mtk-btcvsd-snd.txt b/Documentation/devicetree/bindings/sound/mtk-btcvsd-snd.txt deleted file mode 100644 index 679e44839b48..000000000000 --- a/Documentation/devicetree/bindings/sound/mtk-btcvsd-snd.txt +++ /dev/null @@ -1,24 +0,0 @@ -Mediatek ALSA BT SCO CVSD/MSBC Driver - -Required properties: -- compatible = "mediatek,mtk-btcvsd-snd"; -- reg: register location and size of PKV and SRAM_BANK2 -- interrupts: should contain BTSCO interrupt -- mediatek,infracfg: the phandles of INFRASYS -- mediatek,offset: Array contains of register offset and mask - infra_misc_offset, - infra_conn_bt_cvsd_mask, - cvsd_mcu_read_offset, - cvsd_mcu_write_offset, - cvsd_packet_indicator_offset - -Example: - - mtk-btcvsd-snd@18000000 { - compatible = "mediatek,mtk-btcvsd-snd"; - reg=<0 0x18000000 0 0x1000>, - <0 0x18080000 0 0x8000>; - interrupts = ; - mediatek,infracfg = <&infrasys>; - mediatek,offset = <0xf00 0x800 0xfd0 0xfd4 0xfd8>; - }; -- cgit v1.2.3 From a33e6fa400d174e7970d4efc30dd2e10911281ab Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Wed, 17 Jun 2026 09:33:45 +0200 Subject: ASoC: SOF: topology: Use more common error handling code in sof_link_load() Use an additional label so that a bit of exception handling can be better reused at the end of this function implementation. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Link: https://patch.msgid.link/40b93192-68d2-4de1-845b-9c9ba994a75b@web.de Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 42a2d90bb705..f709935593ef 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1943,8 +1943,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ private->array, le32_to_cpu(private->size)); if (ret < 0) { dev_err(scomp->dev, "Failed tp parse common DAI link tokens\n"); - kfree(slink); - return ret; + goto free_slink; } token_list = tplg_ops ? tplg_ops->token_list : NULL; @@ -2013,8 +2012,8 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ /* allocate memory for tuples array */ slink->tuples = kzalloc_objs(*slink->tuples, num_tuples); if (!slink->tuples) { - kfree(slink); - return -ENOMEM; + ret = -ENOMEM; + goto free_slink; } if (token_list[SOF_DAI_LINK_TOKENS].tokens) { @@ -2070,6 +2069,7 @@ out: err: kfree(slink->tuples); +free_slink: kfree(slink); return ret; -- cgit v1.2.3 From 612ccf42acd14bb2685fa60c3495ca13e63e8989 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Fri, 19 Jun 2026 20:23:25 +0800 Subject: ASoC: rt700-sdw: always drain jack work on remove rt700_sdw_remove() drains jack_detect_work and jack_btn_check_work only when rt700->hw_init is true. That state bit is cleared by rt700_update_status() when the SoundWire slave becomes UNATTACHED, but a jack work item can already have been queued by rt700_interrupt_callback() or rt700_jack_init() while the device was initialized. Do not use hw_init as the remove-time guard for draining these work objects. The delayed works are initialized during rt700_init(), so remove can cancel them unconditionally and pair the object lifetime with the codec-private data lifetime instead of a mutable hardware state bit. This issue was found by our static analysis tool and then confirmed by manual review of the SoundWire status, interrupt and remove paths. The remove path should drain work based on whether the work object exists, not on a runtime hardware state bit that can change after the work was queued. A QEMU PoC queued jack_detect_work, simulated SDW_SLAVE_UNATTACHED, and then entered remove. DEBUG_OBJECTS reported an active timer/work object associated with the rt700 jack work path after remove skipped the cancel. This is sent as an RFC because the practical trigger depends on SoundWire core remove ordering after an UNATTACHED status update. If remove cannot run after hw_init has been cleared while jack work is still pending, this is a defensive lifecycle cleanup rather than a reachable race on current systems. Fixes: 737ee8bdf682 ("ASoC: rt700-sdw: use cancel_work_sync() in .remove as well as .suspend") Signed-off-by: Runyu Xiao Link: https://patch.msgid.link/20260619122325.2504287-1-runyu.xiao@seu.edu.cn Signed-off-by: Mark Brown --- sound/soc/codecs/rt700-sdw.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c index 6bc636c86f42..83f7d52cb598 100644 --- a/sound/soc/codecs/rt700-sdw.c +++ b/sound/soc/codecs/rt700-sdw.c @@ -459,10 +459,8 @@ static void rt700_sdw_remove(struct sdw_slave *slave) { struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev); - if (rt700->hw_init) { - cancel_delayed_work_sync(&rt700->jack_detect_work); - cancel_delayed_work_sync(&rt700->jack_btn_check_work); - } + cancel_delayed_work_sync(&rt700->jack_detect_work); + cancel_delayed_work_sync(&rt700->jack_btn_check_work); pm_runtime_disable(&slave->dev); } -- cgit v1.2.3 From 3a89ddcf0c3d9a068631e8c24d5c9e81d1e6512a Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Mon, 22 Jun 2026 17:48:22 +0800 Subject: ASoC: fsl: mpc5200-i2s: Free DMA resources on probe failure mpc5200_audio_dma_create() creates the DMA resources before registering the component. If snd_soc_register_component() fails, the function returns directly and leaves the DMA resources allocated. Call mpc5200_audio_dma_destroy() before returning from this error path. Fixes: f515b67381de ("ASoC: fsl: mpc5200 combine psc_dma platform data") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Link: https://patch.msgid.link/20260622094822.926166-1-haoxiang_li2024@163.com Signed-off-by: Mark Brown --- sound/soc/fsl/mpc5200_psc_i2s.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c index 9ad44eeed6ad..7831136f4f12 100644 --- a/sound/soc/fsl/mpc5200_psc_i2s.c +++ b/sound/soc/fsl/mpc5200_psc_i2s.c @@ -170,6 +170,7 @@ static int psc_i2s_of_probe(struct platform_device *op) psc_i2s_dai, ARRAY_SIZE(psc_i2s_dai)); if (rc != 0) { pr_err("Failed to register DAI\n"); + mpc5200_audio_dma_destroy(op); return rc; } -- cgit v1.2.3 From 016f29997ebd29d6ab59c8162ce0e7f73bd1e517 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Mon, 22 Jun 2026 17:16:20 +0800 Subject: AsoC: intel: sst: fix PCI device reference leak on probe failure intel_sst_probe() takes a reference to the PCI device with pci_dev_get(). If sst_platform_get_resources() fails afterwards, the probe error path cleans up the driver context but does not drop the PCI device reference. Add a pci_dev_put() error path for failures after pci_dev_get(). Fixes: f533a035e4da ("ASoC: Intel: mrfld - create separate module for pci part") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Link: https://patch.msgid.link/20260622091620.897478-1-haoxiang_li2024@163.com Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst/sst_pci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/atom/sst/sst_pci.c b/sound/soc/intel/atom/sst/sst_pci.c index 22ae2d22f121..44bb11c69490 100644 --- a/sound/soc/intel/atom/sst/sst_pci.c +++ b/sound/soc/intel/atom/sst/sst_pci.c @@ -130,13 +130,15 @@ static int intel_sst_probe(struct pci_dev *pci, sst_drv_ctx->pci = pci_dev_get(pci); ret = sst_platform_get_resources(sst_drv_ctx); if (ret < 0) - goto do_free_drv_ctx; + goto do_put_pci; pci_set_drvdata(pci, sst_drv_ctx); sst_configure_runtime_pm(sst_drv_ctx); return ret; +do_put_pci: + pci_dev_put(sst_drv_ctx->pci); do_free_drv_ctx: sst_context_cleanup(sst_drv_ctx); dev_err(sst_drv_ctx->dev, "Probe failed with %d\n", ret); -- cgit v1.2.3 From 6ad4892c4f5cb437a928a02f5b7d37d496aa9268 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Mon, 22 Jun 2026 22:56:45 +0800 Subject: ASoC: hdac_hda: Fix hlink refcount leak on component registration failure hdac_hda_dev_probe() gets the HDA link with snd_hdac_ext_bus_link_get() before registering the ASoC component. If component registration fails, the function returns without dropping the link reference. Always call snd_hdac_ext_bus_link_put() after the registration attempt so the reference taken during probe is balanced on both success and failure. Fixes: 6bae5ea94989 ("ASoC: hdac_hda: add asoc extension for legacy HDA codec drivers") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Link: https://patch.msgid.link/20260622145645.1184986-1-haoxiang_li2024@163.com Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hda.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c index 680e341aa7f1..1ab5f8a26e03 100644 --- a/sound/soc/codecs/hdac_hda.c +++ b/sound/soc/codecs/hdac_hda.c @@ -642,10 +642,8 @@ static int hdac_hda_dev_probe(struct hdac_device *hdev) &hdac_hda_codec, hdac_hda_dais, ARRAY_SIZE(hdac_hda_dais)); - if (ret < 0) { + if (ret < 0) dev_err(&hdev->dev, "%s: failed to register HDA codec %d\n", __func__, ret); - return ret; - } snd_hdac_ext_bus_link_put(hdev->bus, hlink); -- cgit v1.2.3 From 2e5bf7f48bf170f8e615cea408289440c3328926 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 26 Jun 2026 01:07:54 +0000 Subject: ASoC: codecs: max98090: add missing describe "data" for max98090_set_jack() "data" is missing. Add it. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87zf0ids7p.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/codecs/max98090.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index da416329b038..bccce322ccc3 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -2341,6 +2341,7 @@ static irqreturn_t max98090_interrupt(int irq, void *data) * * @component: MAX98090 component * @jack: jack to report detection events on + * @data: can be used if codec driver need extra data for configuring jack * * Enable microphone detection via IRQ on the MAX98090. If GPIOs are * being used to bring out signals to the processor then only platform -- cgit v1.2.3 From 785903b7770f19ea05b8a76081dff691c56fa771 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Mon, 22 Jun 2026 23:11:27 +0800 Subject: ASoC: hdac_hdmi: Fix resource cleanup on probe failure hdac_hdmi_dev_probe() gets the HDA link before allocating and initializing the HDMI codec private data. Several later error paths return directly without dropping the link reference, leaving the hlink refcount unbalanced. Release the link reference on probe failures. Also turn display power off if the failure happens after it has been enabled. This issue was dicussed in: https://lore.kernel.org/all/s5h1s0esk8o.wl-tiwai@suse.de/ I think the paths fixed here are probe failure paths: the device has not been fully initialized or bound, and runtime PM suspend cannot be relied on to balance the reference taken during probe. Signed-off-by: Haoxiang Li Link: https://patch.msgid.link/20260622151127.1198196-1-haoxiang_li2024@163.com Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hdmi.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 3220f9226e0b..38073a70fa61 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -1866,8 +1866,10 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) snd_hdac_ext_bus_link_get(hdev->bus, hlink); hdmi_priv = devm_kzalloc(&hdev->dev, sizeof(*hdmi_priv), GFP_KERNEL); - if (hdmi_priv == NULL) + if (hdmi_priv == NULL) { + snd_hdac_ext_bus_link_put(hdev->bus, hlink); return -ENOMEM; + } snd_hdac_register_chmap_ops(hdev, &hdmi_priv->chmap); hdmi_priv->chmap.ops.get_chmap = hdac_hdmi_get_chmap; @@ -1876,8 +1878,10 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) hdmi_priv->chmap.ops.get_spk_alloc = hdac_hdmi_get_spk_alloc; hdmi_priv->hdev = hdev; - if (!hdac_id) + if (!hdac_id) { + snd_hdac_ext_bus_link_put(hdev->bus, hlink); return -ENODEV; + } if (hdac_id->driver_data) hdmi_priv->drv_data = @@ -1902,6 +1906,8 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) if (ret < 0) { dev_err(&hdev->dev, "Failed in parse and map nid with err: %d\n", ret); + snd_hdac_ext_bus_link_put(hdev->bus, hlink); + snd_hdac_display_power(hdev->bus, hdev->addr, false); return ret; } snd_hdac_refresh_widgets(hdev); -- cgit v1.2.3 From 3359ba93d01a23b2e4249e9e44ccfe48eb9c5d71 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Thu, 18 Jun 2026 10:38:18 +0800 Subject: ASoC: fsl_audmix: rework runtime PM handling in probe After pm_runtime_enable() the AUDMIX block is powered off and stays suspended until the first runtime resume. Register writes issued between probe() and the first resume (e.g. from DAPM or ALSA control paths) target unpowered hardware and cause a system hang. Fix this by calling pm_runtime_resume_and_get() immediately after pm_runtime_enable() to power the hardware up and enable its clocks. Release the reference afterwards with pm_runtime_put() to allow the runtime PM framework to suspend the device and switch the regmap to cache-only mode when idle. When CONFIG_PM is disabled or runtime PM is not enabled, pm_runtime_* calls are stubs that do not power up the hardware. Handle this case explicitly by calling fsl_audmix_runtime_resume() directly so the hardware is always initialised and its clocks are enabled, ensuring register accesses succeed regardless of PM configuration. Fixes: be1df61cf06ef ("ASoC: fsl: Add Audio Mixer CPU DAI driver") Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/20260618023818.31618-1-shengjiu.wang@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_audmix.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/sound/soc/fsl/fsl_audmix.c b/sound/soc/fsl/fsl_audmix.c index f819f33ec46b..2885cc10b02d 100644 --- a/sound/soc/fsl/fsl_audmix.c +++ b/sound/soc/fsl/fsl_audmix.c @@ -457,6 +457,9 @@ static const struct of_device_id fsl_audmix_ids[] = { }; MODULE_DEVICE_TABLE(of, fsl_audmix_ids); +static int fsl_audmix_runtime_resume(struct device *dev); +static int fsl_audmix_runtime_suspend(struct device *dev); + static int fsl_audmix_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -488,13 +491,25 @@ static int fsl_audmix_probe(struct platform_device *pdev) spin_lock_init(&priv->lock); platform_set_drvdata(pdev, priv); pm_runtime_enable(dev); + if (!pm_runtime_enabled(dev)) { + ret = fsl_audmix_runtime_resume(dev); + if (ret) + goto err_disable_pm; + } + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + goto err_pm_get_sync; + + /* To enable regmap cache only when runtime PM enabled */ + pm_runtime_put(dev); ret = devm_snd_soc_register_component(dev, &fsl_audmix_component, fsl_audmix_dai, ARRAY_SIZE(fsl_audmix_dai)); if (ret) { dev_err(dev, "failed to register ASoC DAI\n"); - goto err_disable_pm; + goto err_pm_get_sync; } /* @@ -506,12 +521,15 @@ static int fsl_audmix_probe(struct platform_device *pdev) if (IS_ERR(priv->pdev)) { ret = PTR_ERR(priv->pdev); dev_err(dev, "failed to register platform: %d\n", ret); - goto err_disable_pm; + goto err_pm_get_sync; } } return 0; +err_pm_get_sync: + if (!pm_runtime_status_suspended(dev)) + fsl_audmix_runtime_suspend(dev); err_disable_pm: pm_runtime_disable(dev); return ret; @@ -522,6 +540,8 @@ static void fsl_audmix_remove(struct platform_device *pdev) struct fsl_audmix *priv = dev_get_drvdata(&pdev->dev); pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + fsl_audmix_runtime_suspend(&pdev->dev); if (priv->pdev) platform_device_unregister(priv->pdev); -- cgit v1.2.3 From d7e261b7ad1bc96a2ee249c6694691244c099acf Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 15 Jun 2026 16:38:14 +0700 Subject: ASoC: fsl_asrc: Use guard() for spin locks Clean up the code using guard() for spin locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260615093824.115751-2-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_asrc.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index 5fda9b647c70..0b28bcfa47fe 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -222,10 +222,9 @@ static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair) enum asrc_pair_index index = ASRC_INVALID_PAIR; struct fsl_asrc *asrc = pair->asrc; struct device *dev = &asrc->pdev->dev; - unsigned long lock_flags; int i, ret = 0; - spin_lock_irqsave(&asrc->lock, lock_flags); + guard(spinlock_irqsave)(&asrc->lock); for (i = ASRC_PAIR_A; i < ASRC_PAIR_MAX_NUM; i++) { if (asrc->pair[i] != NULL) @@ -250,8 +249,6 @@ static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair) pair->index = index; } - spin_unlock_irqrestore(&asrc->lock, lock_flags); - return ret; } @@ -265,19 +262,16 @@ static void fsl_asrc_release_pair(struct fsl_asrc_pair *pair) { struct fsl_asrc *asrc = pair->asrc; enum asrc_pair_index index = pair->index; - unsigned long lock_flags; /* Make sure the pair is disabled */ regmap_update_bits(asrc->regmap, REG_ASRCTR, ASRCTR_ASRCEi_MASK(index), 0); - spin_lock_irqsave(&asrc->lock, lock_flags); + guard(spinlock_irqsave)(&asrc->lock); asrc->channel_avail += pair->channels; asrc->pair[index] = NULL; pair->error = 0; - - spin_unlock_irqrestore(&asrc->lock, lock_flags); } /** -- cgit v1.2.3 From 58712476dee21cc3d3cb3b0b85a8ca3a86d480fe Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 15 Jun 2026 16:38:15 +0700 Subject: ASoC: fsl_audmix: Use guard() for spin locks Clean up the code using guard() for spin locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Frank Li Link: https://patch.msgid.link/20260615093824.115751-3-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_audmix.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/sound/soc/fsl/fsl_audmix.c b/sound/soc/fsl/fsl_audmix.c index f819f33ec46b..a27370862ee8 100644 --- a/sound/soc/fsl/fsl_audmix.c +++ b/sound/soc/fsl/fsl_audmix.c @@ -286,7 +286,6 @@ static int fsl_audmix_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct fsl_audmix *priv = snd_soc_dai_get_drvdata(dai); - unsigned long lock_flags; /* Capture stream shall not be handled */ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) @@ -296,16 +295,14 @@ static int fsl_audmix_dai_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - spin_lock_irqsave(&priv->lock, lock_flags); - priv->tdms |= BIT(dai->driver->id); - spin_unlock_irqrestore(&priv->lock, lock_flags); + scoped_guard(spinlock_irqsave, &priv->lock) + priv->tdms |= BIT(dai->driver->id); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - spin_lock_irqsave(&priv->lock, lock_flags); - priv->tdms &= ~BIT(dai->driver->id); - spin_unlock_irqrestore(&priv->lock, lock_flags); + scoped_guard(spinlock_irqsave, &priv->lock) + priv->tdms &= ~BIT(dai->driver->id); break; default: return -EINVAL; -- cgit v1.2.3 From 48d84310be60578d4413139661b433b1d33aca1d Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 15 Jun 2026 16:38:16 +0700 Subject: ASoC: fsl_easrc: Use guard() for spin locks Clean up the code using guard() for spin locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Frank Li Link: https://patch.msgid.link/20260615093824.115751-4-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_easrc.c | 36 ++++++++++-------------------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c index 114a6c0b6b73..edfd943197a0 100644 --- a/sound/soc/fsl/fsl_easrc.c +++ b/sound/soc/fsl/fsl_easrc.c @@ -1025,7 +1025,6 @@ static int fsl_easrc_config_context(struct fsl_asrc *easrc, unsigned int ctx_id) struct fsl_easrc_ctx_priv *ctx_priv; struct fsl_asrc_pair *ctx; struct device *dev; - unsigned long lock_flags; int ret; if (!easrc) @@ -1053,9 +1052,8 @@ static int fsl_easrc_config_context(struct fsl_asrc *easrc, unsigned int ctx_id) if (ret) return ret; - spin_lock_irqsave(&easrc->lock, lock_flags); - ret = fsl_easrc_config_slot(easrc, ctx->index); - spin_unlock_irqrestore(&easrc->lock, lock_flags); + scoped_guard(spinlock_irqsave, &easrc->lock) + ret = fsl_easrc_config_slot(easrc, ctx->index); if (ret) return ret; @@ -1301,13 +1299,12 @@ static int fsl_easrc_request_context(int channels, struct fsl_asrc_pair *ctx) enum asrc_pair_index index = ASRC_INVALID_PAIR; struct fsl_asrc *easrc = ctx->asrc; struct device *dev; - unsigned long lock_flags; int ret = 0; int i; dev = &easrc->pdev->dev; - spin_lock_irqsave(&easrc->lock, lock_flags); + guard(spinlock_irqsave)(&easrc->lock); for (i = ASRC_PAIR_A; i < EASRC_CTX_MAX_NUM; i++) { if (easrc->pair[i]) @@ -1331,8 +1328,6 @@ static int fsl_easrc_request_context(int channels, struct fsl_asrc_pair *ctx) easrc->channel_avail -= channels; } - spin_unlock_irqrestore(&easrc->lock, lock_flags); - return ret; } @@ -1343,7 +1338,6 @@ static int fsl_easrc_request_context(int channels, struct fsl_asrc_pair *ctx) */ static void fsl_easrc_release_context(struct fsl_asrc_pair *ctx) { - unsigned long lock_flags; struct fsl_asrc *easrc; if (!ctx) @@ -1351,14 +1345,12 @@ static void fsl_easrc_release_context(struct fsl_asrc_pair *ctx) easrc = ctx->asrc; - spin_lock_irqsave(&easrc->lock, lock_flags); + guard(spinlock_irqsave)(&easrc->lock); fsl_easrc_release_slot(easrc, ctx->index); easrc->channel_avail += ctx->channels; easrc->pair[ctx->index] = NULL; - - spin_unlock_irqrestore(&easrc->lock, lock_flags); } /* @@ -2292,15 +2284,13 @@ static int fsl_easrc_runtime_suspend(struct device *dev) { struct fsl_asrc *easrc = dev_get_drvdata(dev); struct fsl_easrc_priv *easrc_priv = easrc->private; - unsigned long lock_flags; regcache_cache_only(easrc->regmap, true); clk_disable_unprepare(easrc->mem_clk); - spin_lock_irqsave(&easrc->lock, lock_flags); - easrc_priv->firmware_loaded = 0; - spin_unlock_irqrestore(&easrc->lock, lock_flags); + scoped_guard(spinlock_irqsave, &easrc->lock) + easrc_priv->firmware_loaded = 0; return 0; } @@ -2311,7 +2301,6 @@ static int fsl_easrc_runtime_resume(struct device *dev) struct fsl_easrc_priv *easrc_priv = easrc->private; struct fsl_easrc_ctx_priv *ctx_priv; struct fsl_asrc_pair *ctx; - unsigned long lock_flags; int ret; int i; @@ -2323,13 +2312,11 @@ static int fsl_easrc_runtime_resume(struct device *dev) regcache_mark_dirty(easrc->regmap); regcache_sync(easrc->regmap); - spin_lock_irqsave(&easrc->lock, lock_flags); - if (easrc_priv->firmware_loaded) { - spin_unlock_irqrestore(&easrc->lock, lock_flags); - goto skip_load; + scoped_guard(spinlock_irqsave, &easrc->lock) { + if (easrc_priv->firmware_loaded) + return 0; + easrc_priv->firmware_loaded = 1; } - easrc_priv->firmware_loaded = 1; - spin_unlock_irqrestore(&easrc->lock, lock_flags); ret = fsl_easrc_get_firmware(easrc); if (ret) { @@ -2377,9 +2364,6 @@ static int fsl_easrc_runtime_resume(struct device *dev) goto disable_mem_clk; } -skip_load: - return 0; - disable_mem_clk: clk_disable_unprepare(easrc->mem_clk); return ret; -- cgit v1.2.3 From cb87bdabd341e399a6fb18e5d9a7c0c669191719 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 15 Jun 2026 16:38:17 +0700 Subject: ASoC: fsl_esai: Use guard() for spin locks Clean up the code using guard() for spin locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Frank Li Link: https://patch.msgid.link/20260615093824.115751-5-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_esai.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index cde0b0c6c1ef..4a530a6c33f0 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -709,10 +709,9 @@ static void fsl_esai_hw_reset(struct work_struct *work) { struct fsl_esai *esai_priv = container_of(work, struct fsl_esai, work); bool tx = true, rx = false, enabled[2]; - unsigned long lock_flags; u32 tfcr, rfcr; - spin_lock_irqsave(&esai_priv->lock, lock_flags); + guard(spinlock_irqsave)(&esai_priv->lock); /* Save the registers */ regmap_read(esai_priv->regmap, REG_ESAI_TFCR, &tfcr); regmap_read(esai_priv->regmap, REG_ESAI_RFCR, &rfcr); @@ -750,8 +749,6 @@ static void fsl_esai_hw_reset(struct work_struct *work) fsl_esai_trigger_start(esai_priv, tx); if (enabled[rx]) fsl_esai_trigger_start(esai_priv, rx); - - spin_unlock_irqrestore(&esai_priv->lock, lock_flags); } static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, @@ -759,7 +756,6 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, { struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; - unsigned long lock_flags; esai_priv->channels[tx] = substream->runtime->channels; @@ -767,16 +763,14 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - spin_lock_irqsave(&esai_priv->lock, lock_flags); - fsl_esai_trigger_start(esai_priv, tx); - spin_unlock_irqrestore(&esai_priv->lock, lock_flags); + scoped_guard(spinlock_irqsave, &esai_priv->lock) + fsl_esai_trigger_start(esai_priv, tx); break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - spin_lock_irqsave(&esai_priv->lock, lock_flags); - fsl_esai_trigger_stop(esai_priv, tx); - spin_unlock_irqrestore(&esai_priv->lock, lock_flags); + scoped_guard(spinlock_irqsave, &esai_priv->lock) + fsl_esai_trigger_stop(esai_priv, tx); break; default: return -EINVAL; -- cgit v1.2.3 From a1b865cff274b8dc35f520f63ca4ef711b442869 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 15 Jun 2026 16:38:18 +0700 Subject: ASoC: fsl_spdif: Use guard() for spin locks Clean up the code using guard() for spin locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Frank Li Link: https://patch.msgid.link/20260615093824.115751-6-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_spdif.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 1b9be85b34c2..ad1206ed9882 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -853,17 +853,15 @@ static int fsl_spdif_subcode_get(struct snd_kcontrol *kcontrol, struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; - unsigned long flags; int ret = -EAGAIN; - spin_lock_irqsave(&ctrl->ctl_lock, flags); + guard(spinlock_irqsave)(&ctrl->ctl_lock); if (ctrl->ready_buf) { int idx = (ctrl->ready_buf - 1) * SPDIF_UBITS_SIZE; memcpy(&ucontrol->value.iec958.subcode[0], &ctrl->subcode[idx], SPDIF_UBITS_SIZE); ret = 0; } - spin_unlock_irqrestore(&ctrl->ctl_lock, flags); return ret; } @@ -885,17 +883,15 @@ static int fsl_spdif_qget(struct snd_kcontrol *kcontrol, struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; - unsigned long flags; int ret = -EAGAIN; - spin_lock_irqsave(&ctrl->ctl_lock, flags); + guard(spinlock_irqsave)(&ctrl->ctl_lock); if (ctrl->ready_buf) { int idx = (ctrl->ready_buf - 1) * SPDIF_QSUB_SIZE; memcpy(&ucontrol->value.bytes.data[0], &ctrl->qsub[idx], SPDIF_QSUB_SIZE); ret = 0; } - spin_unlock_irqrestore(&ctrl->ctl_lock, flags); return ret; } -- cgit v1.2.3 From 4d748e7082ec11764863bd184f6d58cd43584f54 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 15 Jun 2026 16:38:19 +0700 Subject: ASoC: fsl_ssi: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Frank Li Link: https://patch.msgid.link/20260615093824.115751-7-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_ssi.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index b2e1da1781ae..dc022976c982 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -1218,13 +1218,13 @@ static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, if (reg > 0x7f) return; - mutex_lock(&fsl_ac97_data->ac97_reg_lock); + guard(mutex)(&fsl_ac97_data->ac97_reg_lock); ret = clk_prepare_enable(fsl_ac97_data->clk); if (ret) { pr_err("ac97 write clk_prepare_enable failed: %d\n", ret); - goto ret_unlock; + return; } lreg = reg << 12; @@ -1238,9 +1238,6 @@ static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, udelay(100); clk_disable_unprepare(fsl_ac97_data->clk); - -ret_unlock: - mutex_unlock(&fsl_ac97_data->ac97_reg_lock); } static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, @@ -1252,12 +1249,12 @@ static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, unsigned int lreg; int ret; - mutex_lock(&fsl_ac97_data->ac97_reg_lock); + guard(mutex)(&fsl_ac97_data->ac97_reg_lock); ret = clk_prepare_enable(fsl_ac97_data->clk); if (ret) { pr_err("ac97 read clk_prepare_enable failed: %d\n", ret); - goto ret_unlock; + return val; } lreg = (reg & 0x7f) << 12; @@ -1272,8 +1269,6 @@ static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, clk_disable_unprepare(fsl_ac97_data->clk); -ret_unlock: - mutex_unlock(&fsl_ac97_data->ac97_reg_lock); return val; } -- cgit v1.2.3 From 94b0cb1dd6d9633d17bcd3242597b0c70e5118a8 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 15 Jun 2026 16:38:20 +0700 Subject: ASoC: fsl_xcvr: Use guard() for spin locks Clean up the code using guard() for spin locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Frank Li Link: https://patch.msgid.link/20260615093824.115751-8-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_xcvr.c | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c index 6677d3bf36ec..41d100500534 100644 --- a/sound/soc/fsl/fsl_xcvr.c +++ b/sound/soc/fsl/fsl_xcvr.c @@ -797,10 +797,9 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, { struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; - unsigned long lock_flags; int ret = 0; - spin_lock_irqsave(&xcvr->lock, lock_flags); + guard(spinlock_irqsave)(&xcvr->lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -812,7 +811,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, FSL_XCVR_EXT_CTRL_DPTH_RESET(tx)); if (ret < 0) { dev_err(dai->dev, "Failed to set DPATH RESET: %d\n", ret); - goto release_lock; + return ret; } if (tx) { @@ -824,7 +823,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, FSL_XCVR_ISR_CMDC_TX_EN); if (ret < 0) { dev_err(dai->dev, "err updating isr %d\n", ret); - goto release_lock; + return ret; } fallthrough; case FSL_XCVR_MODE_SPDIF: @@ -833,7 +832,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX); if (ret < 0) { dev_err(dai->dev, "Failed to start DATA_TX: %d\n", ret); - goto release_lock; + return ret; } break; } @@ -844,14 +843,14 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, FSL_XCVR_EXT_CTRL_DMA_DIS(tx), 0); if (ret < 0) { dev_err(dai->dev, "Failed to enable DMA: %d\n", ret); - goto release_lock; + return ret; } ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0, FSL_XCVR_IRQ_EARC_ALL, FSL_XCVR_IRQ_EARC_ALL); if (ret < 0) { dev_err(dai->dev, "Error while setting IER0: %d\n", ret); - goto release_lock; + return ret; } /* clear DPATH RESET */ @@ -860,7 +859,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, 0); if (ret < 0) { dev_err(dai->dev, "Failed to clear DPATH RESET: %d\n", ret); - goto release_lock; + return ret; } break; @@ -873,14 +872,14 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, FSL_XCVR_EXT_CTRL_DMA_DIS(tx)); if (ret < 0) { dev_err(dai->dev, "Failed to disable DMA: %d\n", ret); - goto release_lock; + return ret; } ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0, FSL_XCVR_IRQ_EARC_ALL, 0); if (ret < 0) { dev_err(dai->dev, "Failed to clear IER0: %d\n", ret); - goto release_lock; + return ret; } if (tx) { @@ -891,7 +890,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX); if (ret < 0) { dev_err(dai->dev, "Failed to stop DATA_TX: %d\n", ret); - goto release_lock; + return ret; } if (xcvr->soc_data->spdif_only) break; @@ -905,7 +904,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, if (ret < 0) { dev_err(dai->dev, "Err updating ISR %d\n", ret); - goto release_lock; + return ret; } break; } @@ -916,8 +915,6 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, break; } -release_lock: - spin_unlock_irqrestore(&xcvr->lock, lock_flags); return ret; } @@ -1448,11 +1445,10 @@ static void reset_rx_work(struct work_struct *work) { struct fsl_xcvr *xcvr = container_of(work, struct fsl_xcvr, work_rst); struct device *dev = &xcvr->pdev->dev; - unsigned long lock_flags; u32 ext_ctrl; dev_dbg(dev, "reset rx path\n"); - spin_lock_irqsave(&xcvr->lock, lock_flags); + guard(spinlock_irqsave)(&xcvr->lock); regmap_read(xcvr->regmap, FSL_XCVR_EXT_CTRL, &ext_ctrl); if (!(ext_ctrl & FSL_XCVR_EXT_CTRL_DMA_RD_DIS)) { @@ -1469,7 +1465,6 @@ static void reset_rx_work(struct work_struct *work) FSL_XCVR_EXT_CTRL_RX_DPTH_RESET, 0); } - spin_unlock_irqrestore(&xcvr->lock, lock_flags); } static irqreturn_t irq0_isr(int irq, void *devid) -- cgit v1.2.3 From 929d412fdb0d9cb0bd7bae1b1b64cfaf3cc24524 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 15 Jun 2026 16:38:21 +0700 Subject: ASoC: imx-audio-rpmsg: Use guard() for spin locks Clean up the code using guard() for spin locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Frank Li Link: https://patch.msgid.link/20260615093824.115751-9-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/imx-audio-rpmsg.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/sound/soc/fsl/imx-audio-rpmsg.c b/sound/soc/fsl/imx-audio-rpmsg.c index 38aafb8954c7..b55dfbdb4502 100644 --- a/sound/soc/fsl/imx-audio-rpmsg.c +++ b/sound/soc/fsl/imx-audio-rpmsg.c @@ -22,7 +22,6 @@ static int imx_audio_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len, struct rpmsg_r_msg *r_msg = (struct rpmsg_r_msg *)data; struct rpmsg_info *info; struct rpmsg_msg *msg; - unsigned long flags; if (!rpmsg->rpmsg_pdev) return 0; @@ -37,21 +36,21 @@ static int imx_audio_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len, /* TYPE C is notification from M core */ switch (r_msg->header.cmd) { case TX_PERIOD_DONE: - spin_lock_irqsave(&info->lock[TX], flags); - msg = &info->msg[TX_PERIOD_DONE + MSG_TYPE_A_NUM]; - msg->r_msg.param.buffer_tail = - r_msg->param.buffer_tail; - msg->r_msg.param.buffer_tail %= info->num_period[TX]; - spin_unlock_irqrestore(&info->lock[TX], flags); + scoped_guard(spinlock_irqsave, &info->lock[TX]) { + msg = &info->msg[TX_PERIOD_DONE + MSG_TYPE_A_NUM]; + msg->r_msg.param.buffer_tail = + r_msg->param.buffer_tail; + msg->r_msg.param.buffer_tail %= info->num_period[TX]; + } info->callback[TX](info->callback_param[TX]); break; case RX_PERIOD_DONE: - spin_lock_irqsave(&info->lock[RX], flags); - msg = &info->msg[RX_PERIOD_DONE + MSG_TYPE_A_NUM]; - msg->r_msg.param.buffer_tail = - r_msg->param.buffer_tail; - msg->r_msg.param.buffer_tail %= info->num_period[1]; - spin_unlock_irqrestore(&info->lock[RX], flags); + scoped_guard(spinlock_irqsave, &info->lock[RX]) { + msg = &info->msg[RX_PERIOD_DONE + MSG_TYPE_A_NUM]; + msg->r_msg.param.buffer_tail = + r_msg->param.buffer_tail; + msg->r_msg.param.buffer_tail %= info->num_period[1]; + } info->callback[RX](info->callback_param[RX]); break; default: -- cgit v1.2.3 From 11ce4dd4e7bef521e82694a895dbd926b25084da Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 15 Jun 2026 16:38:22 +0700 Subject: ASoC: fsl_rpmsg: Use guard() for mutex & spin locks Clean up the code using guard() for mutex & spin locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Frank Li Link: https://patch.msgid.link/20260615093824.115751-10-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/imx-pcm-rpmsg.c | 69 +++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 39 deletions(-) diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c index 2a4813c6cda9..ee741f3d79bd 100644 --- a/sound/soc/fsl/imx-pcm-rpmsg.c +++ b/sound/soc/fsl/imx-pcm-rpmsg.c @@ -39,10 +39,9 @@ static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg, struct rpmsg_device *rpdev = info->rpdev; int ret = 0; - mutex_lock(&info->msg_lock); + guard(mutex)(&info->msg_lock); if (!rpdev) { dev_err(info->dev, "rpmsg channel not ready\n"); - mutex_unlock(&info->msg_lock); return -EINVAL; } @@ -55,15 +54,12 @@ static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg, sizeof(struct rpmsg_s_msg)); if (ret) { dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret); - mutex_unlock(&info->msg_lock); return ret; } /* No receive msg for TYPE_C command */ - if (msg->s_msg.header.type == MSG_TYPE_C) { - mutex_unlock(&info->msg_lock); + if (msg->s_msg.header.type == MSG_TYPE_C) return 0; - } /* wait response from rpmsg */ ret = wait_for_completion_timeout(&info->cmd_complete, @@ -71,7 +67,6 @@ static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg, if (!ret) { dev_err(&rpdev->dev, "rpmsg_send cmd %d timeout!\n", msg->s_msg.header.cmd); - mutex_unlock(&info->msg_lock); return -ETIMEDOUT; } @@ -100,8 +95,6 @@ static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg, dev_dbg(&rpdev->dev, "cmd:%d, resp %d\n", msg->s_msg.header.cmd, info->r_msg.param.resp); - mutex_unlock(&info->msg_lock); - return 0; } @@ -109,14 +102,13 @@ static int imx_rpmsg_insert_workqueue(struct snd_pcm_substream *substream, struct rpmsg_msg *msg, struct rpmsg_info *info) { - unsigned long flags; int ret = 0; /* * Queue the work to workqueue. * If the queue is full, drop the message. */ - spin_lock_irqsave(&info->wq_lock, flags); + guard(spinlock_irqsave)(&info->wq_lock); if (info->work_write_index != info->work_read_index) { int index = info->work_write_index; @@ -130,7 +122,6 @@ static int imx_rpmsg_insert_workqueue(struct snd_pcm_substream *substream, info->msg_drop_count[substream->stream]++; ret = -EPIPE; } - spin_unlock_irqrestore(&info->wq_lock, flags); return ret; } @@ -523,7 +514,6 @@ static int imx_rpmsg_pcm_ack(struct snd_soc_component *component, snd_pcm_sframes_t avail; struct timer_list *timer; struct rpmsg_msg *msg; - unsigned long flags; int buffer_tail = 0; int written_num; @@ -553,11 +543,11 @@ static int imx_rpmsg_pcm_ack(struct snd_soc_component *component, msg->s_msg.param.buffer_tail = buffer_tail; /* The notification message is updated to latest */ - spin_lock_irqsave(&info->lock[substream->stream], flags); - memcpy(&info->notify[substream->stream], msg, - sizeof(struct rpmsg_s_msg)); - info->notify_updated[substream->stream] = true; - spin_unlock_irqrestore(&info->lock[substream->stream], flags); + scoped_guard(spinlock_irqsave, &info->lock[substream->stream]) { + memcpy(&info->notify[substream->stream], msg, + sizeof(struct rpmsg_s_msg)); + info->notify_updated[substream->stream] = true; + } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) avail = snd_pcm_playback_hw_avail(runtime); @@ -641,7 +631,7 @@ static void imx_rpmsg_pcm_work(struct work_struct *work) bool is_notification = false; struct rpmsg_info *info; struct rpmsg_msg msg; - unsigned long flags; + bool updated; work_of_rpmsg = container_of(work, struct work_of_rpmsg, work); info = work_of_rpmsg->info; @@ -652,25 +642,26 @@ static void imx_rpmsg_pcm_work(struct work_struct *work) * enough data in M core side, need to let M core know * data is updated immediately. */ - spin_lock_irqsave(&info->lock[TX], flags); - if (info->notify_updated[TX]) { - memcpy(&msg, &info->notify[TX], sizeof(struct rpmsg_s_msg)); - info->notify_updated[TX] = false; - spin_unlock_irqrestore(&info->lock[TX], flags); - info->send_message(&msg, info); - } else { - spin_unlock_irqrestore(&info->lock[TX], flags); + scoped_guard(spinlock_irqsave, &info->lock[TX]) { + updated = info->notify_updated[TX]; + if (updated) { + memcpy(&msg, &info->notify[TX], sizeof(struct rpmsg_s_msg)); + info->notify_updated[TX] = false; + } } - - spin_lock_irqsave(&info->lock[RX], flags); - if (info->notify_updated[RX]) { - memcpy(&msg, &info->notify[RX], sizeof(struct rpmsg_s_msg)); - info->notify_updated[RX] = false; - spin_unlock_irqrestore(&info->lock[RX], flags); + if (updated) info->send_message(&msg, info); - } else { - spin_unlock_irqrestore(&info->lock[RX], flags); + + scoped_guard(spinlock_irqsave, &info->lock[RX]) { + updated = info->notify_updated[RX]; + if (updated) { + memcpy(&msg, &info->notify[RX], sizeof(struct rpmsg_s_msg)); + info->notify_updated[RX] = false; + } } + if (updated) + info->send_message(&msg, info); + /* Skip the notification message for it has been processed above */ if (work_of_rpmsg->msg.s_msg.header.type == MSG_TYPE_C && @@ -682,10 +673,10 @@ static void imx_rpmsg_pcm_work(struct work_struct *work) info->send_message(&work_of_rpmsg->msg, info); /* update read index */ - spin_lock_irqsave(&info->wq_lock, flags); - info->work_read_index++; - info->work_read_index %= WORK_MAX_NUM; - spin_unlock_irqrestore(&info->wq_lock, flags); + scoped_guard(spinlock_irqsave, &info->wq_lock) { + info->work_read_index++; + info->work_read_index %= WORK_MAX_NUM; + } } static int imx_rpmsg_pcm_probe(struct platform_device *pdev) -- cgit v1.2.3 From caed9fb2e428e0866c500b8dddabf4993a3aaa30 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 15 Jun 2026 16:38:23 +0700 Subject: ASoC: fsl: mpc5200_dma: Use guard() for spin locks Clean up the code using guard() for spin locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Frank Li Link: https://patch.msgid.link/20260615093824.115751-11-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/mpc5200_dma.c | 56 ++++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c index 56e2cf2f727b..bfedb2dea0b3 100644 --- a/sound/soc/fsl/mpc5200_dma.c +++ b/sound/soc/fsl/mpc5200_dma.c @@ -77,18 +77,20 @@ static irqreturn_t psc_dma_bcom_irq(int irq, void *_psc_dma_stream) { struct psc_dma_stream *s = _psc_dma_stream; - spin_lock(&s->psc_dma->lock); - /* For each finished period, dequeue the completed period buffer - * and enqueue a new one in it's place. */ - while (bcom_buffer_done(s->bcom_task)) { - bcom_retrieve_buffer(s->bcom_task, NULL, NULL); + scoped_guard(spinlock, &s->psc_dma->lock) { + /* + * For each finished period, dequeue the completed period buffer + * and enqueue a new one in its place + */ + while (bcom_buffer_done(s->bcom_task)) { + bcom_retrieve_buffer(s->bcom_task, NULL, NULL); - s->period_current = (s->period_current+1) % s->runtime->periods; - s->period_count++; + s->period_current = (s->period_current+1) % s->runtime->periods; + s->period_count++; - psc_dma_bcom_enqueue_next_buffer(s); + psc_dma_bcom_enqueue_next_buffer(s); + } } - spin_unlock(&s->psc_dma->lock); /* If the stream is active, then also inform the PCM middle layer * of the period finished event. */ @@ -116,7 +118,6 @@ static int psc_dma_trigger(struct snd_soc_component *component, struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma); struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; u16 imr; - unsigned long flags; int i; switch (cmd) { @@ -135,19 +136,18 @@ static int psc_dma_trigger(struct snd_soc_component *component, /* Fill up the bestcomm bd queue and enable DMA. * This will begin filling the PSC's fifo. */ - spin_lock_irqsave(&psc_dma->lock, flags); - - if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) - bcom_gen_bd_rx_reset(s->bcom_task); - else - bcom_gen_bd_tx_reset(s->bcom_task); + scoped_guard(spinlock_irqsave, &psc_dma->lock) { + if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) + bcom_gen_bd_rx_reset(s->bcom_task); + else + bcom_gen_bd_tx_reset(s->bcom_task); - for (i = 0; i < runtime->periods; i++) - if (!bcom_queue_full(s->bcom_task)) - psc_dma_bcom_enqueue_next_buffer(s); + for (i = 0; i < runtime->periods; i++) + if (!bcom_queue_full(s->bcom_task)) + psc_dma_bcom_enqueue_next_buffer(s); - bcom_enable(s->bcom_task); - spin_unlock_irqrestore(&psc_dma->lock, flags); + bcom_enable(s->bcom_task); + } out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); @@ -158,13 +158,13 @@ static int psc_dma_trigger(struct snd_soc_component *component, substream->pstr->stream, s->period_count); s->active = 0; - spin_lock_irqsave(&psc_dma->lock, flags); - bcom_disable(s->bcom_task); - if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) - bcom_gen_bd_rx_reset(s->bcom_task); - else - bcom_gen_bd_tx_reset(s->bcom_task); - spin_unlock_irqrestore(&psc_dma->lock, flags); + scoped_guard(spinlock_irqsave, &psc_dma->lock) { + bcom_disable(s->bcom_task); + if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) + bcom_gen_bd_rx_reset(s->bcom_task); + else + bcom_gen_bd_tx_reset(s->bcom_task); + } break; -- cgit v1.2.3 From 8a2d7e46317a85327f8a7ea7d33139394b2b9662 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 15 Jun 2026 16:38:24 +0700 Subject: ASoC: fsl: mpc5200_psc_ac97: Use guard() for mutex locks Clean up the code using guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Frank Li Link: https://patch.msgid.link/20260615093824.115751-12-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/mpc5200_psc_ac97.c | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c index 8554fb690772..d4d9f5b6bc07 100644 --- a/sound/soc/fsl/mpc5200_psc_ac97.c +++ b/sound/soc/fsl/mpc5200_psc_ac97.c @@ -31,14 +31,13 @@ static unsigned short psc_ac97_read(struct snd_ac97 *ac97, unsigned short reg) int status; unsigned int val; - mutex_lock(&psc_dma->mutex); + guard(mutex)(&psc_dma->mutex); /* Wait for command send status zero = ready */ status = spin_event_timeout(!(in_be16(&psc_dma->psc_regs->sr_csr.status) & MPC52xx_PSC_SR_CMDSEND), 100, 0); if (status == 0) { pr_err("timeout on ac97 bus (rdy)\n"); - mutex_unlock(&psc_dma->mutex); return -ENODEV; } @@ -54,19 +53,16 @@ static unsigned short psc_ac97_read(struct snd_ac97 *ac97, unsigned short reg) if (status == 0) { pr_err("timeout on ac97 read (val) %x\n", in_be16(&psc_dma->psc_regs->sr_csr.status)); - mutex_unlock(&psc_dma->mutex); return -ENODEV; } /* Get the data */ val = in_be32(&psc_dma->psc_regs->ac97_data); if (((val >> 24) & 0x7f) != reg) { pr_err("reg echo error on ac97 read\n"); - mutex_unlock(&psc_dma->mutex); return -ENODEV; } val = (val >> 8) & 0xffff; - mutex_unlock(&psc_dma->mutex); return (unsigned short) val; } @@ -75,52 +71,46 @@ static void psc_ac97_write(struct snd_ac97 *ac97, { int status; - mutex_lock(&psc_dma->mutex); + guard(mutex)(&psc_dma->mutex); /* Wait for command status zero = ready */ status = spin_event_timeout(!(in_be16(&psc_dma->psc_regs->sr_csr.status) & MPC52xx_PSC_SR_CMDSEND), 100, 0); if (status == 0) { pr_err("timeout on ac97 bus (write)\n"); - goto out; + return; } /* Write data */ out_be32(&psc_dma->psc_regs->ac97_cmd, ((reg & 0x7f) << 24) | (val << 8)); - - out: - mutex_unlock(&psc_dma->mutex); } static void psc_ac97_warm_reset(struct snd_ac97 *ac97) { struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; - mutex_lock(&psc_dma->mutex); + guard(mutex)(&psc_dma->mutex); out_be32(®s->sicr, psc_dma->sicr | MPC52xx_PSC_SICR_AWR); udelay(3); out_be32(®s->sicr, psc_dma->sicr); - - mutex_unlock(&psc_dma->mutex); } static void psc_ac97_cold_reset(struct snd_ac97 *ac97) { struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; - mutex_lock(&psc_dma->mutex); - dev_dbg(psc_dma->dev, "cold reset\n"); + scoped_guard(mutex, &psc_dma->mutex) { + dev_dbg(psc_dma->dev, "cold reset\n"); - mpc5200_psc_ac97_gpio_reset(psc_dma->id); + mpc5200_psc_ac97_gpio_reset(psc_dma->id); - /* Notify the PSC that a reset has occurred */ - out_be32(®s->sicr, psc_dma->sicr | MPC52xx_PSC_SICR_ACRB); + /* Notify the PSC that a reset has occurred */ + out_be32(®s->sicr, psc_dma->sicr | MPC52xx_PSC_SICR_ACRB); - /* Re-enable RX and TX */ - out_8(®s->command, MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE); - - mutex_unlock(&psc_dma->mutex); + /* Re-enable RX and TX */ + out_8(®s->command, MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE); + } usleep_range(1000, 2000); psc_ac97_warm_reset(ac97); -- cgit v1.2.3 From f16513ffa944347fe2680a236810fa2d236bb1cc Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 26 Jun 2026 15:29:00 +0700 Subject: ASoC: Intel: avs: Use guard() for locking Clean up the code using guard() for spin & mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260626082904.32344-2-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/apl.c | 8 +++----- sound/soc/intel/avs/control.c | 7 ++----- sound/soc/intel/avs/core.c | 4 ++-- sound/soc/intel/avs/debug.h | 10 +++------- sound/soc/intel/avs/ipc.c | 11 ++++------- sound/soc/intel/avs/path.c | 30 +++++++++-------------------- sound/soc/intel/avs/utils.c | 45 +++++++++++++++---------------------------- 7 files changed, 39 insertions(+), 76 deletions(-) diff --git a/sound/soc/intel/avs/apl.c b/sound/soc/intel/avs/apl.c index b922eeaba843..cf600e1c986e 100644 --- a/sound/soc/intel/avs/apl.c +++ b/sound/soc/intel/avs/apl.c @@ -6,6 +6,7 @@ // Amadeusz Slawinski // +#include #include #include #include @@ -190,7 +191,7 @@ static bool avs_apl_lp_streaming(struct avs_dev *adev) { struct avs_path *path; - spin_lock(&adev->path_list_lock); + guard(spinlock)(&adev->path_list_lock); /* Any gateway without buffer allocated in LP area disqualifies D0IX. */ list_for_each_entry(path, &adev->path_list, node) { struct avs_path_pipeline *ppl; @@ -210,14 +211,11 @@ static bool avs_apl_lp_streaming(struct avs_dev *adev) if (cfg->copier.dma_type == INVALID_OBJECT_ID) continue; - if (!mod->gtw_attrs.lp_buffer_alloc) { - spin_unlock(&adev->path_list_lock); + if (!mod->gtw_attrs.lp_buffer_alloc) return false; - } } } } - spin_unlock(&adev->path_list_lock); return true; } diff --git a/sound/soc/intel/avs/control.c b/sound/soc/intel/avs/control.c index a8f05de338e0..370069247a7d 100644 --- a/sound/soc/intel/avs/control.c +++ b/sound/soc/intel/avs/control.c @@ -27,7 +27,7 @@ static struct avs_path_module *avs_get_volume_module(struct avs_dev *adev, u32 i struct avs_path_pipeline *ppl; struct avs_path_module *mod; - spin_lock(&adev->path_list_lock); + guard(spinlock)(&adev->path_list_lock); list_for_each_entry(path, &adev->path_list, node) { list_for_each_entry(ppl, &path->ppl_list, node) { list_for_each_entry(mod, &ppl->mod_list, node) { @@ -35,14 +35,11 @@ static struct avs_path_module *avs_get_volume_module(struct avs_dev *adev, u32 i if ((guid_equal(type, &AVS_PEAKVOL_MOD_UUID) || guid_equal(type, &AVS_GAIN_MOD_UUID)) && - mod->template->ctl_id == id) { - spin_unlock(&adev->path_list_lock); + mod->template->ctl_id == id) return mod; - } } } } - spin_unlock(&adev->path_list_lock); return NULL; } diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c index 1a53856c2ffb..2afe59646896 100644 --- a/sound/soc/intel/avs/core.c +++ b/sound/soc/intel/avs/core.c @@ -14,6 +14,7 @@ // foundation of this driver // +#include #include #include #include @@ -273,7 +274,7 @@ static irqreturn_t avs_hda_interrupt(struct hdac_bus *bus) if (snd_hdac_bus_handle_stream_irq(bus, status, hdac_update_stream)) ret = IRQ_HANDLED; - spin_lock_irq(&bus->reg_lock); + guard(spinlock_irq)(&bus->reg_lock); /* Clear RIRB interrupt. */ status = snd_hdac_chip_readb(bus, RIRBSTS); if (status & RIRB_INT_MASK) { @@ -283,7 +284,6 @@ static irqreturn_t avs_hda_interrupt(struct hdac_bus *bus) ret = IRQ_HANDLED; } - spin_unlock_irq(&bus->reg_lock); return ret; } diff --git a/sound/soc/intel/avs/debug.h b/sound/soc/intel/avs/debug.h index 94fe8729a5c1..c47fc4e8b02b 100644 --- a/sound/soc/intel/avs/debug.h +++ b/sound/soc/intel/avs/debug.h @@ -9,6 +9,7 @@ #ifndef __SOUND_SOC_INTEL_AVS_DEBUG_H #define __SOUND_SOC_INTEL_AVS_DEBUG_H +#include #include "messages.h" #include "registers.h" @@ -26,14 +27,9 @@ struct avs_dev; static inline int avs_log_buffer_status_locked(struct avs_dev *adev, union avs_notify_msg *msg) { - unsigned long flags; - int ret; + guard(spinlock_irqsave)(&adev->trace_lock); - spin_lock_irqsave(&adev->trace_lock, flags); - ret = avs_dsp_op(adev, log_buffer_status, msg); - spin_unlock_irqrestore(&adev->trace_lock, flags); - - return ret; + return avs_dsp_op(adev, log_buffer_status, msg); } struct avs_apl_log_buffer_layout { diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c index c0feb9edd7f6..e99cb703eb7e 100644 --- a/sound/soc/intel/avs/ipc.c +++ b/sound/soc/intel/avs/ipc.c @@ -6,6 +6,7 @@ // Amadeusz Slawinski // +#include #include #include #include @@ -397,7 +398,7 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request if (!ipc->ready) return -EPERM; - mutex_lock(&ipc->msg_mutex); + guard(mutex)(&ipc->msg_mutex); spin_lock(&ipc->rx_lock); avs_ipc_msg_init(ipc, reply); @@ -412,7 +413,7 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request /* Same treatment as on exception, just stack_dump=0. */ avs_dsp_exception_caught(adev, &msg); } - goto exit; + return ret; } ret = ipc->rx.rsp.status; @@ -436,8 +437,6 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request memcpy(reply->data, ipc->rx.data, reply->size); } -exit: - mutex_unlock(&ipc->msg_mutex); return ret; } @@ -501,7 +500,7 @@ static int avs_dsp_do_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *req struct avs_ipc *ipc = adev->ipc; int ret; - mutex_lock(&ipc->msg_mutex); + guard(mutex)(&ipc->msg_mutex); spin_lock(&ipc->rx_lock); avs_ipc_msg_init(ipc, NULL); @@ -522,8 +521,6 @@ static int avs_dsp_do_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *req dev_err(adev->dev, "%s (0x%08x 0x%08x) failed: %d\n", name, request->glb.primary, request->glb.ext.val, ret); - mutex_unlock(&ipc->msg_mutex); - return ret; } diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c index 2291f9728a54..213d6ecdd7cc 100644 --- a/sound/soc/intel/avs/path.c +++ b/sound/soc/intel/avs/path.c @@ -6,6 +6,7 @@ // Amadeusz Slawinski // +#include #include #include #include @@ -69,16 +70,13 @@ avs_path_find_path(struct avs_dev *adev, const char *name, u32 template_id) if (!template) return NULL; - spin_lock(&adev->path_list_lock); + guard(spinlock)(&adev->path_list_lock); /* Only one variant of given path template may be instantiated at a time. */ list_for_each_entry(path, &adev->path_list, node) { - if (path->template->owner == template) { - spin_unlock(&adev->path_list_lock); + if (path->template->owner == template) return path; - } } - spin_unlock(&adev->path_list_lock); return NULL; } @@ -1305,7 +1303,7 @@ void avs_path_free(struct avs_path *path) struct avs_path *cpath, *csave; struct avs_dev *adev = path->owner; - mutex_lock(&adev->path_mutex); + guard(mutex)(&adev->path_mutex); /* Free all condpaths this path spawned. */ list_for_each_entry_safe(cpath, csave, &path->source_list, source_node) @@ -1314,8 +1312,6 @@ void avs_path_free(struct avs_path *path) avs_condpath_free(path->owner, cpath); avs_path_free_unlocked(path); - - mutex_unlock(&adev->path_mutex); } struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id, @@ -1334,13 +1330,13 @@ struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id, } /* Serialize path and its components creation. */ - mutex_lock(&adev->path_mutex); + guard(mutex)(&adev->path_mutex); /* Satisfy needs of avs_path_find_tplg(). */ - mutex_lock(&adev->comp_list_mutex); + guard(mutex)(&adev->comp_list_mutex); path = avs_path_create_unlocked(adev, dma_id, variant); if (IS_ERR(path)) - goto exit; + return path; ret = avs_condpaths_walk_all(adev, path); if (ret) { @@ -1348,10 +1344,6 @@ struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id, path = ERR_PTR(ret); } -exit: - mutex_unlock(&adev->comp_list_mutex); - mutex_unlock(&adev->path_mutex); - return path; } @@ -1496,15 +1488,13 @@ static void avs_condpaths_pause(struct avs_dev *adev, struct avs_path *path) { struct avs_path *cpath; - mutex_lock(&adev->path_mutex); + guard(mutex)(&adev->path_mutex); /* If either source or sink stops, so do the attached conditional paths. */ list_for_each_entry(cpath, &path->source_list, source_node) avs_condpath_pause(adev, cpath); list_for_each_entry(cpath, &path->sink_list, sink_node) avs_condpath_pause(adev, cpath); - - mutex_unlock(&adev->path_mutex); } int avs_path_pause(struct avs_path *path) @@ -1560,7 +1550,7 @@ static void avs_condpaths_run(struct avs_dev *adev, struct avs_path *path, int t { struct avs_path *cpath; - mutex_lock(&adev->path_mutex); + guard(mutex)(&adev->path_mutex); /* Run conditional paths only if source and sink are both running. */ list_for_each_entry(cpath, &path->source_list, source_node) @@ -1572,8 +1562,6 @@ static void avs_condpaths_run(struct avs_dev *adev, struct avs_path *path, int t if (cpath->source->state == AVS_PPL_STATE_RUNNING && cpath->sink->state == AVS_PPL_STATE_RUNNING) avs_condpath_run(adev, cpath, trigger); - - mutex_unlock(&adev->path_mutex); } int avs_path_run(struct avs_path *path, int trigger) diff --git a/sound/soc/intel/avs/utils.c b/sound/soc/intel/avs/utils.c index ee36725ac731..ea14ec173855 100644 --- a/sound/soc/intel/avs/utils.c +++ b/sound/soc/intel/avs/utils.c @@ -6,6 +6,7 @@ // Amadeusz Slawinski // +#include #include #include #include @@ -48,13 +49,12 @@ int avs_get_module_entry(struct avs_dev *adev, const guid_t *uuid, struct avs_mo { int idx; - mutex_lock(&adev->modres_mutex); + guard(mutex)(&adev->modres_mutex); idx = avs_module_entry_index(adev, uuid); if (idx >= 0) memcpy(entry, &adev->mods_info->entries[idx], sizeof(*entry)); - mutex_unlock(&adev->modres_mutex); return (idx < 0) ? idx : 0; } @@ -62,13 +62,12 @@ int avs_get_module_id_entry(struct avs_dev *adev, u32 module_id, struct avs_modu { int idx; - mutex_lock(&adev->modres_mutex); + guard(mutex)(&adev->modres_mutex); idx = avs_module_id_entry_index(adev, module_id); if (idx >= 0) memcpy(entry, &adev->mods_info->entries[idx], sizeof(*entry)); - mutex_unlock(&adev->modres_mutex); return (idx < 0) ? idx : 0; } @@ -86,13 +85,12 @@ bool avs_is_module_ida_empty(struct avs_dev *adev, u32 module_id) bool ret = false; int idx; - mutex_lock(&adev->modres_mutex); + guard(mutex)(&adev->modres_mutex); idx = avs_module_id_entry_index(adev, module_id); if (idx >= 0) ret = ida_is_empty(adev->mod_idas[idx]); - mutex_unlock(&adev->modres_mutex); return ret; } @@ -163,68 +161,57 @@ int avs_module_info_init(struct avs_dev *adev, bool purge) if (ret) return AVS_IPC_RET(ret); - mutex_lock(&adev->modres_mutex); + guard(mutex)(&adev->modres_mutex); ret = avs_module_ida_alloc(adev, info, purge); if (ret < 0) { dev_err(adev->dev, "initialize module idas failed: %d\n", ret); - goto exit; + return ret; } /* Refresh current information with newly received table. */ kfree(adev->mods_info); adev->mods_info = info; -exit: - mutex_unlock(&adev->modres_mutex); return ret; } void avs_module_info_free(struct avs_dev *adev) { - mutex_lock(&adev->modres_mutex); + guard(mutex)(&adev->modres_mutex); avs_module_ida_destroy(adev); kfree(adev->mods_info); adev->mods_info = NULL; - - mutex_unlock(&adev->modres_mutex); } int avs_module_id_alloc(struct avs_dev *adev, u16 module_id) { - int ret, idx, max_id; + int idx, max_id; - mutex_lock(&adev->modres_mutex); + guard(mutex)(&adev->modres_mutex); idx = avs_module_id_entry_index(adev, module_id); if (idx == -ENOENT) { dev_err(adev->dev, "invalid module id: %d", module_id); - ret = -EINVAL; - goto exit; + return -EINVAL; } max_id = adev->mods_info->entries[idx].instance_max_count - 1; - ret = ida_alloc_max(adev->mod_idas[idx], max_id, GFP_KERNEL); -exit: - mutex_unlock(&adev->modres_mutex); - return ret; + + return ida_alloc_max(adev->mod_idas[idx], max_id, GFP_KERNEL); } void avs_module_id_free(struct avs_dev *adev, u16 module_id, u8 instance_id) { int idx; - mutex_lock(&adev->modres_mutex); + guard(mutex)(&adev->modres_mutex); idx = avs_module_id_entry_index(adev, module_id); - if (idx == -ENOENT) { + if (idx == -ENOENT) dev_err(adev->dev, "invalid module id: %d", module_id); - goto exit; - } - - ida_free(adev->mod_idas[idx], instance_id); -exit: - mutex_unlock(&adev->modres_mutex); + else + ida_free(adev->mod_idas[idx], instance_id); } /* -- cgit v1.2.3 From 6aaac9f6baa55cbc4c5cd6071dba11928dc15302 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 26 Jun 2026 15:29:01 +0700 Subject: ASoC: Intel: avs: Use scoped_guard() for scoped locking Clean up the code using scoped_guard() for mutex & spin locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260626082904.32344-3-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/debugfs.c | 19 ++++++++-------- sound/soc/intel/avs/ipc.c | 50 +++++++++++++++++++++---------------------- sound/soc/intel/avs/loader.c | 15 +++++++------ 3 files changed, 42 insertions(+), 42 deletions(-) diff --git a/sound/soc/intel/avs/debugfs.c b/sound/soc/intel/avs/debugfs.c index 701c247227bf..9ab503da3b75 100644 --- a/sound/soc/intel/avs/debugfs.c +++ b/sound/soc/intel/avs/debugfs.c @@ -6,6 +6,7 @@ // Amadeusz Slawinski // +#include #include #include #include @@ -251,24 +252,22 @@ static int strace_release(struct inode *inode, struct file *file) union avs_notify_msg msg = AVS_NOTIFICATION(LOG_BUFFER_STATUS); struct avs_dev *adev = file->private_data; unsigned long resource_mask; - unsigned long flags, i; + unsigned long i; u32 num_cores; resource_mask = adev->logged_resources; num_cores = adev->hw_cfg.dsp_cores; - spin_lock_irqsave(&adev->trace_lock, flags); + scoped_guard(spinlock_irqsave, &adev->trace_lock) { + /* Gather any remaining logs. */ + for_each_set_bit(i, &resource_mask, num_cores) { + msg.log.core = i; + avs_dsp_op(adev, log_buffer_status, &msg); + } - /* Gather any remaining logs. */ - for_each_set_bit(i, &resource_mask, num_cores) { - msg.log.core = i; - avs_dsp_op(adev, log_buffer_status, &msg); + kfifo_free(&adev->trace_fifo); } - kfifo_free(&adev->trace_fifo); - - spin_unlock_irqrestore(&adev->trace_lock, flags); - module_put(adev->dev->driver->owner); return 0; } diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c index e99cb703eb7e..71e7997e52c2 100644 --- a/sound/soc/intel/avs/ipc.c +++ b/sound/soc/intel/avs/ipc.c @@ -100,39 +100,39 @@ static void avs_dsp_recovery(struct avs_dev *adev) unsigned int core_mask; int ret; - mutex_lock(&adev->comp_list_mutex); - /* disconnect all running streams */ - list_for_each_entry(acomp, &adev->comp_list, node) { - struct snd_soc_pcm_runtime *rtd; - struct snd_soc_card *card; - - card = acomp->base.card; - if (!card) - continue; - - for_each_card_rtds(card, rtd) { - struct snd_pcm *pcm; - int dir; - - pcm = rtd->pcm; - if (!pcm || rtd->dai_link->no_pcm) + scoped_guard(mutex, &adev->comp_list_mutex) { + /* disconnect all running streams */ + list_for_each_entry(acomp, &adev->comp_list, node) { + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_card *card; + + card = acomp->base.card; + if (!card) continue; - for_each_pcm_streams(dir) { - struct snd_pcm_substream *substream; + for_each_card_rtds(card, rtd) { + struct snd_pcm *pcm; + int dir; - substream = pcm->streams[dir].substream; - if (!substream || !substream->runtime) + pcm = rtd->pcm; + if (!pcm || rtd->dai_link->no_pcm) continue; - /* No need for _irq() as we are in nonatomic context. */ - snd_pcm_stream_lock(substream); - snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); - snd_pcm_stream_unlock(substream); + for_each_pcm_streams(dir) { + struct snd_pcm_substream *substream; + + substream = pcm->streams[dir].substream; + if (!substream || !substream->runtime) + continue; + + /* No need for _irq() as we are in nonatomic context. */ + snd_pcm_stream_lock(substream); + snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); + snd_pcm_stream_unlock(substream); + } } } } - mutex_unlock(&adev->comp_list_mutex); /* forcibly shutdown all cores */ core_mask = GENMASK(adev->hw_cfg.dsp_cores - 1, 0); diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c index 353e343b1d28..bebdc79ec88e 100644 --- a/sound/soc/intel/avs/loader.c +++ b/sound/soc/intel/avs/loader.c @@ -6,6 +6,7 @@ // Amadeusz Slawinski // +#include #include #include #include @@ -630,15 +631,15 @@ static int avs_load_firmware(struct avs_dev *adev, bool purge) if (ret) goto reenable_gating; - mutex_lock(&adev->comp_list_mutex); - list_for_each_entry(acomp, &adev->comp_list, node) { - struct avs_tplg *tplg = acomp->tplg; + scoped_guard(mutex, &adev->comp_list_mutex) { + list_for_each_entry(acomp, &adev->comp_list, node) { + struct avs_tplg *tplg = acomp->tplg; - ret = avs_dsp_load_libraries(adev, tplg->libs, tplg->num_libs); - if (ret < 0) - break; + ret = avs_dsp_load_libraries(adev, tplg->libs, tplg->num_libs); + if (ret < 0) + break; + } } - mutex_unlock(&adev->comp_list_mutex); reenable_gating: avs_hda_l1sen_enable(adev, true); -- cgit v1.2.3 From 4889a8a73f65b8e4feb49ea434b8a41806513574 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 26 Jun 2026 15:29:02 +0700 Subject: ASoC: intel: atom: Use __free(kfree) for stream pointer Declare 'stream' with __free(kfree) so it is automatically freed when leaving scope. This allows direct returns from error paths and removes the explicit kfree(stream) call. Set 'stream' to NULL after ownership has been transferred to runtime->private_data to prevent it from being freed on the success path. This cleanup is a preparation step for upcoming locking changes. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260626082904.32344-4-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst-mfld-platform-pcm.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index f074af2499c8..67506a718158 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -11,6 +11,7 @@ */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include @@ -304,7 +305,7 @@ static int sst_media_open(struct snd_pcm_substream *substream, { int ret_val = 0; struct snd_pcm_runtime *runtime = substream->runtime; - struct sst_runtime_stream *stream; + struct sst_runtime_stream *stream __free(kfree) = NULL; stream = kzalloc_obj(*stream); if (!stream) @@ -330,7 +331,7 @@ static int sst_media_open(struct snd_pcm_substream *substream, ret_val = power_up_sst(stream); if (ret_val < 0) - goto out_power_up; + return ret_val; /* * Make sure the period to be multiple of 1ms to align the @@ -347,12 +348,19 @@ static int sst_media_open(struct snd_pcm_substream *substream, snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS, 2); - return snd_pcm_hw_constraint_integer(runtime, - SNDRV_PCM_HW_PARAM_PERIODS); + ret_val = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + + if (ret_val < 0) + return ret_val; + + stream = NULL; + + return ret_val; + out_ops: mutex_unlock(&sst_lock); -out_power_up: - kfree(stream); + return ret_val; } -- cgit v1.2.3 From 71c0f725d35de81b06076a4d1448ea4733993a4b Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 26 Jun 2026 15:29:03 +0700 Subject: ASoC: Intel: atom: Use guard() for locking Clean up the code using guard() for spin & mutex locks. Merely code refactoring, and no behavior change. Reviewed-by: Cezary Rojewski Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260626082904.32344-5-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst-atom-controls.c | 29 +++++++++------------------- sound/soc/intel/atom/sst-mfld-platform-pcm.c | 25 ++++++++---------------- sound/soc/intel/atom/sst/sst_ipc.c | 5 ++--- sound/soc/intel/atom/sst/sst_pvt.c | 9 ++++----- 4 files changed, 23 insertions(+), 45 deletions(-) diff --git a/sound/soc/intel/atom/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c index 3629ceaaac17..701369349fb6 100644 --- a/sound/soc/intel/atom/sst-atom-controls.c +++ b/sound/soc/intel/atom/sst-atom-controls.c @@ -14,6 +14,7 @@ */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include @@ -73,14 +74,10 @@ static int sst_fill_and_send_cmd(struct sst_data *drv, u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id, void *cmd_data, u16 len) { - int ret; + guard(mutex)(&drv->lock); - mutex_lock(&drv->lock); - ret = sst_fill_and_send_cmd_unlocked(drv, ipc_msg, block, - task_id, pipe_id, cmd_data, len); - mutex_unlock(&drv->lock); - - return ret; + return sst_fill_and_send_cmd_unlocked(drv, ipc_msg, block, + task_id, pipe_id, cmd_data, len); } /* @@ -167,7 +164,7 @@ static int sst_slot_get(struct snd_kcontrol *kcontrol, unsigned int val, mux; u8 *map = is_tx ? sst_ssp_rx_map : sst_ssp_tx_map; - mutex_lock(&drv->lock); + guard(mutex)(&drv->lock); val = 1 << ctl_no; /* search which slot/channel has this bit set - there should be only one */ for (mux = e->max; mux > 0; mux--) @@ -175,7 +172,6 @@ static int sst_slot_get(struct snd_kcontrol *kcontrol, break; ucontrol->value.enumerated.item[0] = mux; - mutex_unlock(&drv->lock); dev_dbg(c->dev, "%s - %s map = %#x\n", is_tx ? "tx channel" : "rx slot", @@ -235,7 +231,7 @@ static int sst_slot_put(struct snd_kcontrol *kcontrol, if (mux > e->max - 1) return -EINVAL; - mutex_lock(&drv->lock); + guard(mutex)(&drv->lock); /* first clear all registers of this bit */ for (i = 0; i < e->max; i++) map[i] &= ~val; @@ -244,7 +240,6 @@ static int sst_slot_put(struct snd_kcontrol *kcontrol, /* kctl set to 'none' and we reset the bits so send IPC */ ret = sst_check_and_send_slot_map(drv, kcontrol); - mutex_unlock(&drv->lock); return ret; } @@ -258,7 +253,6 @@ static int sst_slot_put(struct snd_kcontrol *kcontrol, ret = sst_check_and_send_slot_map(drv, kcontrol); - mutex_unlock(&drv->lock); return ret; } @@ -354,13 +348,12 @@ static int sst_algo_control_set(struct snd_kcontrol *kcontrol, struct sst_algo_control *bc = (void *)kcontrol->private_value; dev_dbg(cmpnt->dev, "control_name=%s\n", kcontrol->id.name); - mutex_lock(&drv->lock); + guard(mutex)(&drv->lock); switch (bc->type) { case SST_ALGO_PARAMS: memcpy(bc->params, ucontrol->value.bytes.data, bc->max); break; default: - mutex_unlock(&drv->lock); dev_err(cmpnt->dev, "Invalid Input- algo type:%d\n", bc->type); return -EINVAL; @@ -368,7 +361,6 @@ static int sst_algo_control_set(struct snd_kcontrol *kcontrol, /*if pipe is enabled, need to send the algo params from here*/ if (bc->w && bc->w->power) ret = sst_send_algo_cmd(drv, bc); - mutex_unlock(&drv->lock); return ret; } @@ -475,7 +467,7 @@ static int sst_gain_put(struct snd_kcontrol *kcontrol, struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value; struct sst_gain_value *gv = mc->gain_val; - mutex_lock(&drv->lock); + guard(mutex)(&drv->lock); switch (mc->type) { case SST_GAIN_TLV: @@ -497,7 +489,6 @@ static int sst_gain_put(struct snd_kcontrol *kcontrol, break; default: - mutex_unlock(&drv->lock); dev_err(cmpnt->dev, "Invalid Input- gain type:%d\n", mc->type); return -EINVAL; @@ -506,7 +497,6 @@ static int sst_gain_put(struct snd_kcontrol *kcontrol, if (mc->w && mc->w->power) ret = sst_send_gain_cmd(drv, gv, mc->task_id, mc->pipe_id | mc->instance_id, mc->module_id, 0); - mutex_unlock(&drv->lock); return ret; } @@ -521,10 +511,9 @@ static int sst_send_pipe_module_params(struct snd_soc_dapm_widget *w, struct sst_data *drv = snd_soc_component_get_drvdata(c); struct sst_ids *ids = w->priv; - mutex_lock(&drv->lock); + guard(mutex)(&drv->lock); sst_find_and_send_pipe_algo(drv, w->name, ids); sst_set_pipe_gain(ids, drv, 0); - mutex_unlock(&drv->lock); return 0; } diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index 67506a718158..c757e4dcd7cf 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -33,7 +33,7 @@ int sst_register_dsp(struct sst_device *dev) return -EINVAL; if (!try_module_get(dev->dev->driver->owner)) return -ENODEV; - mutex_lock(&sst_lock); + guard(mutex)(&sst_lock); if (sst) { dev_err(dev->dev, "we already have a device %s\n", sst->name); module_put(dev->dev->driver->owner); @@ -42,7 +42,7 @@ int sst_register_dsp(struct sst_device *dev) } dev_dbg(dev->dev, "registering device %s\n", dev->name); sst = dev; - mutex_unlock(&sst_lock); + return 0; } EXPORT_SYMBOL_GPL(sst_register_dsp); @@ -54,17 +54,15 @@ int sst_unregister_dsp(struct sst_device *dev) if (dev != sst) return -EINVAL; - mutex_lock(&sst_lock); + guard(mutex)(&sst_lock); - if (!sst) { - mutex_unlock(&sst_lock); + if (!sst) return -EIO; - } module_put(sst->dev->driver->owner); dev_dbg(dev->dev, "unreg %s\n", sst->name); sst = NULL; - mutex_unlock(&sst_lock); + return 0; } EXPORT_SYMBOL_GPL(sst_unregister_dsp); @@ -104,21 +102,14 @@ static int sst_media_digital_mute(struct snd_soc_dai *dai, int mute, int stream) void sst_set_stream_status(struct sst_runtime_stream *stream, int state) { - unsigned long flags; - spin_lock_irqsave(&stream->status_lock, flags); + guard(spinlock_irqsave)(&stream->status_lock); stream->stream_status = state; - spin_unlock_irqrestore(&stream->status_lock, flags); } static inline int sst_get_stream_status(struct sst_runtime_stream *stream) { - int state; - unsigned long flags; - - spin_lock_irqsave(&stream->status_lock, flags); - state = stream->stream_status; - spin_unlock_irqrestore(&stream->status_lock, flags); - return state; + guard(spinlock_irqsave)(&stream->status_lock); + return stream->stream_status; } static void sst_fill_alloc_params(struct snd_pcm_substream *substream, diff --git a/sound/soc/intel/atom/sst/sst_ipc.c b/sound/soc/intel/atom/sst/sst_ipc.c index 0d5e71e8a5b5..6c19ab63aa4f 100644 --- a/sound/soc/intel/atom/sst/sst_ipc.c +++ b/sound/soc/intel/atom/sst/sst_ipc.c @@ -11,6 +11,7 @@ * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +#include #include #include #include @@ -180,9 +181,8 @@ void intel_sst_clear_intr_mrfld(struct intel_sst_drv *sst_drv_ctx) union interrupt_reg_mrfld isr; union interrupt_reg_mrfld imr; union ipc_header_mrfld clear_ipc; - unsigned long irq_flags; - spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags); + guard(spinlock_irqsave)(&sst_drv_ctx->ipc_spin_lock); imr.full = sst_shim_read64(sst_drv_ctx->shim, SST_IMRX); isr.full = sst_shim_read64(sst_drv_ctx->shim, SST_ISRX); @@ -200,7 +200,6 @@ void intel_sst_clear_intr_mrfld(struct intel_sst_drv *sst_drv_ctx) /* un mask busy interrupt */ imr.part.busy_interrupt = 0; sst_shim_write64(sst_drv_ctx->shim, SST_IMRX, imr.full); - spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); } diff --git a/sound/soc/intel/atom/sst/sst_pvt.c b/sound/soc/intel/atom/sst/sst_pvt.c index 67b1ab14239f..0b0cfd70efbc 100644 --- a/sound/soc/intel/atom/sst/sst_pvt.c +++ b/sound/soc/intel/atom/sst/sst_pvt.c @@ -11,6 +11,7 @@ * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +#include #include #include #include @@ -64,9 +65,8 @@ u64 sst_shim_read64(void __iomem *addr, int offset) void sst_set_fw_state_locked( struct intel_sst_drv *sst_drv_ctx, int sst_state) { - mutex_lock(&sst_drv_ctx->sst_lock); + guard(mutex)(&sst_drv_ctx->sst_lock); sst_drv_ctx->sst_state = sst_state; - mutex_unlock(&sst_drv_ctx->sst_lock); } /* @@ -302,18 +302,17 @@ int sst_assign_pvt_id(struct intel_sst_drv *drv) { int local; - spin_lock(&drv->block_lock); + guard(spinlock)(&drv->block_lock); /* find first zero index from lsb */ local = ffz(drv->pvt_id); dev_dbg(drv->dev, "pvt_id assigned --> %d\n", local); if (local >= SST_MAX_BLOCKS){ - spin_unlock(&drv->block_lock); dev_err(drv->dev, "PVT _ID error: no free id blocks "); return -EINVAL; } /* toggle the index */ change_bit(local, &drv->pvt_id); - spin_unlock(&drv->block_lock); + return local; } -- cgit v1.2.3 From 08d2d5e683e85a7dc33c1abfd50fdc380bfafb5a Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Fri, 26 Jun 2026 15:29:04 +0700 Subject: ASoC: Intel: atom: Use scoped_guard() for scoped locking Clean up the code using scoped_guard() for mutex locks. Merely code refactoring, and no behavior change. Signed-off-by: bui duc phuc Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260626082904.32344-6-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst-atom-controls.c | 38 +++++++++++++++------------- sound/soc/intel/atom/sst-mfld-platform-pcm.c | 20 +++++---------- 2 files changed, 27 insertions(+), 31 deletions(-) diff --git a/sound/soc/intel/atom/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c index 701369349fb6..82df398237da 100644 --- a/sound/soc/intel/atom/sst-atom-controls.c +++ b/sound/soc/intel/atom/sst-atom-controls.c @@ -750,27 +750,29 @@ int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable) return ret; } - mutex_lock(&drv->lock); - if (enable) - timer_usage++; - else - timer_usage--; - - /* - * Send the command only if this call is the first enable or last - * disable - */ - if ((enable && (timer_usage == 1)) || - (!enable && (timer_usage == 0))) { - ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_CMD, - SST_FLAG_BLOCKED, SST_TASK_SBA, 0, &cmd, - sizeof(cmd.header) + cmd.header.length); - if (ret && enable) { + scoped_guard(mutex, &drv->lock) { + if (enable) + timer_usage++; + else timer_usage--; - enable = false; + + /* + * Send the command only if this call is the first enable or last + * disable + */ + if ((enable && timer_usage == 1) || + (!enable && timer_usage == 0)) { + ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_CMD, + SST_FLAG_BLOCKED, + SST_TASK_SBA, 0, &cmd, + sizeof(cmd.header) + + cmd.header.length); + if (ret && enable) { + timer_usage--; + enable = false; + } } } - mutex_unlock(&drv->lock); if (!enable) sst->ops->power(sst->dev, false); diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index c757e4dcd7cf..9ee4d9926e06 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -304,15 +304,14 @@ static int sst_media_open(struct snd_pcm_substream *substream, spin_lock_init(&stream->status_lock); /* get the sst ops */ - mutex_lock(&sst_lock); - if (!sst || - !try_module_get(sst->dev->driver->owner)) { - dev_err(dai->dev, "no device available to run\n"); - ret_val = -ENODEV; - goto out_ops; + scoped_guard(mutex, &sst_lock) { + if (!sst || + !try_module_get(sst->dev->driver->owner)) { + dev_err(dai->dev, "no device available to run\n"); + return -ENODEV; + } + stream->ops = sst->ops; } - stream->ops = sst->ops; - mutex_unlock(&sst_lock); stream->stream_info.str_id = 0; @@ -347,11 +346,6 @@ static int sst_media_open(struct snd_pcm_substream *substream, stream = NULL; - return ret_val; - -out_ops: - mutex_unlock(&sst_lock); - return ret_val; } -- cgit v1.2.3 From 84eb7c8e59ccbcd45b2678abac56b778c97ee270 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Mon, 22 Jun 2026 17:11:39 +0800 Subject: ASoC: rt1321: move DSP status checking into a helper function This patch creates the rt1320_dspfw_status helper function to check DSP status. The new function can be reused by other functions and supports checking the DSP status across different amplifiers. Signed-off-by: Shuming Fan Link: https://patch.msgid.link/20260622091139.2183297-1-shumingf@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt1320-sdw.c | 117 ++++++++++++++++++------------------------ 1 file changed, 49 insertions(+), 68 deletions(-) diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index 13493b85f3c9..8d6fe2e36fc7 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -893,6 +893,42 @@ static int rt1320_check_fw_ready(struct rt1320_sdw_priv *rt1320) return 0; } +static int rt1320_dspfw_status(struct rt1320_sdw_priv *rt1320) +{ + struct device *dev = &rt1320->sdw_slave->dev; + unsigned int fw_status_addr, fw_ready; + unsigned int dspfw_run; + + switch (rt1320->dev_id) { + case RT1320_DEV_ID: + fw_status_addr = RT1320_DSPFW_STATUS_ADDR; + break; + case RT1321_DEV_ID: + fw_status_addr = RT1321_DSPFW_STATUS_ADDR; + break; + default: + dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); + return -EINVAL; + } + + regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); + fw_ready &= 0x1; + + if (rt1320->dev_id == RT1321_DEV_ID) { + regmap_read(rt1320->regmap, 0xf01e, &dspfw_run); + dspfw_run &= 0x1; + fw_ready = (!dspfw_run && fw_ready); + } + + if (fw_ready) { + dev_dbg(dev, "%s, DSP FW was already\n", __func__); + return 1; + } + + dev_dbg(dev, "%s, DSP FW is NOT ready. Please load DSP FW first\n", __func__); + return 0; +} + static int rt1320_check_power_state_ready(struct rt1320_sdw_priv *rt1320, enum rt1320_power_state ps) { struct device *dev = &rt1320->sdw_slave->dev; @@ -1126,30 +1162,18 @@ static void rt1320_calibrate(struct rt1320_sdw_priv *rt1320) struct device *dev = &rt1320->sdw_slave->dev; struct rt1320_datafixpoint audfixpoint[2]; unsigned int reg_c5fb, reg_c570, reg_cd00; - unsigned int vol_reg[4], fw_ready; + unsigned int vol_reg[4]; unsigned long long l_meanr0, r_meanr0; - unsigned int fw_status_addr; int l_re[5], r_re[5]; int ret, tmp; unsigned long long factor = (1 << 27); unsigned short l_advancegain, r_advancegain; unsigned int delay_s = 7; /* delay seconds for the calibration */ + int dspfw_status; if (!rt1320->component) return; - switch (rt1320->dev_id) { - case RT1320_DEV_ID: - fw_status_addr = RT1320_DSPFW_STATUS_ADDR; - break; - case RT1321_DEV_ID: - fw_status_addr = RT1321_DSPFW_STATUS_ADDR; - break; - default: - dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); - return; - } - /* set volume 0dB */ regmap_read(rt1320->regmap, 0xdd0b, &vol_reg[3]); regmap_read(rt1320->regmap, 0xdd0a, &vol_reg[2]); @@ -1172,9 +1196,8 @@ static void rt1320_calibrate(struct rt1320_sdw_priv *rt1320) goto _finish_; } - regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); - fw_ready &= 0x1; - if (!fw_ready) { + dspfw_status = rt1320_dspfw_status(rt1320); + if (dspfw_status <= 0) { dev_dbg(dev, "%s, DSP FW is NOT ready. Please load DSP FW first\n", __func__); goto _finish_; } @@ -1383,30 +1406,17 @@ static void rt1320_vab_preset(struct rt1320_sdw_priv *rt1320) static void rt1320_t0_load(struct rt1320_sdw_priv *rt1320, unsigned int l_t0, unsigned int r_t0) { struct device *dev = &rt1320->sdw_slave->dev; - unsigned int factor = (1 << 22), fw_ready; + unsigned int factor = (1 << 22); int l_t0_data[38], r_t0_data[38]; - unsigned int fw_status_addr; - - switch (rt1320->dev_id) { - case RT1320_DEV_ID: - fw_status_addr = RT1320_DSPFW_STATUS_ADDR; - break; - case RT1321_DEV_ID: - fw_status_addr = RT1321_DSPFW_STATUS_ADDR; - break; - default: - dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); - return; - } + int dspfw_status; regmap_write(rt1320->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x00); rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x00); - regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); - fw_ready &= 0x1; - if (!fw_ready) { + dspfw_status = rt1320_dspfw_status(rt1320); + if (dspfw_status <= 0) { dev_warn(dev, "%s, DSP FW is NOT ready\n", __func__); goto _exit_; } @@ -1599,8 +1609,7 @@ struct rt1320_dspfwheader { struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(rt1320->component); struct device *dev = &rt1320->sdw_slave->dev; - unsigned int val, i, fw_offset, fw_ready; - unsigned int fw_status_addr; + unsigned int val, i, fw_offset; struct rt1320_dspfwheader *fwheader; struct rt1320_imageinfo *ptr_img; struct sdw_bpt_section sec[10]; @@ -1613,18 +1622,6 @@ struct rt1320_dspfwheader { int len_vendor, len_product, len_sku; char filename[512]; - switch (rt1320->dev_id) { - case RT1320_DEV_ID: - fw_status_addr = RT1320_DSPFW_STATUS_ADDR; - break; - case RT1321_DEV_ID: - fw_status_addr = RT1321_DSPFW_STATUS_ADDR; - break; - default: - dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); - return; - } - dmi_vendor = dmi_get_system_info(DMI_SYS_VENDOR); dmi_product = dmi_get_system_info(DMI_PRODUCT_NAME); dmi_sku = dmi_get_system_info(DMI_PRODUCT_SKU); @@ -1654,9 +1651,7 @@ struct rt1320_dspfwheader { RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x00); rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x00); - regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); - fw_ready &= 0x1; - if (fw_ready) { + if (rt1320_dspfw_status(rt1320)) { dev_dbg(dev, "%s, DSP FW was already\n", __func__); rt1320->fw_load_done = true; goto _exit_; @@ -2327,25 +2322,12 @@ static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0); static int rt1320_r0_load(struct rt1320_sdw_priv *rt1320) { struct device *dev = regmap_get_device(rt1320->regmap); - unsigned int fw_status_addr; - unsigned int fw_ready; + int dspfw_status; int ret = 0; if (!rt1320->r0_l_reg || !rt1320->r0_r_reg) return -EINVAL; - switch (rt1320->dev_id) { - case RT1320_DEV_ID: - fw_status_addr = RT1320_DSPFW_STATUS_ADDR; - break; - case RT1321_DEV_ID: - fw_status_addr = RT1321_DSPFW_STATUS_ADDR; - break; - default: - dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); - return -EINVAL; - } - regmap_write(rt1320->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x00); ret = rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x00); @@ -2354,9 +2336,8 @@ static int rt1320_r0_load(struct rt1320_sdw_priv *rt1320) goto _timeout_; } - regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); - fw_ready &= 0x1; - if (!fw_ready) { + dspfw_status = rt1320_dspfw_status(rt1320); + if (dspfw_status <= 0) { dev_dbg(dev, "%s, DSP FW is NOT ready\n", __func__); goto _timeout_; } -- cgit v1.2.3 From d79828f380b337a118ef93d7f4daabb41dde24bc Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Mon, 22 Jun 2026 17:11:48 +0800 Subject: ASoC: rt1321: add support for rt1321 VA1/VA2 This patch adds support for the RT1321 VA1 and VA2 amplifier. Signed-off-by: Shuming Fan Link: https://patch.msgid.link/20260622091149.2183334-1-shumingf@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt1320-sdw.c | 699 ++++++++++++++++++++++++++++++++++++++---- sound/soc/codecs/rt1320-sdw.h | 12 + 2 files changed, 653 insertions(+), 58 deletions(-) diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index 8d6fe2e36fc7..49fe100aef6e 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -411,6 +411,402 @@ static const struct reg_sequence rt1321_blind_write[] = { { 0x41001988, 0x03 }, }; +static const struct reg_sequence rt1321_va1_blind_write[] = { + { 0x0000c003, 0xf0 }, + { 0x0000c01b, 0xfc }, + { 0x0000c5c3, 0xf2 }, + { 0x0000c5c2, 0x00 }, + { 0x0000c5c1, 0x10 }, + { 0x0000c5c0, 0x04 }, + { 0x0000c5c7, 0x03 }, + { 0x0000c5c6, 0x10 }, + { 0x0000c526, 0x47 }, + { 0x0000c5c4, 0x12 }, + { 0x0000c5c5, 0x60 }, + { 0x0000c520, 0x10 }, + { 0x0000c521, 0x32 }, + { 0x0000c5c7, 0x00 }, + { 0x0000c5c8, 0x03 }, + { 0x0000c5d3, 0x08 }, + { 0x0000c5d2, 0x0a }, + { 0x0000c5d1, 0x49 }, + { 0x0000c5d0, 0x0f }, + { 0x0000c580, 0x10 }, + { 0x0000c581, 0x32 }, + { 0x0000c582, 0x01 }, + { 0x0000c682, 0x60 }, + { 0x0000c019, 0x10 }, + { 0x0000c5f0, 0x01 }, + { 0x0000c5f7, 0x22 }, + { 0x0000c5f6, 0x22 }, + { 0x0000c057, 0x51 }, + { 0x0000c054, 0x55 }, + { 0x0000c053, 0x55 }, + { 0x0000c052, 0x55 }, + { 0x0000c051, 0x01 }, + { 0x0000c050, 0x15 }, + { 0x0000c060, 0x99 }, + { 0x0000c030, 0x55 }, + { 0x0000c061, 0x55 }, + { 0x0000c063, 0x55 }, + { 0x0000c065, 0xa5 }, + { 0x0000c06b, 0x0a }, + { 0x0000ca05, 0xd6 }, + { 0x0000ca06, 0x11 }, + { 0x0000ca07, 0x1e }, + { 0x0000ca25, 0xd6 }, + { 0x0000ca26, 0x11 }, + { 0x0000ca27, 0x1e }, + { 0x0000cd00, 0x05 }, + { 0x0000cd81, 0x49 }, + { 0x0000cd82, 0x49 }, + { 0x0000c604, 0x40 }, + { 0x0000c609, 0x40 }, + { 0x0000c046, 0xf7 }, + { 0x0000c045, 0xff }, + { 0x0000c044, 0xff }, + { 0x0000c043, 0xff }, + { 0x0000c042, 0xff }, + { 0x0000c041, 0xff }, + { 0x0000c040, 0xff }, + { 0x0000c049, 0xff }, + { 0x0000c028, 0x3f }, + { 0x0000c020, 0x3f }, + { 0x0000c032, 0x13 }, + { 0x0000c033, 0x01 }, + { 0x0000cc10, 0x01 }, + { 0x0000dc20, 0x03 }, + { 0x0000de03, 0x05 }, + { 0x0000dc00, 0x00 }, + { 0x0000c700, 0xf0 }, + { 0x0000c701, 0x13 }, + { 0x0000c900, 0xc3 }, + { 0x0000c570, 0x08 }, + { 0x0000c086, 0x02 }, + { 0x0000c085, 0x7f }, + { 0x0000c084, 0x00 }, + { 0x0000c081, 0xff }, + { 0x0000f084, 0x0f }, + { 0x0000f083, 0xff }, + { 0x0000f082, 0xff }, + { 0x0000f081, 0xff }, + { 0x0000f080, 0xff }, + { 0x20003003, 0x3f }, + { 0x20005818, 0x81 }, + { 0x20009018, 0x81 }, + { 0x2000301c, 0x81 }, + { 0x0000c003, 0xc0 }, + { 0x0000c047, 0x80 }, + { 0x0000d541, 0x80 }, + { 0x0000d487, 0x0b }, + { 0x0000d487, 0x3b }, + { 0x0000d486, 0xc3 }, + { 0x0000d470, 0x89 }, + { 0x0000d471, 0x3a }, + { 0x0000d472, 0x3d }, + { 0x0000d474, 0x11 }, + { 0x0000d475, 0x32 }, + { 0x0000d476, 0x64 }, + { 0x0000d477, 0x10 }, + { 0x0000d478, 0xff }, + { 0x0000d479, 0x20 }, + { 0x0000d47a, 0x10 }, + { 0x0000d73c, 0xb7 }, + { 0x0000d73d, 0xd7 }, + { 0x0000d73e, 0x00 }, + { 0x0000d73f, 0x10 }, + { 0x1000cd56, 0x00 }, + { 0x3fc2dfc0, 0x03 }, + { 0x3fc2dfc1, 0x00 }, + { 0x3fc2dfc2, 0x00 }, + { 0x3fc2dfc3, 0x00 }, + { 0x3fc2dfc4, 0x01 }, + { 0x3fc2dfc5, 0x00 }, + { 0x3fc2dfc6, 0x00 }, + { 0x3fc2dfc7, 0x00 }, + { 0x3fc2df80, 0x00 }, + { 0x3fc2df81, 0x00 }, + { 0x3fc2df82, 0x00 }, + { 0x3fc2df83, 0x00 }, + { 0x0000d541, 0x40 }, + { 0x0000d486, 0x43 }, + { 0x1000db00, 0x04 }, + { 0x1000db01, 0x00 }, + { 0x1000db02, 0x10 }, + { 0x1000db03, 0x00 }, + { 0x1000db04, 0x00 }, + { 0x1000db05, 0x45 }, + { 0x1000db06, 0x0d }, + { 0x1000db07, 0x01 }, + { 0x1000db08, 0x00 }, + { 0x1000db09, 0x00 }, + { 0x1000db0a, 0xbf }, + { 0x1000db0b, 0x0b }, + { 0x1000db0c, 0x11 }, + { 0x1000db0d, 0x00 }, + { 0x1000db0e, 0x00 }, + { 0x1000db0f, 0x00 }, + { 0x1000db10, 0x2c }, + { 0x1000db11, 0xfa }, + { 0x1000db12, 0x00 }, + { 0x1000db13, 0x00 }, + { 0x1000db14, 0x09 }, + { 0x0000d540, 0x21 }, + { 0x0000c570, 0x08 }, + { 0x0000d714, 0x17 }, + { 0x0000c5c3, 0xf2 }, + { 0x0000c5c8, 0x03 }, + { 0x20009012, 0x00 }, + { 0x0000dd08, 0x17 }, + { 0x0000dd09, 0x0e }, + { 0x0000dd0a, 0x17 }, + { 0x0000dd0b, 0x0e }, + { 0x0000c570, 0x08 }, + { 0x0000d471, 0x3a }, + { 0x0000db00, 0x00 }, + { 0x0000db01, 0x00 }, + { 0x0000db02, 0x73 }, + { 0x0000db03, 0x00 }, + { 0x0000db04, 0x00 }, + { 0x0000db05, 0x00 }, + { 0x0000db06, 0x00 }, + { 0x0000db07, 0x00 }, + { 0x0000db08, 0x7f }, + { 0x0000db09, 0x00 }, + { 0x0000db1a, 0x00 }, + { 0x0000db1b, 0x00 }, + { 0x0000db19, 0x00 }, +}; + +static const struct reg_sequence rt1321_va2_blind_write[] = { + { 0x0000c003, 0xf0 }, + { 0x0000c01b, 0xfc }, + { 0x0000c5c3, 0xf2 }, + { 0x0000c5c2, 0x00 }, + { 0x0000c5c1, 0x10 }, + { 0x0000c5c0, 0x04 }, + { 0x0000c5c7, 0x03 }, + { 0x0000c5c6, 0x10 }, + { 0x0000c526, 0x47 }, + { 0x0000c5c4, 0x12 }, + { 0x0000c5c5, 0x60 }, + { 0x0000c520, 0x10 }, + { 0x0000c521, 0x32 }, + { 0x0000c5c7, 0x00 }, + { 0x0000c5c8, 0x03 }, + { 0x0000c5d3, 0x08 }, + { 0x0000c5d2, 0x0a }, + { 0x0000c5d1, 0x49 }, + { 0x0000c5d0, 0x0f }, + { 0x0000c580, 0x10 }, + { 0x0000c581, 0x32 }, + { 0x0000c582, 0x01 }, + { 0x0000c682, 0x60 }, + { 0x0000c019, 0x10 }, + { 0x0000c5f0, 0x01 }, + { 0x0000c5f7, 0x22 }, + { 0x0000c5f6, 0x22 }, + { 0x0000c057, 0x51 }, + { 0x0000c054, 0x55 }, + { 0x0000c053, 0x55 }, + { 0x0000c052, 0x55 }, + { 0x0000c051, 0x01 }, + { 0x0000c050, 0x15 }, + { 0x0000c060, 0x99 }, + { 0x0000c030, 0x55 }, + { 0x0000c061, 0x55 }, + { 0x0000c063, 0x55 }, + { 0x0000c065, 0xa5 }, + { 0x0000c06b, 0x0a }, + { 0x0000ca05, 0xd6 }, + { 0x0000ca06, 0x11 }, + { 0x0000ca07, 0x1e }, + { 0x0000ca25, 0xd6 }, + { 0x0000ca26, 0x11 }, + { 0x0000ca27, 0x1e }, + { 0x0000cd00, 0x05 }, + { 0x0000cd81, 0x49 }, + { 0x0000cd82, 0x49 }, + { 0x0000c604, 0x40 }, + { 0x0000c609, 0x40 }, + { 0x0000c046, 0xf7 }, + { 0x0000c045, 0xff }, + { 0x0000c044, 0xff }, + { 0x0000c043, 0xff }, + { 0x0000c042, 0xff }, + { 0x0000c041, 0xff }, + { 0x0000c040, 0xff }, + { 0x0000c049, 0xff }, + { 0x0000c028, 0x3f }, + { 0x0000c020, 0x3f }, + { 0x0000c032, 0x13 }, + { 0x0000c033, 0x01 }, + { 0x0000cc10, 0x01 }, + { 0x0000dc20, 0x03 }, + { 0x0000de03, 0x05 }, + { 0x0000dc00, 0x00 }, + { 0x0000c700, 0xf0 }, + { 0x0000c701, 0x13 }, + { 0x0000c900, 0xc3 }, + { 0x0000c570, 0x08 }, + { 0x0000c086, 0x02 }, + { 0x0000c085, 0x7f }, + { 0x0000c084, 0x00 }, + { 0x0000c081, 0xff }, + { 0x0000f084, 0x0f }, + { 0x0000f083, 0xff }, + { 0x0000f082, 0xff }, + { 0x0000f081, 0xff }, + { 0x0000f080, 0xff }, + { 0x20003003, 0x3f }, + { 0x20005818, 0x81 }, + { 0x20009018, 0x81 }, + { 0x2000301c, 0x81 }, + { 0x0000c003, 0xc0 }, + { 0x0000c047, 0x80 }, + { 0x0000d541, 0x80 }, + { 0x0000d487, 0x0b }, + { 0x0000d487, 0x3b }, + { 0x0000d486, 0xc3 }, + { 0x0000d470, 0x89 }, + { 0x0000d471, 0x3a }, + { 0x0000d472, 0x3d }, + { 0x0000d474, 0x11 }, + { 0x0000d475, 0x32 }, + { 0x0000d476, 0x64 }, + { 0x0000d477, 0x10 }, + { 0x0000d478, 0xff }, + { 0x0000d479, 0x20 }, + { 0x0000d47a, 0x10 }, + { 0x10008000, 0x67 }, + { 0x10008001, 0x80 }, + { 0x10008002, 0x00 }, + { 0x10008003, 0x00 }, + { 0x1000cd56, 0x00 }, + { 0x0000d486, 0x43 }, + { 0x3fc2dfc3, 0x00 }, + { 0x3fc2dfc2, 0x00 }, + { 0x3fc2dfc1, 0x00 }, + { 0x3fc2dfc0, 0x03 }, + { 0x3fc2dfc7, 0x00 }, + { 0x3fc2dfc6, 0x00 }, + { 0x3fc2dfc5, 0x00 }, + { 0x3fc2dfc4, 0x01 }, + { 0x3fc2dfa3, 0x00 }, + { 0x3fc2dfa2, 0x00 }, + { 0x3fc2dfa1, 0x00 }, + { 0x3fc2dfa0, 0x00 }, + { 0x3fc2df80, 0x10 }, + { 0x3fc2df81, 0x20 }, + { 0x3fc2df82, 0x00 }, + { 0x3fc2df83, 0x00 }, + { 0x3fc2df84, 0x50 }, + { 0x3fc2df85, 0x19 }, + { 0x3fc2df86, 0x00 }, + { 0x3fc2df87, 0x00 }, + { 0x3fc2df88, 0x52 }, + { 0x3fc2df89, 0x23 }, + { 0x3fc2df8a, 0x00 }, + { 0x3fc2df8b, 0x00 }, + { 0x3fc2df8c, 0xe0 }, + { 0x3fc2df8d, 0x2e }, + { 0x3fc2df8e, 0x00 }, + { 0x3fc2df8f, 0x00 }, + { 0x3fc2df90, 0xe0 }, + { 0x3fc2df91, 0x2e }, + { 0x3fc2df92, 0x00 }, + { 0x3fc2df93, 0x00 }, + { 0x3fc2df94, 0x01 }, + { 0x3fc2df95, 0x08 }, + { 0x3fc2df96, 0x00 }, + { 0x3fc2df97, 0x00 }, + { 0x3fc2df40, 0x80 }, + { 0x3fc2df41, 0xbb }, + { 0x3fc2df42, 0x00 }, + { 0x3fc2df43, 0x00 }, + { 0x3fc2df44, 0xc0 }, + { 0x3fc2df45, 0x99 }, + { 0x3fc2df46, 0x01 }, + { 0x3fc2df47, 0x00 }, + { 0x3fc2df48, 0x00 }, + { 0x3fc2df49, 0x00 }, + { 0x3fc2df4a, 0x00 }, + { 0x3fc2df4b, 0x00 }, + { 0x3fc2df4c, 0x00 }, + { 0x3fc2df4d, 0x00 }, + { 0x3fc2df4e, 0x00 }, + { 0x3fc2df4f, 0x00 }, + { 0x3fc2df50, 0x01 }, + { 0x3fc2df51, 0x00 }, + { 0x3fc2df52, 0x00 }, + { 0x3fc2df53, 0x00 }, + { 0x3fc2df54, 0x01 }, + { 0x3fc2df55, 0x00 }, + { 0x3fc2df56, 0x00 }, + { 0x3fc2df57, 0x00 }, + { 0x3fc2df58, 0x00 }, + { 0x3fc2df59, 0x00 }, + { 0x3fc2df5a, 0x00 }, + { 0x3fc2df5b, 0x00 }, + { 0x3fc2df5c, 0x01 }, + { 0x3fc2df5d, 0x00 }, + { 0x3fc2df5e, 0x00 }, + { 0x3fc2df5f, 0x00 }, + { 0x3fc2df60, 0x00 }, + { 0x3fc2df61, 0x00 }, + { 0x3fc2df62, 0x00 }, + { 0x3fc2df63, 0x00 }, + { 0x3fc2df64, 0x00 }, + { 0x3fc2df65, 0x00 }, + { 0x3fc2df66, 0x00 }, + { 0x3fc2df67, 0x10 }, + { 0x3fc2df68, 0x01 }, + { 0x3fc2df69, 0x00 }, + { 0x3fc2df6a, 0x00 }, + { 0x3fc2df6b, 0x00 }, + { 0x3fc2df6c, 0x01 }, + { 0x3fc2df6d, 0x00 }, + { 0x3fc2df6e, 0x00 }, + { 0x3fc2df6f, 0x00 }, + { 0x3fc2df70, 0x04 }, + { 0x3fc2df71, 0x00 }, + { 0x3fc2df72, 0x00 }, + { 0x3fc2df73, 0x00 }, + { 0x3fc2df74, 0x01 }, + { 0x3fc2df75, 0x00 }, + { 0x3fc2df76, 0x00 }, + { 0x3fc2df77, 0x00 }, + { 0x1000db00, 0x04 }, + { 0x1000db01, 0x00 }, + { 0x1000db02, 0x10 }, + { 0x1000db03, 0x00 }, + { 0x1000db04, 0x00 }, + { 0x1000db05, 0x45 }, + { 0x1000db06, 0x0d }, + { 0x1000db07, 0x01 }, + { 0x1000db08, 0x00 }, + { 0x1000db09, 0x00 }, + { 0x1000db0a, 0xbf }, + { 0x1000db0b, 0x0b }, + { 0x1000db0c, 0x11 }, + { 0x1000db0d, 0x00 }, + { 0x1000db0e, 0x00 }, + { 0x1000db0f, 0x00 }, + { 0x1000db10, 0x2c }, + { 0x1000db11, 0xfa }, + { 0x1000db12, 0x00 }, + { 0x1000db13, 0x00 }, + { 0x1000db14, 0x09 }, + { 0x0000d540, 0x21 }, + { 0x0000d714, 0x17 }, + { 0x0000dd0b, 0x0d }, + { 0x0000dd0a, 0xff }, + { 0x0000dd09, 0x0d }, + { 0x0000dd08, 0xff }, + { 0x0000c5fb, 0x12 }, + { 0x0000c570, 0x08 }, +}; + static const struct reg_default rt1320_reg_defaults[] = { { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, @@ -498,10 +894,15 @@ static bool rt1320_readable_register(struct device *dev, unsigned int reg) case RT1321_PATCH_MAIN_VER ... RT1321_PATCH_BETA_VER: case 0x1000f008: case 0x1000f021: - case 0x2000300f: + case 0x20003000 ... 0x2000300f: case 0x2000301c: - case 0x2000900f: + case 0x20003040 ... 0x20003059: + case 0x20005800 ... 0x2000580f: + case 0x20005818: + case 0x20005840 ... 0x20005859: + case 0x20009000 ... 0x2000900f: case 0x20009018: + case 0x20009040 ... 0x20009059: case 0x3fc000c0 ... 0x3fc2dfc8: case 0x3fe00000 ... 0x3fe36fff: /* 0x40801508/0x40801809/0x4080180a/0x40801909/0x4080190a */ @@ -597,15 +998,20 @@ static bool rt1320_volatile_register(struct device *dev, unsigned int reg) case 0x1000c000 ... 0x1000dfff: case 0x1000f008: case 0x1000f021: - case 0x2000300f: + case 0x2000300e ... 0x2000300f: case 0x2000301c: - case 0x2000900f: + case 0x20003040 ... 0x20003059: + case 0x2000580e ... 0x2000580f: + case 0x20005818: + case 0x20005840 ... 0x20005859: + case 0x2000900e ... 0x2000900f: case 0x20009018: + case 0x20009040 ... 0x20009059: case 0x3fc2ab80 ... 0x3fc2ac4c: case 0x3fc2b780: case 0x3fc2bf80 ... 0x3fc2bf83: case 0x3fc2bfc0 ... 0x3fc2bfc8: - case 0x3fc2d300 ... 0x3fc2d354: + case 0x3fc2d300 ... 0x3fc2d3cc: case 0x3fc2dfc0 ... 0x3fc2dfc8: case 0x3fe2e000 ... 0x3fe2e003: case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_ACTUAL_POWER_STATE, 0): @@ -1019,6 +1425,9 @@ static int rt1320_fw_param_protocol(struct rt1320_sdw_priv *rt1320, enum rt1320_ if (!tempbuf) return -ENOMEM; + if (rt1320->dev_id == RT1321_DEV_ID && rt1320->version_id == RT1321_VA2) + paramid += 0x0bff0000; + paramhr.moudleid = 1; paramhr.commandtype = cmdid; /* 8 is "sizeof(paramid) + sizeof(paramlength)" */ @@ -1157,6 +1566,52 @@ static void rt1320_calc_r0(struct rt1320_sdw_priv *rt1320) l_calir0, l_calir0_lo, r_calir0, r_calir0_lo); } +static int rt1320_pilot_tone_output(struct rt1320_sdw_priv *rt1320) +{ + struct device *dev = &rt1320->sdw_slave->dev; + int l_targetpostgain, r_targetpostgain; + unsigned long long factor = (1 << 12); + int l_pilotgain[9], r_pilotgain[9]; + const int postgain_step = 234; + int targetGain; + + switch (rt1320->dev_id) { + case RT1320_DEV_ID: + targetGain = -320000; + break; + case RT1321_DEV_ID: + targetGain = -420000; + if (rt1320->version_id == RT1321_VA0) + targetGain = -320000; + break; + default: + dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); + return -EINVAL; + } + + rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 70, &l_pilotgain[0], sizeof(l_pilotgain)); + rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 71, &r_pilotgain[0], sizeof(r_pilotgain)); + dev_dbg(dev, "%s, LR pilotgain %d, %d\n", __func__, l_pilotgain[2], r_pilotgain[2]); + + /* calculate pilot tone gain */ + l_pilotgain[2] = (l_pilotgain[2] * 10000) / factor; + r_pilotgain[2] = (r_pilotgain[2] * 10000) / factor; + + /* calculate post gain to meet target gain */ + l_targetpostgain = abs(targetGain - l_pilotgain[2]) / postgain_step; + r_targetpostgain = abs(targetGain - r_pilotgain[2]) / postgain_step; + l_targetpostgain = 0xfff - l_targetpostgain; + r_targetpostgain = 0xfff - r_targetpostgain; + dev_dbg(dev, "%s, LR targetpostgain=0x%x, 0x%x\n", __func__, l_targetpostgain, r_targetpostgain); + + regmap_write(rt1320->regmap, 0xdd0b, (l_targetpostgain & 0xf00) >> 8); + regmap_write(rt1320->regmap, 0xdd0a, l_targetpostgain & 0xff); + regmap_write(rt1320->regmap, 0xdd09, (r_targetpostgain & 0xf00) >> 8); + regmap_write(rt1320->regmap, 0xdd08, r_targetpostgain & 0xff); + + return 0; +} + static void rt1320_calibrate(struct rt1320_sdw_priv *rt1320) { struct device *dev = &rt1320->sdw_slave->dev; @@ -1174,16 +1629,10 @@ static void rt1320_calibrate(struct rt1320_sdw_priv *rt1320) if (!rt1320->component) return; - /* set volume 0dB */ regmap_read(rt1320->regmap, 0xdd0b, &vol_reg[3]); regmap_read(rt1320->regmap, 0xdd0a, &vol_reg[2]); regmap_read(rt1320->regmap, 0xdd09, &vol_reg[1]); regmap_read(rt1320->regmap, 0xdd08, &vol_reg[0]); - regmap_write(rt1320->regmap, 0xdd0b, 0x0f); - regmap_write(rt1320->regmap, 0xdd0a, 0xff); - regmap_write(rt1320->regmap, 0xdd09, 0x0f); - regmap_write(rt1320->regmap, 0xdd08, 0xff); - regmap_read(rt1320->regmap, 0xc5fb, ®_c5fb); regmap_read(rt1320->regmap, 0xc570, ®_c570); regmap_read(rt1320->regmap, 0xcd00, ®_cd00); @@ -1208,8 +1657,19 @@ static void rt1320_calibrate(struct rt1320_sdw_priv *rt1320) goto _finish_; } - if (rt1320->dev_id == RT1320_DEV_ID) - regmap_write(rt1320->regmap, 0xc5fb, 0x00); + /* fine tune pilot tone output */ + ret = rt1320_pilot_tone_output(rt1320); + if (ret < 0) { + dev_dbg(dev, "%s, Failed to tune pilot tone output\n", __func__); + goto _finish_; + } + + if (rt1320->dev_id == RT1321_DEV_ID) { + regmap_update_bits(rt1320->regmap, 0xc047, 0x80, 0x00); + regmap_write(rt1320->regmap, 0xc5c4, 0x12); + } + + regmap_write(rt1320->regmap, 0xc5fb, 0x00); regmap_write(rt1320->regmap, 0xc570, 0x0b); regmap_write(rt1320->regmap, 0xcd00, 0xc5); @@ -1270,6 +1730,11 @@ _finish_: SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03); rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x03); + if (rt1320->dev_id == RT1321_DEV_ID) { + regmap_update_bits(rt1320->regmap, 0xc047, 0x80, 0x80); + regmap_write(rt1320->regmap, 0xc5c4, 0x10); + } + /* advance gain will be set when R0 load, not here */ regmap_write(rt1320->regmap, 0xdd0b, vol_reg[3]); regmap_write(rt1320->regmap, 0xdd0a, vol_reg[2]); @@ -1336,6 +1801,9 @@ static void rt1320_load_mcu_patch(struct rt1320_sdw_priv *rt1320) max_addr = 0x10007fff; break; case RT1321_DEV_ID: + if (rt1320->version_id == RT1321_VA2) + return; + filename = RT1321_VA_MCU_PATCH; min_addr = 0x10008000; max_addr = 0x10008fff; @@ -1494,22 +1962,48 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) request_firmware(&rae_fw, rae_filename, dev); if (rae_fw) { - /* RAE CRC clear */ - regmap_write(rt1320->regmap, 0xe80b, 0x0f); - - /* RAE stop & CRC disable */ - regmap_update_bits(rt1320->regmap, 0xe803, 0xbc, 0x00); - - while (--retry) { - regmap_read(rt1320->regmap, 0xe83f, &value); - if (value & 0x40) - break; - usleep_range(1000, 1100); - } - if (!retry && !(value & 0x40)) { - dev_err(dev, "%s: RAE is not ready to load\n", __func__); - release_firmware(rae_fw); - return -ETIMEDOUT; + switch (rt1320->dev_id) { + case RT1320_DEV_ID: + /* RAE CRC clear */ + regmap_write(rt1320->regmap, 0xe80b, 0x0f); + /* RAE stop & CRC disable */ + regmap_update_bits(rt1320->regmap, 0xe803, 0xbc, 0x00); + while (--retry) { + regmap_read(rt1320->regmap, 0xe83f, &value); + if (value & 0x40) + break; + usleep_range(1000, 1100); + } + if (!retry && !(value & 0x40)) { + dev_err(dev, "%s: RAE is not ready to load\n", __func__); + release_firmware(rae_fw); + return -ETIMEDOUT; + } + break; + case RT1321_DEV_ID: + /* RAE CRC clear */ + regmap_write(rt1320->regmap, 0x2000300e, 0xc0); + regmap_write(rt1320->regmap, 0x2000300f, 0x0f); + /* RAE stop & Phase sync & CRC disable */ + regmap_update_bits(rt1320->regmap, 0x20003003, 0xfe, 0x00); + regmap_update_bits(rt1320->regmap, 0xc047, 0x80, 0x00); + regmap_update_bits(rt1320->regmap, 0x2000301c, 0x01, 0x00); + /* check whether write state is ready */ + while (--retry) { + regmap_read(rt1320->regmap, 0x20003043, &value); + if (value & 0x40) + break; + usleep_range(1000, 1100); + } + if (!retry && !(value & 0x40)) { + dev_err(dev, "%s: RAE is not ready to load\n", __func__); + release_firmware(rae_fw); + return -ETIMEDOUT; + } + break; + default: + dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); + return -EINVAL; } dev_dbg(dev, "%s, rae_fw size=0x%zx\n", __func__, rae_fw->size); @@ -1570,18 +2064,34 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) goto _exit_; } - /* RAE CRC enable */ - regmap_update_bits(rt1320->regmap, 0xe803, 0x0c, 0x0c); - - /* RAE update */ - regmap_update_bits(rt1320->regmap, 0xe80b, 0x80, 0x00); - regmap_update_bits(rt1320->regmap, 0xe80b, 0x80, 0x80); - - /* RAE run */ - regmap_update_bits(rt1320->regmap, 0xe803, 0x80, 0x80); - - regmap_read(rt1320->regmap, 0xe80b, &value); - dev_dbg(dev, "%s: CAE run => 0xe80b reg = 0x%x\n", __func__, value); + switch (rt1320->dev_id) { + case RT1320_DEV_ID: + /* RAE CRC enable */ + regmap_update_bits(rt1320->regmap, 0xe803, 0x0c, 0x0c); + /* RAE update */ + regmap_update_bits(rt1320->regmap, 0xe80b, 0x80, 0x00); + regmap_update_bits(rt1320->regmap, 0xe80b, 0x80, 0x80); + /* RAE run */ + regmap_update_bits(rt1320->regmap, 0xe803, 0x80, 0x80); + regmap_read(rt1320->regmap, 0xe80b, &value); + dev_dbg(dev, "%s: CAE run => 0xe80b reg = 0x%x\n", __func__, value); + break; + case RT1321_DEV_ID: + /* RAE CRC enable */ + regmap_update_bits(rt1320->regmap, 0x20003003, 0x30, 0x30); + /* RAE update */ + regmap_update_bits(rt1320->regmap, 0x2000301c, 0x80, 0x00); + regmap_update_bits(rt1320->regmap, 0x2000301c, 0x80, 0x80); + regmap_update_bits(rt1320->regmap, 0x20009018, 0x80, 0x00); + regmap_update_bits(rt1320->regmap, 0x20009018, 0x80, 0x80); + regmap_update_bits(rt1320->regmap, 0x20005818, 0x80, 0x00); + regmap_update_bits(rt1320->regmap, 0x20005818, 0x80, 0x80); + /* RAE run */ + regmap_update_bits(rt1320->regmap, 0x2000301c, 0x01, 0x01); + /* Phase sync eanble */ + regmap_update_bits(rt1320->regmap, 0xc047, 0x80, 0x80); + break; + } rt1320->rae_update_done = true; @@ -1620,6 +2130,8 @@ struct rt1320_dspfwheader { unsigned int hdr_size = 0; const char *dmi_vendor, *dmi_product, *dmi_sku; int len_vendor, len_product, len_sku; + unsigned char boot_mode = 0; /* 0: from RAM; 1: from ROM */ + unsigned char has_0x3fc00000 = 0; char filename[512]; dmi_vendor = dmi_get_system_info(DMI_SYS_VENDOR); @@ -1657,9 +2169,6 @@ struct rt1320_dspfwheader { goto _exit_; } - /* change to IRAM */ - regmap_update_bits(rt1320->regmap, 0xf01e, 0x80, 0x00); - request_firmware(&fw, filename, dev); if (fw) { fwheader = (struct rt1320_dspfwheader *)fw->data; @@ -1705,9 +2214,11 @@ struct rt1320_dspfwheader { dev_fw_match = true; break; case RT1321_DEV_ID: - if (ptr_img->addr == 0x3fc00000) - if (fw_data[9] == '1') + if (ptr_img->addr == 0x3fc00000) { + if (fw_data[7] == '1') dev_fw_match = true; + has_0x3fc00000 = 1; + } break; default: dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); @@ -1717,6 +2228,22 @@ struct rt1320_dspfwheader { fw_offset += ptr_img->size; } + if (rt1320->dev_id == RT1321_DEV_ID && rt1320->version_id == RT1321_VA2) { + /* + * For 1321 VA2, if the FW doesn't include the section for address 0x3fc00000, + * it means the FW will boot from ROM and force dev_fw_match to true to download FW by BRA. + */ + if (!has_0x3fc00000) { + boot_mode = 1; + dev_fw_match = true; + } + dev_dbg(dev, "%s: Boot from %s for VA2\n", __func__, (boot_mode ? "ROM" : "RAM")); + } + + /* change to IRAM */ + if (!boot_mode) + regmap_update_bits(rt1320->regmap, 0xf01e, 0x80, 0x00); + if (dev_fw_match) { dev_dbg(dev, "%s, starting BRA downloading FW..\n", __func__); rt1320->bra_msg.dev_num = rt1320->sdw_slave->dev_num; @@ -1739,24 +2266,39 @@ struct rt1320_dspfwheader { goto _exit_; } - /* run RAM code */ - regmap_read(rt1320->regmap, 0x3fc2bfc0, &val); - val |= 0x8; - regmap_write(rt1320->regmap, 0x3fc2bfc0, val); - - /* clear frame counter */ switch (rt1320->dev_id) { case RT1320_DEV_ID: + /* run RAM code */ + regmap_read(rt1320->regmap, 0x3fc2bfc0, &val); + val |= 0x8; + regmap_write(rt1320->regmap, 0x3fc2bfc0, val); + /* clear frame counter */ regmap_write(rt1320->regmap, 0x3fc2bfcb, 0x00); regmap_write(rt1320->regmap, 0x3fc2bfca, 0x00); regmap_write(rt1320->regmap, 0x3fc2bfc9, 0x00); regmap_write(rt1320->regmap, 0x3fc2bfc8, 0x00); break; case RT1321_DEV_ID: + if (!boot_mode) { + /* run RAM code */ + regmap_read(rt1320->regmap, 0x3fc2dfc0, &val); + val |= 0x8; + regmap_write(rt1320->regmap, 0x3fc2dfc0, val); + } + /* clear frame counter */ regmap_write(rt1320->regmap, 0x3fc2dfcb, 0x00); regmap_write(rt1320->regmap, 0x3fc2dfca, 0x00); regmap_write(rt1320->regmap, 0x3fc2dfc9, 0x00); regmap_write(rt1320->regmap, 0x3fc2dfc8, 0x00); + /* enable handshake */ + regmap_write(rt1320->regmap, 0x3fc2dfc4, 0x00); + regmap_write(rt1320->regmap, 0xd470, 0xad); + /* minimum phase settings */ + regmap_write(rt1320->regmap, 0xc5c4, 0x10); + regmap_write(rt1320->regmap, 0x20003003, 0x31); + regmap_update_bits(rt1320->regmap, 0x20003002, 0x40, 0x00); + regmap_write(rt1320->regmap, 0xc5b3, 0x01); + regmap_write(rt1320->regmap, 0xc052, 0x11); break; } @@ -1839,14 +2381,35 @@ static void rt1320_vc_preset(struct rt1320_sdw_priv *rt1320) static void rt1321_preset(struct rt1320_sdw_priv *rt1320) { + const struct reg_sequence *blindwrite; unsigned int i, reg, val, delay; + unsigned int array_size; + + switch (rt1320->version_id) { + case RT1321_VA0: + blindwrite = rt1321_blind_write; + array_size = ARRAY_SIZE(rt1321_blind_write); + break; + case RT1321_VA1: + blindwrite = rt1321_va1_blind_write; + array_size = ARRAY_SIZE(rt1321_va1_blind_write); + break; + case RT1321_VA2: + blindwrite = rt1321_va2_blind_write; + array_size = ARRAY_SIZE(rt1321_va2_blind_write); + break; + default: + dev_err(&rt1320->sdw_slave->dev, "%s: Unknown version ID %d\n", + __func__, rt1320->version_id); + return; + } - for (i = 0; i < ARRAY_SIZE(rt1321_blind_write); i++) { - reg = rt1321_blind_write[i].reg; - val = rt1321_blind_write[i].def; - delay = rt1321_blind_write[i].delay_us; + for (i = 0; i < array_size; i++) { + reg = blindwrite[i].reg; + val = blindwrite[i].def; + delay = blindwrite[i].delay_us; - if (reg == 0x3fc2dfc3) + if (reg == 0x1000cd56) rt1320_load_mcu_patch(rt1320); regmap_write(rt1320->regmap, reg, val); @@ -1888,6 +2451,24 @@ static int rt1320_io_init(struct device *dev, struct sdw_slave *slave) regmap_read(rt1320->regmap, RT1320_DEV_ID_0, &val); regmap_read(rt1320->regmap, RT1320_DEV_ID_1, &tmp); rt1320->dev_id = (val << 8) | tmp; + + /* This is a workaround that reads the value twice to obtain the correct result. */ + rt1320_pr_read(rt1320, RT1320_HV_DEV_ID_0, &val); + rt1320_pr_read(rt1320, RT1320_HV_DEV_ID_1, &tmp); + rt1320_pr_read(rt1320, RT1320_HV_DEV_ID_0, &val); + rt1320_pr_read(rt1320, RT1320_HV_DEV_ID_1, &tmp); + val = (val << 8) | tmp; + + if (rt1320->dev_id == RT1321_DEV_ID) { + if (rt1320->version_id == 0x01) + rt1320->version_id = RT1321_VA2; + else if (val == RT1321_DEV_HV_VA0_ID) + rt1320->version_id = RT1321_VA0; + else if (val == RT1321_DEV_HV_VA1_ID) + rt1320->version_id = RT1321_VA1; + else + dev_err(dev, "%s: Unknown version ID 0x%x for RT1321\n", __func__, rt1320->version_id); + } } regmap_read(rt1320->regmap, @@ -2440,7 +3021,7 @@ static int rt1320_dspfw_load_put(struct snd_kcontrol *kcontrol, if (!rt1320->hw_init) return 0; - ret = pm_runtime_resume(component->dev); + ret = pm_runtime_resume_and_get(component->dev); if (ret < 0 && ret != -EACCES) return ret; @@ -2451,6 +3032,8 @@ static int rt1320_dspfw_load_put(struct snd_kcontrol *kcontrol, if (!ucontrol->value.integer.value[0]) rt1320->fw_load_done = false; + pm_runtime_mark_last_busy(component->dev); + pm_runtime_put_autosuspend(component->dev); return 0; } diff --git a/sound/soc/codecs/rt1320-sdw.h b/sound/soc/codecs/rt1320-sdw.h index a7b573883dd0..bc0f78e03529 100644 --- a/sound/soc/codecs/rt1320-sdw.h +++ b/sound/soc/codecs/rt1320-sdw.h @@ -17,12 +17,17 @@ #define RT1320_DEV_ID 0x6981 #define RT1321_DEV_ID 0x7045 +#define RT1321_DEV_HV_VA0_ID 0x6997 +#define RT1321_DEV_HV_VA1_ID 0x7071 /* imp-defined registers */ #define RT1320_DEV_VERSION_ID_1 0xc404 #define RT1320_DEV_ID_1 0xc405 #define RT1320_DEV_ID_0 0xc406 +#define RT1320_HV_DEV_ID_0 0xf622 +#define RT1320_HV_DEV_ID_1 0xf623 + #define RT1320_POWER_STATE 0xc560 #define RT1321_PATCH_MAIN_VER 0x1000cffe @@ -94,6 +99,12 @@ enum rt1320_version_id { RT1320_VC, }; +enum rt1321_version_id { + RT1321_VA0, + RT1321_VA1, + RT1321_VA2, +}; + #define RT1320_VER_B_ID 0x07392238 #define RT1320_VAB_MCU_PATCH "realtek/rt1320/rt1320-patch-code-vab.bin" #define RT1320_VC_MCU_PATCH "realtek/rt1320/rt1320-patch-code-vc.bin" @@ -121,6 +132,7 @@ struct rt1320_datafixpoint { int invrs; }; +/* FW parameter id 1300 */ typedef struct FwPara_HwSwGain { unsigned int SwAdvGain; unsigned int SwBasGain; -- cgit v1.2.3 From 99e361b2a50424ffd923dc151fd605afa5b213ce Mon Sep 17 00:00:00 2001 From: Diogo Ivo Date: Sat, 20 Jun 2026 15:50:58 +0200 Subject: ASoC: rt5677: Add GPIO .get_direction() callback Implement the get_direction callback for the GPIO controller to allow consumers to query the direction of GPIO pins. Signed-off-by: Diogo Ivo Link: https://patch.msgid.link/20260620-smaug-audio-v1-1-e318acdf5abd@bootlin.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5677.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index ac084ca008f3..73fc008d558a 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -6,6 +6,7 @@ * Author: Oder Chiou */ +#include #include #include #include @@ -4767,6 +4768,21 @@ static int rt5677_gpio_direction_in(struct gpio_chip *chip, unsigned offset) return rt5677_update_gpio_bits(rt5677, offset, m, v); } +static int rt5677_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + struct rt5677_priv *rt5677 = gpiochip_get_data(chip); + unsigned int shift = RT5677_GPIOx_DIR_SFT + (offset % 5) * 3; + unsigned int bank = offset / 5; + unsigned int reg = bank ? RT5677_GPIO_CTRL3 : RT5677_GPIO_CTRL2; + int ret; + + ret = regmap_test_bits(rt5677->regmap, reg, BIT(shift)); + if (ret < 0) + return ret; + + return ret ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; +} + /* * Configures the GPIO as * 0 - floating @@ -4834,6 +4850,7 @@ static int rt5677_to_irq(struct gpio_chip *chip, unsigned offset) static const struct gpio_chip rt5677_template_chip = { .label = RT5677_DRV_NAME, .owner = THIS_MODULE, + .get_direction = rt5677_gpio_get_direction, .direction_output = rt5677_gpio_direction_out, .set = rt5677_gpio_set, .direction_input = rt5677_gpio_direction_in, -- cgit v1.2.3 From a5edc45d9cf6e60be1cbd39f68e92685a91b13ac Mon Sep 17 00:00:00 2001 From: Diogo Ivo Date: Sat, 20 Jun 2026 15:50:59 +0200 Subject: ASoC: rt5677: Enable standalone compilation for generic card use Add a prompt string to make the RT5677 driver user-selectable, allowing it to be built independently for use with generic sound card bindings. Signed-off-by: Diogo Ivo Link: https://patch.msgid.link/20260620-smaug-audio-v1-2-e318acdf5abd@bootlin.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 252f683be3c1..e9de333c5c8a 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1888,7 +1888,7 @@ config SND_SOC_RT5670 depends on I2C config SND_SOC_RT5677 - tristate + tristate "Realtek RT5677 Codec" depends on I2C select REGMAP_I2C select REGMAP_IRQ -- cgit v1.2.3 From a295be8d6210ab6a6ec9710b5b146cba34f36c6f Mon Sep 17 00:00:00 2001 From: Valerio Setti Date: Wed, 10 Jun 2026 23:29:25 +0200 Subject: ASoC: meson: gx: add gx-formatter and gx-interface These files are the basic block which allow to shape I2S in GX devices the same as the AXG ones: the DAI backend only controls the interface (i.e. clocks and pins) whereas a formatter takes care of properly formatting the data. gx-formatter and gx-interface are strongly inspired to axg-tdm-formatter and axg-tdm, respectively. The long term plan is to join the two platforms to use the same formatter solution. There is only a minor addition here compared to what has been done for AXG and it's "gx_formatter_create()" which is required in order to let already existing AIU code to make use of this formatter without making any devicetree change. Signed-off-by: Valerio Setti Reviewed-by: Jerome Brunet Link: https://patch.msgid.link/20260610-reshape-aiu-as-axg-v2-1-cac3663a8b51@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/Makefile | 1 + sound/soc/meson/gx-formatter.c | 282 +++++++++++++++++++++++++++++++++++++++++ sound/soc/meson/gx-formatter.h | 56 ++++++++ sound/soc/meson/gx-interface.h | 48 +++++++ 4 files changed, 387 insertions(+) create mode 100644 sound/soc/meson/gx-formatter.c create mode 100644 sound/soc/meson/gx-formatter.h create mode 100644 sound/soc/meson/gx-interface.h diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile index 24078e4396b0..146ec81526ba 100644 --- a/sound/soc/meson/Makefile +++ b/sound/soc/meson/Makefile @@ -4,6 +4,7 @@ snd-soc-meson-aiu-y := aiu.o snd-soc-meson-aiu-y += aiu-acodec-ctrl.o snd-soc-meson-aiu-y += aiu-codec-ctrl.o snd-soc-meson-aiu-y += aiu-encoder-i2s.o +snd-soc-meson-aiu-y += gx-formatter.o snd-soc-meson-aiu-y += aiu-encoder-spdif.o snd-soc-meson-aiu-y += aiu-fifo.o snd-soc-meson-aiu-y += aiu-fifo-i2s.o diff --git a/sound/soc/meson/gx-formatter.c b/sound/soc/meson/gx-formatter.c new file mode 100644 index 000000000000..311e63affb23 --- /dev/null +++ b/sound/soc/meson/gx-formatter.c @@ -0,0 +1,282 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// +// Copyright (c) 2026 BayLibre, SAS. +// Author: Valerio Setti + +#include +#include +#include +#include + +#include "gx-formatter.h" + +struct gx_formatter { + struct list_head list; + struct gx_stream *stream; + const struct gx_formatter_driver *drv; + bool enabled; + struct regmap *map; +}; + +static int gx_formatter_enable(struct gx_formatter *formatter) +{ + int ret; + + /* Do nothing if the formatter is already enabled */ + if (formatter->enabled) + return 0; + + /* Setup the stream parameter in the formatter */ + if (formatter->drv->ops->prepare) { + ret = formatter->drv->ops->prepare(formatter->map, + formatter->drv->quirks, + formatter->stream); + if (ret) + return ret; + } + + /* Finally, actually enable the formatter */ + if (formatter->drv->ops->enable) + formatter->drv->ops->enable(formatter->map); + + formatter->enabled = true; + + return 0; +} + +static void gx_formatter_disable(struct gx_formatter *formatter) +{ + /* Do nothing if the formatter is already disabled */ + if (!formatter->enabled) + return; + + if (formatter->drv->ops->disable) + formatter->drv->ops->disable(formatter->map); + + formatter->enabled = false; +} + +static int gx_formatter_attach(struct gx_formatter *formatter) +{ + struct gx_stream *ts = formatter->stream; + int ret = 0; + + mutex_lock(&ts->lock); + + /* Catch up if the stream is already running when we attach */ + if (ts->ready) { + ret = gx_formatter_enable(formatter); + if (ret) { + pr_err("failed to enable formatter\n"); + goto out; + } + } + + list_add_tail(&formatter->list, &ts->formatter_list); +out: + mutex_unlock(&ts->lock); + return ret; +} + +static void gx_formatter_detach(struct gx_formatter *formatter) +{ + struct gx_stream *ts = formatter->stream; + + if (!ts) + return; + + mutex_lock(&ts->lock); + list_del(&formatter->list); + mutex_unlock(&ts->lock); + + gx_formatter_disable(formatter); +} + +static int gx_formatter_power_up(struct gx_formatter *formatter, + struct snd_soc_dapm_widget *w) +{ + struct gx_stream *ts = formatter->drv->ops->get_stream(w); + int ret; + + /* + * If we don't get a stream at this stage, it would mean that the + * widget is powering up but is not attached to any backend DAI. + * It should not happen, ever ! + */ + if (WARN_ON(!ts)) + return -ENODEV; + + formatter->stream = ts; + INIT_LIST_HEAD(&formatter->list); + ret = gx_formatter_attach(formatter); + if (ret) + return ret; + + return 0; +} + +static void gx_formatter_power_down(struct gx_formatter *formatter) +{ + gx_formatter_detach(formatter); + formatter->stream = NULL; +} + +int gx_formatter_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *control, + int event) +{ + struct snd_soc_component *c; + struct gx_formatter *formatter; + int ret = 0; + + c = snd_soc_dapm_to_component(w->dapm); + + if (w->priv) + formatter = w->priv; + else + formatter = snd_soc_component_get_drvdata(c); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = gx_formatter_power_up(formatter, w); + break; + + case SND_SOC_DAPM_PRE_PMD: + gx_formatter_power_down(formatter); + break; + + default: + dev_err(c->dev, "Unexpected event %d\n", event); + return -EINVAL; + } + + return ret; +} +EXPORT_SYMBOL_GPL(gx_formatter_event); + +int gx_formatter_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct gx_formatter_driver *drv; + struct gx_formatter *formatter; + void __iomem *regs; + + drv = of_device_get_match_data(dev); + if (!drv) { + dev_err(dev, "failed to match device\n"); + return -ENODEV; + } + + formatter = devm_kzalloc(dev, sizeof(*formatter), GFP_KERNEL); + if (!formatter) + return -ENOMEM; + platform_set_drvdata(pdev, formatter); + formatter->drv = drv; + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + formatter->map = devm_regmap_init_mmio(dev, regs, drv->regmap_cfg); + if (IS_ERR(formatter->map)) { + dev_err(dev, "failed to init regmap: %ld\n", + PTR_ERR(formatter->map)); + return PTR_ERR(formatter->map); + } + + return devm_snd_soc_register_component(dev, drv->component_drv, + NULL, 0); +} +EXPORT_SYMBOL_GPL(gx_formatter_probe); + +int gx_formatter_create(struct device *dev, + struct snd_soc_dapm_widget *w, + const struct gx_formatter_driver *drv, + struct regmap *regmap) +{ + struct gx_formatter *formatter; + + formatter = devm_kzalloc(dev, sizeof(*formatter), GFP_KERNEL); + if (!formatter) + return -ENOMEM; + + formatter->drv = drv; + formatter->map = regmap; + + w->priv = formatter; + + return 0; +} +EXPORT_SYMBOL_GPL(gx_formatter_create); + +int gx_stream_start(struct gx_stream *ts) +{ + struct gx_formatter *formatter; + int ret = 0; + + mutex_lock(&ts->lock); + + /* Start all the formatters attached to the stream */ + list_for_each_entry(formatter, &ts->formatter_list, list) { + ret = gx_formatter_enable(formatter); + if (ret) { + pr_err("failed to enable formatter\n"); + goto out; + } + } + + ts->ready = true; + +out: + mutex_unlock(&ts->lock); + return ret; +} +EXPORT_SYMBOL_GPL(gx_stream_start); + +void gx_stream_stop(struct gx_stream *ts) +{ + struct gx_formatter *formatter; + + mutex_lock(&ts->lock); + ts->ready = false; + + /* Stop all the formatters attached to the stream */ + list_for_each_entry(formatter, &ts->formatter_list, list) { + gx_formatter_disable(formatter); + } + + mutex_unlock(&ts->lock); +} +EXPORT_SYMBOL_GPL(gx_stream_stop); + +struct gx_stream *gx_stream_alloc(struct gx_iface *iface) +{ + struct gx_stream *ts; + + ts = kzalloc(sizeof(*ts), GFP_KERNEL); + if (ts) { + INIT_LIST_HEAD(&ts->formatter_list); + mutex_init(&ts->lock); + ts->iface = iface; + } + + return ts; +} +EXPORT_SYMBOL_GPL(gx_stream_alloc); + +void gx_stream_free(struct gx_stream *ts) +{ + /* + * If the list is not empty, it would mean that one of the formatter + * widget is still powered and attached to the interface while we + * are removing the TDM DAI. It should not be possible + */ + WARN_ON(!list_empty(&ts->formatter_list)); + mutex_destroy(&ts->lock); + kfree(ts); +} +EXPORT_SYMBOL_GPL(gx_stream_free); + +MODULE_DESCRIPTION("Amlogic GX formatter driver"); +MODULE_AUTHOR("Valerio Setti "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/meson/gx-formatter.h b/sound/soc/meson/gx-formatter.h new file mode 100644 index 000000000000..b90b1814d79b --- /dev/null +++ b/sound/soc/meson/gx-formatter.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ +/* + * Copyright (c) 2026 Baylibre SAS. + * Author: Valerio Setti + */ + +#ifndef _MESON_GX_FORMATTER_H +#define _MESON_GX_FORMATTER_H + +#include "gx-interface.h" + +struct platform_device; +struct regmap; +struct snd_soc_dapm_widget; +struct snd_kcontrol; + +struct gx_formatter_hw { + unsigned int skew_offset; +}; + +struct gx_formatter_ops { + struct gx_stream *(*get_stream)(struct snd_soc_dapm_widget *w); + void (*enable)(struct regmap *map); + void (*disable)(struct regmap *map); + int (*prepare)(struct regmap *map, + const struct gx_formatter_hw *quirks, + struct gx_stream *ts); +}; + +struct gx_formatter_driver { + const struct snd_soc_component_driver *component_drv; + const struct regmap_config *regmap_cfg; + const struct gx_formatter_ops *ops; + const struct gx_formatter_hw *quirks; +}; + +int gx_formatter_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *control, + int event); +int gx_formatter_probe(struct platform_device *pdev); + +int gx_formatter_create(struct device *dev, + struct snd_soc_dapm_widget *w, + const struct gx_formatter_driver *drv, + struct regmap *regmap); + +/* + * Formatter data is already freed when the associated device is removed, + * so we just need to remove the pointer from the widget. + */ +static inline void gx_formatter_free(struct snd_soc_dapm_widget *w) +{ + w->priv = NULL; +} + +#endif /* _MESON_GX_FORMATTER_H */ diff --git a/sound/soc/meson/gx-interface.h b/sound/soc/meson/gx-interface.h new file mode 100644 index 000000000000..65c46dcce32a --- /dev/null +++ b/sound/soc/meson/gx-interface.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ +/* + * Copyright (c) 2026 Baylibre SAS. + * Author: Valerio Setti + */ + +#ifndef _MESON_GX_INTERFACE_H +#define _MESON_GX_INTERFACE_H + +#include +#include +#include +#include +#include + +struct gx_iface { + struct clk *mclk; + unsigned long mclk_rate; + + /* format is common to all the DAIs of the iface */ + unsigned int fmt; + + /* For component wide symmetry */ + int rate; + + /* Only for GX platform */ + int bs_quirk; +}; + +struct gx_stream { + struct gx_iface *iface; + struct list_head formatter_list; + struct mutex lock; + unsigned int channels; + unsigned int width; + unsigned int physical_width; + bool ready; + + /* For continuous clock tracking */ + bool clk_enabled; +}; + +struct gx_stream *gx_stream_alloc(struct gx_iface *iface); +void gx_stream_free(struct gx_stream *ts); +int gx_stream_start(struct gx_stream *ts); +void gx_stream_stop(struct gx_stream *ts); + +#endif /* _MESON_GX_INTERFACE_H */ -- cgit v1.2.3 From 9335117a221a7886bb6cac8a15905cf567d3413b Mon Sep 17 00:00:00 2001 From: Valerio Setti Date: Wed, 10 Jun 2026 23:29:26 +0200 Subject: ASoC: meson: aiu-encoder-i2s: prepare for multiple streams aiu-encoder-i2s is going to be the interface that handles both playback and capture, so this commit does all the required changes to prepare for that since so far it only handled playback: - probe/remove functions are added to allocate/free per stream data, respectively. - 'struc gx_iface' and 'struct gx_stream' are used to store interface or stream associated data, respecively. - interface wide rate symmetry is enforced. - quirks on bclk are also enforced if/when necessary. Clock-wise instead of bulk enabling all the clocks on startup and disabling them on shutdown, only the peripheral's internal ones are enabled/disabled in those functions, whereas MCLK and I2S clock divider are handled in prepare/hw_free. Finally a trigger() callback is also added to start/stop the associated I2S data formatter. Signed-off-by: Valerio Setti Reviewed-by: Jerome Brunet Link: https://patch.msgid.link/20260610-reshape-aiu-as-axg-v2-2-cac3663a8b51@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/aiu-encoder-i2s.c | 207 ++++++++++++++++++++++++++++++++++---- sound/soc/meson/aiu.h | 3 + 2 files changed, 193 insertions(+), 17 deletions(-) diff --git a/sound/soc/meson/aiu-encoder-i2s.c b/sound/soc/meson/aiu-encoder-i2s.c index 3b4061508c18..f50b03824ad2 100644 --- a/sound/soc/meson/aiu-encoder-i2s.c +++ b/sound/soc/meson/aiu-encoder-i2s.c @@ -10,6 +10,8 @@ #include #include "aiu.h" +#include "gx-formatter.h" +#include "gx-interface.h" #define AIU_I2S_SOURCE_DESC_MODE_8CH BIT(0) #define AIU_I2S_SOURCE_DESC_MODE_24BIT BIT(5) @@ -112,6 +114,9 @@ static int aiu_encoder_i2s_set_more_div(struct snd_soc_component *component, struct snd_pcm_hw_params *params, unsigned int bs) { + struct aiu *aiu = snd_soc_component_get_drvdata(component); + struct gx_iface *iface = &aiu->i2s.iface; + /* * NOTE: this HW is odd. * In most configuration, the i2s divider is 'mclk / blck'. @@ -126,6 +131,18 @@ static int aiu_encoder_i2s_set_more_div(struct snd_soc_component *component, return -EINVAL; } bs += bs / 2; + iface->bs_quirk = true; + } else { + /* + * If the bs quirk is currently applied for one stream and another + * ones tries to setup a configuration for which the quirk is + * not required, then fail. + */ + if (iface->bs_quirk) { + dev_err(component->dev, + "bclk requirements are incompatible with active stream\n"); + return -EINVAL; + } } /* Use CLK_MORE for mclk to bclk divider */ @@ -145,14 +162,15 @@ static int aiu_encoder_i2s_set_clocks(struct snd_soc_component *component, struct snd_pcm_hw_params *params) { struct aiu *aiu = snd_soc_component_get_drvdata(component); + struct gx_iface *iface = &aiu->i2s.iface; unsigned int srate = params_rate(params); unsigned int fs, bs; int ret; /* Get the oversampling factor */ - fs = DIV_ROUND_CLOSEST(clk_get_rate(aiu->i2s.clks[MCLK].clk), srate); + fs = DIV_ROUND_CLOSEST(iface->mclk_rate, srate); - if (fs % 64) + if ((fs % 64) || (fs == 0)) return -EINVAL; /* Send data MSB first */ @@ -188,24 +206,59 @@ static int aiu_encoder_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { + struct gx_stream *ts = snd_soc_dai_get_dma_data(dai, substream); + struct gx_iface *iface = ts->iface; struct snd_soc_component *component = dai->component; int ret; - /* Disable the clock while changing the settings */ - aiu_encoder_i2s_divider_enable(component, false); + /* + * Enforce interface wide rate symmetry only if there is more than + * 1 stream active. + */ + if (snd_soc_dai_active(dai) > 1) { + if (iface->rate && iface->rate != params_rate(params)) { + dev_err(dai->dev, "can't set iface rate (%d != %d)\n", + iface->rate, params_rate(params)); + return -EINVAL; + } + } ret = aiu_encoder_i2s_setup_desc(component, params); if (ret) { - dev_err(dai->dev, "setting i2s desc failed\n"); + dev_err(dai->dev, "setting i2s desc failed: %d\n", ret); return ret; } ret = aiu_encoder_i2s_set_clocks(component, params); if (ret) { - dev_err(dai->dev, "setting i2s clocks failed\n"); + dev_err(dai->dev, "setting i2s clocks failed: %d\n", ret); return ret; } + iface->rate = params_rate(params); + ts->physical_width = params_physical_width(params); + ts->width = params_width(params); + ts->channels = params_channels(params); + + return 0; +} + +static int aiu_encoder_i2s_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct gx_stream *ts = snd_soc_dai_get_dma_data(dai, substream); + struct snd_soc_component *component = dai->component; + int ret; + + if (ts->clk_enabled) + return 0; + + ret = clk_prepare_enable(ts->iface->mclk); + if (ret) + return ret; + + ts->clk_enabled = true; + aiu_encoder_i2s_divider_enable(component, true); return 0; @@ -214,9 +267,24 @@ static int aiu_encoder_i2s_hw_params(struct snd_pcm_substream *substream, static int aiu_encoder_i2s_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { + struct gx_stream *ts = snd_soc_dai_get_dma_data(dai, substream); + struct gx_iface *iface = ts->iface; struct snd_soc_component *component = dai->component; - aiu_encoder_i2s_divider_enable(component, false); + /* + * If this is the last substream being closed then disable the i2s + * clock divider and clear 'iface->rate'. + */ + if (snd_soc_dai_active(dai) <= 1) { + aiu_encoder_i2s_divider_enable(component, 0); + iface->rate = 0; + iface->bs_quirk = false; + } + + if (ts->clk_enabled) { + clk_disable_unprepare(ts->iface->mclk); + ts->clk_enabled = false; + } return 0; } @@ -224,6 +292,8 @@ static int aiu_encoder_i2s_hw_free(struct snd_pcm_substream *substream, static int aiu_encoder_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct snd_soc_component *component = dai->component; + struct aiu *aiu = snd_soc_component_get_drvdata(component); + struct gx_iface *iface = &aiu->i2s.iface; unsigned int inv = fmt & SND_SOC_DAIFMT_INV_MASK; unsigned int val = 0; unsigned int skew; @@ -255,9 +325,12 @@ static int aiu_encoder_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) skew = 0; break; default: + dev_err(dai->dev, "unsupported dai format\n"); return -EINVAL; } + iface->fmt = fmt; + val |= FIELD_PREP(AIU_CLK_CTRL_LRCLK_SKEW, skew); snd_soc_component_update_bits(component, AIU_CLK_CTRL, AIU_CLK_CTRL_LRCLK_INVERT | @@ -272,6 +345,7 @@ static int aiu_encoder_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); + struct gx_iface *iface = &aiu->i2s.iface; int ret; if (WARN_ON(clk_id != 0)) @@ -280,11 +354,15 @@ static int aiu_encoder_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, if (dir == SND_SOC_CLOCK_IN) return 0; - ret = clk_set_rate(aiu->i2s.clks[MCLK].clk, freq); - if (ret) - dev_err(dai->dev, "Failed to set sysclk to %uHz", freq); + ret = clk_set_rate(iface->mclk, freq); + if (ret) { + dev_err(dai->dev, "Failed to set sysclk to %uHz: %d", freq, ret); + return ret; + } - return ret; + iface->mclk_rate = freq; + + return 0; } static const unsigned int hw_channels[] = {2, 8}; @@ -305,15 +383,35 @@ static int aiu_encoder_i2s_startup(struct snd_pcm_substream *substream, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_channel_constraints); if (ret) { - dev_err(dai->dev, "adding channels constraints failed\n"); + dev_err(dai->dev, "adding channels constraints failed: %d\n", ret); return ret; } - ret = clk_bulk_prepare_enable(aiu->i2s.clk_num, aiu->i2s.clks); - if (ret) - dev_err(dai->dev, "failed to enable i2s clocks\n"); + /* + * Enable only clocks which are required for the interface internal + * logic. MCLK is enabled/disabled from the formatter and the I2S + * divider is enabled/disabled in "hw_params"/"hw_free", respectively. + */ + ret = clk_prepare_enable(aiu->i2s.clks[PCLK].clk); + if (ret) { + dev_err(dai->dev, "failed to enable PCLK: %d\n", ret); + return ret; + } + ret = clk_prepare_enable(aiu->i2s.clks[MIXER].clk); + if (ret) { + dev_err(dai->dev, "failed to enable MIXER: %d\n", ret); + clk_disable_unprepare(aiu->i2s.clks[PCLK].clk); + return ret; + } + ret = clk_prepare_enable(aiu->i2s.clks[AOCLK].clk); + if (ret) { + dev_err(dai->dev, "failed to enable AOCLK: %d\n", ret); + clk_disable_unprepare(aiu->i2s.clks[MIXER].clk); + clk_disable_unprepare(aiu->i2s.clks[PCLK].clk); + return ret; + } - return ret; + return 0; } static void aiu_encoder_i2s_shutdown(struct snd_pcm_substream *substream, @@ -321,14 +419,89 @@ static void aiu_encoder_i2s_shutdown(struct snd_pcm_substream *substream, { struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); - clk_bulk_disable_unprepare(aiu->i2s.clk_num, aiu->i2s.clks); + clk_disable_unprepare(aiu->i2s.clks[AOCLK].clk); + clk_disable_unprepare(aiu->i2s.clks[MIXER].clk); + clk_disable_unprepare(aiu->i2s.clks[PCLK].clk); +} + +static int aiu_encoder_i2s_trigger(struct snd_pcm_substream *substream, + int cmd, + struct snd_soc_dai *dai) +{ + struct gx_stream *ts = snd_soc_dai_get_dma_data(dai, substream); + int ret; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ret = gx_stream_start(ts); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_STOP: + gx_stream_stop(ts); + ret = 0; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int aiu_encoder_i2s_remove_dai(struct snd_soc_dai *dai) +{ + int stream; + + for_each_pcm_streams(stream) { + struct gx_stream *ts; + + ts = snd_soc_dai_dma_data_get(dai, stream); + if (ts) + gx_stream_free(ts); + + snd_soc_dai_dma_data_set(dai, stream, NULL); + } + + return 0; +} + +static int aiu_encoder_i2s_probe_dai(struct snd_soc_dai *dai) +{ + struct aiu *aiu = snd_soc_dai_get_drvdata(dai); + struct gx_iface *iface = &aiu->i2s.iface; + int stream; + + for_each_pcm_streams(stream) { + struct gx_stream *ts; + + if (!snd_soc_dai_get_widget(dai, stream)) + continue; + + ts = gx_stream_alloc(iface); + if (!ts) { + aiu_encoder_i2s_remove_dai(dai); + return -ENOMEM; + } + snd_soc_dai_dma_data_set(dai, stream, ts); + } + + iface->mclk = aiu->i2s.clks[MCLK].clk; + iface->mclk_rate = clk_get_rate(iface->mclk); + + return 0; } const struct snd_soc_dai_ops aiu_encoder_i2s_dai_ops = { + .probe = aiu_encoder_i2s_probe_dai, + .remove = aiu_encoder_i2s_remove_dai, .hw_params = aiu_encoder_i2s_hw_params, + .prepare = aiu_encoder_i2s_prepare, .hw_free = aiu_encoder_i2s_hw_free, .set_fmt = aiu_encoder_i2s_set_fmt, .set_sysclk = aiu_encoder_i2s_set_sysclk, .startup = aiu_encoder_i2s_startup, .shutdown = aiu_encoder_i2s_shutdown, + .trigger = aiu_encoder_i2s_trigger, }; diff --git a/sound/soc/meson/aiu.h b/sound/soc/meson/aiu.h index 0f94c8bf6081..68310de0bdf7 100644 --- a/sound/soc/meson/aiu.h +++ b/sound/soc/meson/aiu.h @@ -7,6 +7,8 @@ #ifndef _MESON_AIU_H #define _MESON_AIU_H +#include "gx-formatter.h" + struct clk; struct clk_bulk_data; struct device; @@ -25,6 +27,7 @@ struct aiu_interface { struct clk_bulk_data *clks; unsigned int clk_num; int irq; + struct gx_iface iface; }; struct aiu_platform_data { -- cgit v1.2.3 From 2cbb32d8dce0358a385760349fe19758a0d1a49e Mon Sep 17 00:00:00 2001 From: Valerio Setti Date: Wed, 10 Jun 2026 23:29:27 +0200 Subject: ASoC: meson: aiu: introduce I2S output formatter Introduce aiu-formatter-i2s, a gx_formatter implementation for the AIU I2S playback path. This is going to replace data formatting tasks that are currently being implemented in aiu-encoder-i2s. This should ideally follow the same design pattern used on the AXG platform (see axg-tdmout), where basically the widget/formatter corresponds to a single audio component. This is not possible in the GX platform though because all the features are currently implemented in the AIU audio component and changing that would require backward incompatible device-tree changes. Therefore aiu-formatter-i2s is kept very simple and it only implements the bare minimum functionalities to provide I2S playback formatting. It's not a standalone component though because this is still belongs to AIU. Signed-off-by: Valerio Setti Reviewed-by: Jerome Brunet Link: https://patch.msgid.link/20260610-reshape-aiu-as-axg-v2-3-cac3663a8b51@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/Makefile | 1 + sound/soc/meson/aiu-formatter-i2s.c | 104 ++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 sound/soc/meson/aiu-formatter-i2s.c diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile index 146ec81526ba..f9ec0ebb01f0 100644 --- a/sound/soc/meson/Makefile +++ b/sound/soc/meson/Makefile @@ -5,6 +5,7 @@ snd-soc-meson-aiu-y += aiu-acodec-ctrl.o snd-soc-meson-aiu-y += aiu-codec-ctrl.o snd-soc-meson-aiu-y += aiu-encoder-i2s.o snd-soc-meson-aiu-y += gx-formatter.o +snd-soc-meson-aiu-y += aiu-formatter-i2s.o snd-soc-meson-aiu-y += aiu-encoder-spdif.o snd-soc-meson-aiu-y += aiu-fifo.o snd-soc-meson-aiu-y += aiu-fifo-i2s.o diff --git a/sound/soc/meson/aiu-formatter-i2s.c b/sound/soc/meson/aiu-formatter-i2s.c new file mode 100644 index 000000000000..b4604734fe88 --- /dev/null +++ b/sound/soc/meson/aiu-formatter-i2s.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2026 BayLibre, SAS. +// Author: Valerio Setti + +#include +#include +#include + +#include "aiu.h" +#include "gx-formatter.h" + +#define AIU_I2S_SOURCE_DESC_MODE_8CH BIT(0) +#define AIU_I2S_SOURCE_DESC_MODE_24BIT BIT(5) +#define AIU_I2S_SOURCE_DESC_MODE_32BIT BIT(9) +#define AIU_RST_SOFT_I2S_FAST BIT(0) + +#define AIU_I2S_DAC_CFG_MSB_FIRST BIT(2) + +static struct snd_soc_dai * +aiu_formatter_i2s_get_be(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dapm_path *p; + struct snd_soc_dai *be; + + snd_soc_dapm_widget_for_each_sink_path(w, p) { + if (!p->connect) + continue; + + if (p->sink->id == snd_soc_dapm_dai_in) + return (struct snd_soc_dai *)p->sink->priv; + + be = aiu_formatter_i2s_get_be(p->sink); + if (be) + return be; + } + + return NULL; +} + +static struct gx_stream * +aiu_formatter_i2s_get_stream(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dai *be = aiu_formatter_i2s_get_be(w); + + if (!be) + return NULL; + + return snd_soc_dai_dma_data_get_playback(be); +} + +static int aiu_formatter_i2s_prepare(struct regmap *map, + const struct gx_formatter_hw *quirks, + struct gx_stream *ts) +{ + /* Always operate in split (classic interleaved) mode */ + unsigned int desc = 0; + unsigned int tmp; + + /* Reset required to update the pipeline */ + regmap_write(map, AIU_RST_SOFT, AIU_RST_SOFT_I2S_FAST); + regmap_read(map, AIU_I2S_SYNC, &tmp); + + switch (ts->physical_width) { + case 16: /* Nothing to do */ + break; + + case 32: + desc |= (AIU_I2S_SOURCE_DESC_MODE_24BIT | + AIU_I2S_SOURCE_DESC_MODE_32BIT); + break; + + default: + return -EINVAL; + } + + switch (ts->channels) { + case 2: /* Nothing to do */ + break; + case 8: + desc |= AIU_I2S_SOURCE_DESC_MODE_8CH; + break; + default: + return -EINVAL; + } + + regmap_update_bits(map, AIU_I2S_SOURCE_DESC, + AIU_I2S_SOURCE_DESC_MODE_8CH | + AIU_I2S_SOURCE_DESC_MODE_24BIT | + AIU_I2S_SOURCE_DESC_MODE_32BIT, + desc); + + /* Send data MSB first */ + regmap_update_bits(map, AIU_I2S_DAC_CFG, + AIU_I2S_DAC_CFG_MSB_FIRST, + AIU_I2S_DAC_CFG_MSB_FIRST); + + return 0; +} + +const struct gx_formatter_ops aiu_formatter_i2s_ops = { + .get_stream = aiu_formatter_i2s_get_stream, + .prepare = aiu_formatter_i2s_prepare, +}; -- cgit v1.2.3 From 83b83024cdbfdddee104f4a84c2c2f1f8e6659f3 Mon Sep 17 00:00:00 2001 From: Valerio Setti Date: Wed, 10 Jun 2026 23:29:28 +0200 Subject: ASoC: meson: aiu: use aiu-formatter-i2s to format I2S output data Create a new DAPM widget for "I2S formatter" and place it on the path between FIFO and output DAI interface. Remove I2S output formatting code from aiu-encoder-i2s since it's now implemented from aiu-formatter-i2s. Signed-off-by: Valerio Setti Reviewed-by: Jerome Brunet Link: https://patch.msgid.link/20260610-reshape-aiu-as-axg-v2-4-cac3663a8b51@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/aiu-encoder-i2s.c | 78 ++++++++++----------------------------- sound/soc/meson/aiu.c | 32 ++++++++++++++-- sound/soc/meson/aiu.h | 1 + 3 files changed, 48 insertions(+), 63 deletions(-) diff --git a/sound/soc/meson/aiu-encoder-i2s.c b/sound/soc/meson/aiu-encoder-i2s.c index f50b03824ad2..83b579e98f1c 100644 --- a/sound/soc/meson/aiu-encoder-i2s.c +++ b/sound/soc/meson/aiu-encoder-i2s.c @@ -13,13 +13,8 @@ #include "gx-formatter.h" #include "gx-interface.h" -#define AIU_I2S_SOURCE_DESC_MODE_8CH BIT(0) -#define AIU_I2S_SOURCE_DESC_MODE_24BIT BIT(5) -#define AIU_I2S_SOURCE_DESC_MODE_32BIT BIT(9) #define AIU_I2S_SOURCE_DESC_MODE_SPLIT BIT(11) -#define AIU_RST_SOFT_I2S_FAST BIT(0) -#define AIU_I2S_DAC_CFG_MSB_FIRST BIT(2) #define AIU_CLK_CTRL_I2S_DIV_EN BIT(0) #define AIU_CLK_CTRL_I2S_DIV GENMASK(3, 2) #define AIU_CLK_CTRL_AOCLK_INVERT BIT(6) @@ -37,49 +32,6 @@ static void aiu_encoder_i2s_divider_enable(struct snd_soc_component *component, enable ? AIU_CLK_CTRL_I2S_DIV_EN : 0); } -static int aiu_encoder_i2s_setup_desc(struct snd_soc_component *component, - struct snd_pcm_hw_params *params) -{ - /* Always operate in split (classic interleaved) mode */ - unsigned int desc = AIU_I2S_SOURCE_DESC_MODE_SPLIT; - - /* Reset required to update the pipeline */ - snd_soc_component_write(component, AIU_RST_SOFT, AIU_RST_SOFT_I2S_FAST); - snd_soc_component_read(component, AIU_I2S_SYNC); - - switch (params_physical_width(params)) { - case 16: /* Nothing to do */ - break; - - case 32: - desc |= (AIU_I2S_SOURCE_DESC_MODE_24BIT | - AIU_I2S_SOURCE_DESC_MODE_32BIT); - break; - - default: - return -EINVAL; - } - - switch (params_channels(params)) { - case 2: /* Nothing to do */ - break; - case 8: - desc |= AIU_I2S_SOURCE_DESC_MODE_8CH; - break; - default: - return -EINVAL; - } - - snd_soc_component_update_bits(component, AIU_I2S_SOURCE_DESC, - AIU_I2S_SOURCE_DESC_MODE_8CH | - AIU_I2S_SOURCE_DESC_MODE_24BIT | - AIU_I2S_SOURCE_DESC_MODE_32BIT | - AIU_I2S_SOURCE_DESC_MODE_SPLIT, - desc); - - return 0; -} - static int aiu_encoder_i2s_set_legacy_div(struct snd_soc_component *component, struct snd_pcm_hw_params *params, unsigned int bs) @@ -173,11 +125,6 @@ static int aiu_encoder_i2s_set_clocks(struct snd_soc_component *component, if ((fs % 64) || (fs == 0)) return -EINVAL; - /* Send data MSB first */ - snd_soc_component_update_bits(component, AIU_I2S_DAC_CFG, - AIU_I2S_DAC_CFG_MSB_FIRST, - AIU_I2S_DAC_CFG_MSB_FIRST); - /* Set bclk to lrlck ratio */ snd_soc_component_update_bits(component, AIU_CODEC_DAC_LRCLK_CTRL, AIU_CODEC_DAC_LRCLK_CTRL_DIV, @@ -223,12 +170,6 @@ static int aiu_encoder_i2s_hw_params(struct snd_pcm_substream *substream, } } - ret = aiu_encoder_i2s_setup_desc(component, params); - if (ret) { - dev_err(dai->dev, "setting i2s desc failed: %d\n", ret); - return ret; - } - ret = aiu_encoder_i2s_set_clocks(component, params); if (ret) { dev_err(dai->dev, "setting i2s clocks failed: %d\n", ret); @@ -411,6 +352,25 @@ static int aiu_encoder_i2s_startup(struct snd_pcm_substream *substream, return ret; } + /* + * We're always operating in split mode for the playback stream. + * + * This setting arguably belong to the 'aiu-formatter', but it's kept + * here for backward compatibility reason. At reset the I2S encoder + * operates in normal mode which would only support 8ch, but by default + * only 2ch are enabled. If a playback stream is started without + * changing to split mode, then the I2S encoder doesn't consume audio + * samples and the playback fails. + * Moving this to 'aiu-formatter' would cause the split mode to be set + * only when the formatter is enabled, which doesn't happen at boot as + * the default value for "HDMI CTRL SRC" is "DISABLED". + */ + ret = snd_soc_component_update_bits(dai->component, AIU_I2S_SOURCE_DESC, + AIU_I2S_SOURCE_DESC_MODE_SPLIT, + AIU_I2S_SOURCE_DESC_MODE_SPLIT); + if (ret < 0) + dev_err(dai->dev, "failed to update AIU_I2S_SOURCE_DESC: %d", ret); + return 0; } diff --git a/sound/soc/meson/aiu.c b/sound/soc/meson/aiu.c index f2890111c1d2..64ace4d25d92 100644 --- a/sound/soc/meson/aiu.c +++ b/sound/soc/meson/aiu.c @@ -29,13 +29,22 @@ static SOC_ENUM_SINGLE_DECL(aiu_spdif_encode_sel_enum, AIU_I2S_MISC, static const struct snd_kcontrol_new aiu_spdif_encode_mux = SOC_DAPM_ENUM("SPDIF Buffer Src", aiu_spdif_encode_sel_enum); -static const struct snd_soc_dapm_widget aiu_cpu_dapm_widgets[] = { - SND_SOC_DAPM_MUX("SPDIF SRC SEL", SND_SOC_NOPM, 0, 0, - &aiu_spdif_encode_mux), +#define AIU_WIDGET_SPDIF_SRC_SEL 0 +#define AIU_WIDGET_I2S_FORMATTER 1 + +static struct snd_soc_dapm_widget aiu_cpu_dapm_widgets[] = { + [AIU_WIDGET_SPDIF_SRC_SEL] = + SND_SOC_DAPM_MUX("SPDIF SRC SEL", SND_SOC_NOPM, 0, 0, + &aiu_spdif_encode_mux), + [AIU_WIDGET_I2S_FORMATTER] = + SND_SOC_DAPM_PGA_E("I2S Formatter", SND_SOC_NOPM, 0, 0, NULL, 0, + gx_formatter_event, + (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD)), }; static const struct snd_soc_dapm_route aiu_cpu_dapm_routes[] = { - { "I2S Encoder Playback", NULL, "I2S FIFO Playback" }, + { "I2S Formatter", NULL, "I2S FIFO Playback" }, + { "I2S Encoder Playback", NULL, "I2S Formatter" }, { "SPDIF SRC SEL", "SPDIF", "SPDIF FIFO Playback" }, { "SPDIF SRC SEL", "I2S", "I2S FIFO Playback" }, { "SPDIF Encoder Playback", NULL, "SPDIF SRC SEL" }, @@ -172,6 +181,11 @@ static const struct regmap_config aiu_regmap_cfg = { .max_register = 0x2ac, }; +const struct gx_formatter_driver aiu_formatter_i2s_drv = { + .regmap_cfg = &aiu_regmap_cfg, + .ops = &aiu_formatter_i2s_ops, +}; + static int aiu_clk_bulk_get(struct device *dev, const char * const *ids, unsigned int num, @@ -282,6 +296,14 @@ static int aiu_probe(struct platform_device *pdev) if (ret) return ret; + /* Allocate the aiu-formatter into its widget */ + ret = gx_formatter_create(dev, &aiu_cpu_dapm_widgets[AIU_WIDGET_I2S_FORMATTER], + &aiu_formatter_i2s_drv, map); + if (ret) { + dev_err(dev, "Failed to allocate aiu formatter\n"); + goto err; + } + /* Register the cpu component of the aiu */ ret = snd_soc_register_component(dev, &aiu_cpu_component, aiu_cpu_dai_drv, @@ -310,12 +332,14 @@ static int aiu_probe(struct platform_device *pdev) return 0; err: + gx_formatter_free(&aiu_cpu_dapm_widgets[AIU_WIDGET_I2S_FORMATTER]); snd_soc_unregister_component(dev); return ret; } static void aiu_remove(struct platform_device *pdev) { + gx_formatter_free(&aiu_cpu_dapm_widgets[AIU_WIDGET_I2S_FORMATTER]); snd_soc_unregister_component(&pdev->dev); } diff --git a/sound/soc/meson/aiu.h b/sound/soc/meson/aiu.h index 68310de0bdf7..7d0b98c1f351 100644 --- a/sound/soc/meson/aiu.h +++ b/sound/soc/meson/aiu.h @@ -61,6 +61,7 @@ extern const struct snd_soc_dai_ops aiu_fifo_i2s_dai_ops; extern const struct snd_soc_dai_ops aiu_fifo_spdif_dai_ops; extern const struct snd_soc_dai_ops aiu_encoder_i2s_dai_ops; extern const struct snd_soc_dai_ops aiu_encoder_spdif_dai_ops; +extern const struct gx_formatter_ops aiu_formatter_i2s_ops; #define AIU_IEC958_BPF 0x000 #define AIU_958_MISC 0x010 -- cgit v1.2.3 From b2133ca33e66dafe948c5a219e7f40b2ccd07881 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 26 Jun 2026 01:08:07 +0000 Subject: ASoC: au1x: psc-ac97: remove unused chans chans is not used in au1xpsc_ac97_hw_params(). Remove it. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87y0g2ds7c.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/au1x/psc-ac97.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c index 94698e08a513..0f9d80883116 100644 --- a/sound/soc/au1x/psc-ac97.c +++ b/sound/soc/au1x/psc-ac97.c @@ -210,9 +210,7 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream, { struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai); unsigned long r, ro, stat; - int chans, t, stype = substream->stream; - - chans = params_channels(params); + int t, stype = substream->stream; r = ro = __raw_readl(AC97_CFG(pscdata)); stat = __raw_readl(AC97_STAT(pscdata)); -- cgit v1.2.3 From c97f0bf5f705b16d150f2b0d5ce0ee24eee4f68a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 26 Jun 2026 05:45:10 +0000 Subject: ASoC: sdw_utils: tidyup .count_sidecar count_sidecar() is not using *card. Tidyup it. Current code makes old style / new style conversion difficult. To make future conversions easier to understand, this patch clean up the code a little. but no functional change. Signed-off-by: Kuninori Morimoto Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/87jyrlety1.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc_sdw_utils.h | 28 ++++++++++++++-------------- sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c | 4 +--- sound/soc/sdw_utils/soc_sdw_utils.c | 2 +- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/include/sound/soc_sdw_utils.h b/include/sound/soc_sdw_utils.h index 79c21966220b..443d63dc6ea3 100644 --- a/include/sound/soc_sdw_utils.h +++ b/include/sound/soc_sdw_utils.h @@ -44,6 +44,18 @@ struct asoc_sdw_codec_info; +struct asoc_sdw_mc_private { + struct snd_soc_card card; + struct snd_soc_jack sdw_headset; + struct device *headset_codec_dev; /* only one headset per card */ + struct device *amp_dev1, *amp_dev2; + bool append_dai_type; + bool ignore_internal_dmic; + void *private; + unsigned long mc_quirk; + int codec_info_list_count; +}; + struct asoc_sdw_dai_info { const bool direction[2]; /* playback & capture support */ const char *codec_name; @@ -88,25 +100,13 @@ struct asoc_sdw_codec_info { int (*codec_card_late_probe)(struct snd_soc_card *card); - int (*count_sidecar)(struct snd_soc_card *card, + int (*count_sidecar)(struct asoc_sdw_mc_private *ctx, int *num_dais, int *num_devs); int (*add_sidecar)(struct snd_soc_card *card, struct snd_soc_dai_link **dai_links, struct snd_soc_codec_conf **codec_conf); }; -struct asoc_sdw_mc_private { - struct snd_soc_card card; - struct snd_soc_jack sdw_headset; - struct device *headset_codec_dev; /* only one headset per card */ - struct device *amp_dev1, *amp_dev2; - bool append_dai_type; - bool ignore_internal_dmic; - void *private; - unsigned long mc_quirk; - int codec_info_list_count; -}; - struct asoc_sdw_endpoint { struct list_head list; @@ -235,7 +235,7 @@ int asoc_sdw_es9356_amp_init(struct snd_soc_card *card, int asoc_sdw_es9356_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); /* CS AMP support */ -int asoc_sdw_bridge_cs35l56_count_sidecar(struct snd_soc_card *card, +int asoc_sdw_bridge_cs35l56_count_sidecar(struct asoc_sdw_mc_private *ctx, int *num_dais, int *num_devs); int asoc_sdw_bridge_cs35l56_add_sidecar(struct snd_soc_card *card, struct snd_soc_dai_link **dai_links, diff --git a/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c b/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c index e0e32a279787..129a437ae397 100644 --- a/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c +++ b/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c @@ -99,11 +99,9 @@ static const struct snd_soc_dai_link bridge_dai_template = { SND_SOC_DAILINK_REG(asoc_sdw_bridge_dai), }; -int asoc_sdw_bridge_cs35l56_count_sidecar(struct snd_soc_card *card, +int asoc_sdw_bridge_cs35l56_count_sidecar(struct asoc_sdw_mc_private *ctx, int *num_dais, int *num_devs) { - struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); - if (ctx->mc_quirk & SOC_SDW_SIDECAR_AMPS) { (*num_dais)++; (*num_devs) += ARRAY_SIZE(bridge_cs35l56_name_prefixes); diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index d8db8fc5313e..073f3f9205a7 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -2045,7 +2045,7 @@ int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, ctx->ignore_internal_dmic |= codec_info->ignore_internal_dmic; if (codec_info->count_sidecar && codec_info->add_sidecar) { - ret = codec_info->count_sidecar(card, &num_dais, num_devs); + ret = codec_info->count_sidecar(ctx, &num_dais, num_devs); if (ret) return ret; -- cgit v1.2.3 From a1332be2a07090cf422507ec812ce2b9ba0a558a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 26 Jun 2026 05:45:15 +0000 Subject: ASoC: sdw_utils: tidyup asoc_sdw_parse_sdw_endpoints() We can avoid to use *card. Tidyup it. Current code makes old style / new style conversion difficult. To make future conversions easier to understand, this patch clean up the code a little. but no functional change. Signed-off-by: Kuninori Morimoto Reviewed-by: Cezary Rojewski Reviewed-by: Vijendar Mukunda Link: https://patch.msgid.link/87ik75etxw.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc_sdw_utils.h | 3 ++- sound/soc/amd/acp/acp-sdw-legacy-mach.c | 2 +- sound/soc/amd/acp/acp-sdw-sof-mach.c | 2 +- sound/soc/intel/boards/sof_sdw.c | 2 +- sound/soc/sdw_utils/soc_sdw_utils.c | 5 ++--- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/sound/soc_sdw_utils.h b/include/sound/soc_sdw_utils.h index 443d63dc6ea3..9b28e9aef4f1 100644 --- a/include/sound/soc_sdw_utils.h +++ b/include/sound/soc_sdw_utils.h @@ -182,7 +182,8 @@ struct asoc_sdw_dailink *asoc_sdw_find_dailink(struct asoc_sdw_dailink *dailinks const struct snd_soc_acpi_endpoint *new); int asoc_sdw_get_dai_type(u32 type); -int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, +int asoc_sdw_parse_sdw_endpoints(struct device *dev, + struct asoc_sdw_mc_private *ctx, struct snd_soc_aux_dev *soc_aux, struct asoc_sdw_dailink *soc_dais, struct asoc_sdw_endpoint *soc_ends, diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c index e8b6819cc4b4..9726a9d33ec6 100644 --- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c +++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c @@ -432,7 +432,7 @@ static int soc_card_dai_links_create(struct snd_soc_card *card) if (!soc_aux) return -ENOMEM; - ret = asoc_sdw_parse_sdw_endpoints(card, soc_aux, soc_dais, soc_ends, &num_confs); + ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, soc_aux, soc_dais, soc_ends, &num_confs); if (ret < 0) return ret; diff --git a/sound/soc/amd/acp/acp-sdw-sof-mach.c b/sound/soc/amd/acp/acp-sdw-sof-mach.c index a423853f3a97..963ce6fd4012 100644 --- a/sound/soc/amd/acp/acp-sdw-sof-mach.c +++ b/sound/soc/amd/acp/acp-sdw-sof-mach.c @@ -303,7 +303,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) if (!sof_aux) return -ENOMEM; - ret = asoc_sdw_parse_sdw_endpoints(card, sof_aux, sof_dais, sof_ends, &num_devs); + ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_devs); if (ret < 0) return ret; diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index d43daf9b025d..24226a387cc2 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -1285,7 +1285,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) goto err_dai; } - ret = asoc_sdw_parse_sdw_endpoints(card, sof_aux, sof_dais, sof_ends, &num_confs); + ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_confs); if (ret < 0) goto err_end; diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index 073f3f9205a7..dd2cc57059d6 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -1976,14 +1976,13 @@ put_device: return ret; } -int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, +int asoc_sdw_parse_sdw_endpoints(struct device *dev, + struct asoc_sdw_mc_private *ctx, struct snd_soc_aux_dev *soc_aux, struct asoc_sdw_dailink *soc_dais, struct asoc_sdw_endpoint *soc_ends, int *num_devs) { - struct device *dev = card->dev; - struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); struct snd_soc_acpi_mach *mach = dev_get_platdata(dev); struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params; const struct snd_soc_acpi_link_adr *adr_link; -- cgit v1.2.3 From 2f82d58a87d707c54ba649a4f71b9e7bc9c56f47 Mon Sep 17 00:00:00 2001 From: Julian Braha Date: Tue, 30 Jun 2026 09:52:49 +0100 Subject: ASoC: codecs: cleanup kconfig indentations Cleanup various bad indentations in the kconfig: 1. spaces instead of tabs (this file mostly uses tabs) 2. too much indentation 3. not enough indentation Signed-off-by: Julian Braha Link: https://patch.msgid.link/20260630085249.380365-1-julianbraha@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index e9de333c5c8a..f90a0d4c77ea 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -615,8 +615,8 @@ config SND_SOC_AK4613 depends on I2C config SND_SOC_AK4619 - tristate "AKM AK4619 CODEC" - depends on I2C + tristate "AKM AK4619 CODEC" + depends on I2C config SND_SOC_AK4642 tristate "AKM AK4642 CODEC" @@ -1201,16 +1201,16 @@ config SND_SOC_JZ4725B_CODEC will be called snd-soc-jz4725b-codec. config SND_SOC_JZ4760_CODEC - depends on MACH_INGENIC || COMPILE_TEST - depends on OF - select REGMAP - tristate "Ingenic JZ4760 internal CODEC" - help - Enable support for the internal CODEC found in the JZ4760 SoC - from Ingenic. + depends on MACH_INGENIC || COMPILE_TEST + depends on OF + select REGMAP + tristate "Ingenic JZ4760 internal CODEC" + help + Enable support for the internal CODEC found in the JZ4760 SoC + from Ingenic. - This driver can also be built as a module. If so, the module - will be called snd-soc-jz4760-codec. + This driver can also be built as a module. If so, the module + will be called snd-soc-jz4760-codec. config SND_SOC_JZ4770_CODEC depends on MACH_INGENIC || COMPILE_TEST @@ -2296,8 +2296,8 @@ config SND_SOC_TLV320ADC3XXX depends on I2C depends on GPIOLIB help - Enable support for Texas Instruments TLV320ADC3001 and TLV320ADC3101 - ADCs. + Enable support for Texas Instruments TLV320ADC3001 and TLV320ADC3101 + ADCs. config SND_SOC_TLV320AIC23 tristate @@ -2893,7 +2893,7 @@ config SND_SOC_TPA6130A2 depends on I2C config SND_SOC_LPASS_MACRO_COMMON - tristate + tristate config SND_SOC_LPASS_WSA_MACRO depends on COMMON_CLK -- cgit v1.2.3 From 3848617c64ac5bf71e02f437e1974720d78843ca Mon Sep 17 00:00:00 2001 From: Narasimharao Vadlamudi Date: Tue, 30 Jun 2026 22:43:33 +0530 Subject: ASoC: renesas: fsi: Propagate platform_get_irq() errors platform_get_irq() returns a positive IRQ number on success and a negative error code on failure. It no longer returns zero. The driver currently stores the return value in an unsigned int and returns -ENODEV for all failures, which loses useful errors such as -EPROBE_DEFER. Store the IRQ in an int and return the error from platform_get_irq() directly. Acked-by: Kuninori Morimoto Reviewed-by: Geert Uytterhoeven Signed-off-by: Narasimharao Vadlamudi Link: https://patch.msgid.link/20260630171333.36396-1-ahmisaranrao@gmail.com Signed-off-by: Mark Brown --- sound/soc/renesas/fsi.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sound/soc/renesas/fsi.c b/sound/soc/renesas/fsi.c index ae86014c3819..6be6587e1095 100644 --- a/sound/soc/renesas/fsi.c +++ b/sound/soc/renesas/fsi.c @@ -1992,7 +1992,7 @@ static int fsi_probe(struct platform_device *pdev) const struct fsi_core *core; struct fsi_priv *fsi; struct resource *res; - unsigned int irq; + int irq; int ret; memset(&info, 0, sizeof(info)); @@ -2007,12 +2007,15 @@ static int fsi_probe(struct platform_device *pdev) } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - irq = platform_get_irq(pdev, 0); - if (!res || (int)irq <= 0) { + if (!res) { dev_err(&pdev->dev, "Not enough FSI platform resources.\n"); return -ENODEV; } + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL); if (!master) return -ENOMEM; -- cgit v1.2.3 From ae2e2f1ff1e80f88e5720a3c642992d182adb025 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Mon, 29 Jun 2026 18:30:24 -0700 Subject: ASoC: mediatek: mt2701: add COMPILE_TEST Enable COMPILE_TEST for mt2701 to get extra build coverage as done with other mediatek platforms. Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260630013024.1500623-1-rosenp@gmail.com Signed-off-by: Mark Brown --- sound/soc/mediatek/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig index 4af7bbb58010..224746e7664f 100644 --- a/sound/soc/mediatek/Kconfig +++ b/sound/soc/mediatek/Kconfig @@ -7,7 +7,7 @@ config SND_SOC_MEDIATEK config SND_SOC_MT2701 tristate "ASoC support for Mediatek MT2701 chip" - depends on ARCH_MEDIATEK + depends on ARCH_MEDIATEK || COMPILE_TEST select SND_SOC_MEDIATEK help This adds ASoC driver for Mediatek MT2701 boards -- cgit v1.2.3 From bff7fad1010eea6f183fb110b54171cf8700ef8e Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Wed, 24 Jun 2026 23:10:02 +0200 Subject: ASoC: dt-bindings: Convert cirrus,cs35l36 to DT schema Convert CS35L36 Speaker Amplifier to yaml. Changes: - maintainers email to the generic Cirrus email - Both the codec and downstream worked just fine without VP-supply provided. Align with datasheet for similar models. - add dai-common.yaml to cover for '#sound-dai-cells', 'sound-name-prefix' - updated not yet implemented: cirrus,weak-fet-delay -> cirrus,classh-wk-fet-delay-ms (in both definition and example) cirrus,weak-fet-thld -> cirrus,weak-fet-thld-millivolt (only in the example) - added two required properties: cirrus,boost-ctl-millivolt cirrus,boost-peak-milliamp Assisted-by: OpenAI:gpt-4 Reviewed-by: David Rhodes Co-developed-by: Rob Herring (Arm) Signed-off-by: Rob Herring (Arm) Signed-off-by: David Heidelberg Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260624-dt-cirrus-cs35l36-v3-1-ec451d5a2908@ixit.cz Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/cirrus,cs35l36.yaml | 240 +++++++++++++++++++++ .../devicetree/bindings/sound/cs35l36.txt | 168 --------------- 2 files changed, 240 insertions(+), 168 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/cirrus,cs35l36.yaml delete mode 100644 Documentation/devicetree/bindings/sound/cs35l36.txt diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs35l36.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs35l36.yaml new file mode 100644 index 000000000000..2a142b32acf5 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/cirrus,cs35l36.yaml @@ -0,0 +1,240 @@ +# SPDX-License-Identifier: GPL-2.0-only +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/cirrus,cs35l36.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cirrus Logic CS35L36 Speaker Amplifier + +maintainers: + - David Rhodes + - patches@opensource.cirrus.com + +description: + CS35L36 is a boosted mono Class D amplifier + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + enum: + - cirrus,cs35l36 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + VA-supply: + description: Voltage regulator of analog internal section + + VP-supply: + description: Voltage regulator of boost converter + + reset-gpios: + maxItems: 1 + + cirrus,boost-ctl-millivolt: + description: Boost converter output voltage (step 50) + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 2550 + maximum: 12000 + + cirrus,boost-peak-milliamp: + description: Boost-converter peak current limit (step 50) + $ref: /schemas/types.yaml#/definitions/uint32 + default: 4500 + minimum: 1600 + maximum: 4500 + + cirrus,boost-ind-nanohenry: + description: Initial inductor estimation reference value (1000=1μH, 1200=1.2μH) + $ref: /schemas/types.yaml#/definitions/uint32 + default: 1000 + + cirrus,multi-amp-mode: + description: Hi-Z ASP port when more than one amplifier in system + type: boolean + + cirrus,boost-ctl-select: + description: Boost converter control source selection + $ref: /schemas/types.yaml#/definitions/uint32 + default: 1 + enum: + - 0 # Control Port + - 1 # Class + - 2 # Sync + + cirrus,amp-pcm-inv: + description: Invert incoming PCM data when true + type: boolean + + cirrus,imon-pol-inv: + description: Invert polarity of outbound IMON feedback when true + type: boolean + + cirrus,vmon-pol-inv: + description: Invert polarity of outbound VMON feedback when true + type: boolean + + cirrus,dcm-mode-enable: + description: Enable boost converter automatic Discontinuous Conduction Mode + type: boolean + + cirrus,weak-fet-disable: + description: Reduce output driver strength in Weak-FET Drive Mode when true + type: boolean + + cirrus,classh-wk-fet-delay-ms: + description: Weak-FET entry delay + default: 100 + enum: [0, 5, 10, 50, 100, 200, 500, 1000] + + cirrus,classh-weak-fet-thld-millivolt: + description: Weak-FET drive threshold + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [50, 100, 150, 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700] + + cirrus,temp-warn-threshold: + description: Overtemperature warning threshold + $ref: /schemas/types.yaml#/definitions/uint32 + default: 2 + enum: + - 0 # 105°C + - 1 # 115°C + - 2 # 125°C + - 3 # 135°C + + cirrus,irq-drive-select: + description: Interrupt output driver type + $ref: /schemas/types.yaml#/definitions/uint32 + default: 1 + enum: + - 0 # open-drain + - 1 # push-pull + + cirrus,irq-gpio-select: + description: Programmable IRQ pin selection + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 0 # PDM_DATA/SWIRE_SD/INT + - 1 # GPIO + + cirrus,vpbr-config: + $ref: "#/$defs/vpbr-config" + +$defs: + vpbr-config: + description: Brownout prevention configuration sub-node + type: object + additionalProperties: false + + properties: + cirrus,vpbr-en: + description: VBST brownout prevention enable + $ref: /schemas/types.yaml#/definitions/uint32 + default: 0 + enum: + - 0 # disabled + - 1 # enabled + + cirrus,vpbr-thld: + description: Initial VPBR threshold voltage + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 2 + maximum: 31 + + cirrus,vpbr-atk-rate: + description: Attenuation attack step rate + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 7 + + cirrus,vpbr-atk-vol: + description: VP brownout prevention step size + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 7 + + cirrus,vpbr-max-attn: + description: Maximum attenuation during VP brownout prevention (dB) + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 15 + + cirrus,vpbr-wait: + description: Delay between brownout clearance and attenuation release (ms) + $ref: /schemas/types.yaml#/definitions/uint32 + default: 1 + enum: + - 0 # 10 + - 1 # 100 + - 2 # 250 + - 3 # 500 + + cirrus,vpbr-rel-rate: + description: Attenuation release step rate + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 7 + + cirrus,vpbr-mute-en: + description: Mute audio if maximum attenuation reached + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 1 + +required: + - compatible + - reg + - interrupts + - VA-supply + - cirrus,boost-ctl-millivolt + - cirrus,boost-peak-milliamp + +unevaluatedProperties: false + +examples: + - | + #include + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + codec@40 { + compatible = "cirrus,cs35l36"; + reg = <0x40>; + VA-supply = <&dummy_vreg>; + VP-supply = <&dummy_vreg>; + reset-gpios = <&gpio0 54 GPIO_ACTIVE_HIGH>; + interrupts = <3 IRQ_TYPE_LEVEL_LOW>; + + cirrus,boost-ind-nanohenry = <1000>; + cirrus,boost-ctl-millivolt = <10000>; + cirrus,boost-peak-milliamp = <4500>; + cirrus,boost-ctl-select = <0>; + cirrus,classh-wk-fet-delay-ms = <100>; + cirrus,classh-weak-fet-thld-millivolt = <100>; + cirrus,temp-warn-threshold = <1>; + cirrus,multi-amp-mode; + cirrus,irq-drive-select = <1>; + cirrus,irq-gpio-select = <1>; + + cirrus,vpbr-config { + cirrus,vpbr-en = <0>; + cirrus,vpbr-thld = <5>; + cirrus,vpbr-atk-rate = <2>; + cirrus,vpbr-atk-vol = <1>; + cirrus,vpbr-max-attn = <9>; + cirrus,vpbr-wait = <1>; + cirrus,vpbr-rel-rate = <5>; + cirrus,vpbr-mute-en = <0>; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/cs35l36.txt b/Documentation/devicetree/bindings/sound/cs35l36.txt deleted file mode 100644 index d34117b8558e..000000000000 --- a/Documentation/devicetree/bindings/sound/cs35l36.txt +++ /dev/null @@ -1,168 +0,0 @@ -CS35L36 Speaker Amplifier - -Required properties: - - - compatible : "cirrus,cs35l36" - - - reg : the I2C address of the device for I2C - - - VA-supply, VP-supply : power supplies for the device, - as covered in - Documentation/devicetree/bindings/regulator/regulator.txt. - - - cirrus,boost-ctl-millivolt : Boost Voltage Value. Configures the boost - converter's output voltage in mV. The range is from 2550mV to 12000mV with - increments of 50mV. - (Default) VP - - - cirrus,boost-peak-milliamp : Boost-converter peak current limit in mA. - Configures the peak current by monitoring the current through the boost FET. - Range starts at 1600mA and goes to a maximum of 4500mA with increments of - 50mA. - (Default) 4.50 Amps - - - cirrus,boost-ind-nanohenry : Inductor estimation LBST reference value. - Seeds the digital boost converter's inductor estimation block with the initial - inductance value to reference. - - 1000 = 1uH (Default) - 1200 = 1.2uH - -Optional properties: - - cirrus,multi-amp-mode : Boolean to determine if there are more than - one amplifier in the system. If more than one it is best to Hi-Z the ASP - port to prevent bus contention on the output signal - - - cirrus,boost-ctl-select : Boost converter control source selection. - Selects the source of the BST_CTL target VBST voltage for the boost - converter to generate. - 0x00 - Control Port Value - 0x01 - Class H Tracking (Default) - 0x10 - MultiDevice Sync Value - - - cirrus,amp-pcm-inv : Boolean to determine Amplifier will invert incoming - PCM data - - - cirrus,imon-pol-inv : Boolean to determine Amplifier will invert the - polarity of outbound IMON feedback data - - - cirrus,vmon-pol-inv : Boolean to determine Amplifier will invert the - polarity of outbound VMON feedback data - - - cirrus,dcm-mode-enable : Boost converter automatic DCM Mode enable. - This enables the digital boost converter to operate in a low power - (Discontinuous Conduction) mode during low loading conditions. - - - cirrus,weak-fet-disable : Boolean : The strength of the output drivers is - reduced when operating in a Weak-FET Drive Mode and must not be used to drive - a large load. - - - cirrus,classh-wk-fet-delay : Weak-FET entry delay. Controls the delay - (in ms) before the Class H algorithm switches to the weak-FET voltage - (after the audio falls and remains below the value specified in WKFET_AMP_THLD). - - 0 = 0ms - 1 = 5ms - 2 = 10ms - 3 = 50ms - 4 = 100ms (Default) - 5 = 200ms - 6 = 500ms - 7 = 1000ms - - - cirrus,classh-weak-fet-thld-millivolt : Weak-FET amplifier drive threshold. - Configures the signal threshold at which the PWM output stage enters - weak-FET operation. The range is 50mV to 700mV in 50mV increments. - - - cirrus,temp-warn-threshold : Amplifier overtemperature warning threshold. - Configures the threshold at which the overtemperature warning condition occurs. - When the threshold is met, the overtemperature warning attenuation is applied - and the TEMP_WARN_EINT interrupt status bit is set. - If TEMP_WARN_MASK = 0, INTb is asserted. - - 0 = 105C - 1 = 115C - 2 = 125C (Default) - 3 = 135C - - - cirrus,irq-drive-select : Selects the driver type of the selected interrupt - output. - - 0 = Open-drain - 1 = Push-pull (Default) - - - cirrus,irq-gpio-select : Selects the pin to serve as the programmable - interrupt output. - - 0 = PDM_DATA / SWIRE_SD / INT (Default) - 1 = GPIO - -Optional properties for the "cirrus,vpbr-config" Sub-node - - - cirrus,vpbr-en : VBST brownout prevention enable. Configures whether the - VBST brownout prevention algorithm is enabled or disabled. - - 0 = VBST brownout prevention disabled (default) - 1 = VBST brownout prevention enabled - - See Section 7.31.1 VPBR Config for configuration options & further details - - - cirrus,vpbr-thld : Initial VPBR threshold. Configures the VP brownout - threshold voltage - - - cirrus,cirrus,vpbr-atk-rate : Attenuation attack step rate. Configures the - amount delay between consecutive volume attenuation steps when a brownout - condition is present and the VP brownout condition is in an attacking state. - - - cirrus,vpbr-atk-vol : VP brownout prevention step size. Configures the VP - brownout prevention attacking attenuation step size when operating in either - digital volume or analog gain modes. - - - cirrus,vpbr-max-attn : Maximum attenuation that the VP brownout prevention - can apply to the audio signal. - - - cirrus,vpbr-wait : Configures the delay time between a brownout condition - no longer being present and the VP brownout prevention entering an attenuation - release state. - - - cirrus,vpbr-rel-rate : Attenuation release step rate. Configures the delay - between consecutive volume attenuation release steps when a brownout condition - is not longer present and the VP brownout is in an attenuation release state. - - - cirrus,vpbr-mute-en : During the attack state, if the vpbr-max-attn value - is reached, the error condition still remains, and this bit is set, the audio - is muted. - -Example: - -cs35l36: cs35l36@40 { - compatible = "cirrus,cs35l36"; - reg = <0x40>; - VA-supply = <&dummy_vreg>; - VP-supply = <&dummy_vreg>; - reset-gpios = <&gpio0 54 0>; - interrupt-parent = <&gpio8>; - interrupts = <3 IRQ_TYPE_LEVEL_LOW>; - - cirrus,boost-ind-nanohenry = <1000>; - cirrus,boost-ctl-millivolt = <10000>; - cirrus,boost-peak-milliamp = <4500>; - cirrus,boost-ctl-select = <0x00>; - cirrus,weak-fet-delay = <0x04>; - cirrus,weak-fet-thld = <0x01>; - cirrus,temp-warn-threshold = <0x01>; - cirrus,multi-amp-mode; - cirrus,irq-drive-select = <0x01>; - cirrus,irq-gpio-select = <0x01>; - - cirrus,vpbr-config { - cirrus,vpbr-en = <0x00>; - cirrus,vpbr-thld = <0x05>; - cirrus,vpbr-atk-rate = <0x02>; - cirrus,vpbr-atk-vol = <0x01>; - cirrus,vpbr-max-attn = <0x09>; - cirrus,vpbr-wait = <0x01>; - cirrus,vpbr-rel-rate = <0x05>; - cirrus,vpbr-mute-en = <0x00>; - }; -}; -- cgit v1.2.3 From 7ffb66fd96ec73dd5413d27624476588beb57c30 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 1 Jul 2026 06:17:57 +0000 Subject: ASoC: mediatek: mt8173-rt5650: tidyup error message mt8173_rt5650_dev_probe() has strange error message => ret = device_property_read_u32(...); ^^^^^^^^^^^^^^^^^^^^^^^^ if (ret) => dev_err(... "%s snd_soc_register_card fail %d\n", ...); ^^^^^^^^^^^^^^^^^^^^^ It should be "device_property_read_u32() fail". Fix it. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/875x2zcjxn.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8173/mt8173-rt5650.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c index 3d6d7bc05b87..8c5096482520 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c @@ -315,7 +315,7 @@ static int mt8173_rt5650_dev_probe(struct platform_device *pdev) &mt8173_rt5650_priv.pll_from); if (ret) { dev_err(&pdev->dev, - "%s snd_soc_register_card fail %d\n", + "%s device_property_read_u32() fail %d\n", __func__, ret); } } -- cgit v1.2.3 From 882e55031187791a2b87810d4a93f866a2da6d47 Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Tue, 30 Jun 2026 15:23:06 +0800 Subject: ASoC: codecs: ES8389: Modify volatile_register Mark some registers that are not volatile as false And modified the logic for `cache_bypass` during `8389_resume`. Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20260630072311.8427-2-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8389.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c index 3484c87853cb..10de2143f8d8 100644 --- a/sound/soc/codecs/es8389.c +++ b/sound/soc/codecs/es8389.c @@ -50,10 +50,29 @@ static const char * const es8389_core_supplies[] = { static bool es8389_volatile_register(struct device *dev, unsigned int reg) { - if ((reg <= 0xff)) - return true; - else + switch (reg) { + case ES8389_ADCL_VOL: + case ES8389_ADCR_VOL: + case ES8389_MIC1_GAIN: + case ES8389_MIC2_GAIN: + case ES8389_DACL_VOL: + case ES8389_DACR_VOL: + case ES8389_ALC_ON: + case ES8389_ALC_CTL: + case ES8389_ALC_TARGET: + case ES8389_ALC_GAIN: + case ES8389_ADC_MUTE: + case ES8389_OSR_VOL: + case ES8389_DAC_INV: + case ES8389_MIX_VOL: + case ES8389_DAC_MIX: + case ES8389_ADC_RESET: + case ES8389_ADC_MODE: + case ES8389_DMIC_EN: return false; + default: + return true; + } } static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9550, 50, 0); @@ -861,13 +880,13 @@ static int es8389_resume(struct snd_soc_component *component) regcache_cache_only(es8389->regmap, false); regcache_cache_bypass(es8389->regmap, true); regmap_read(es8389->regmap, ES8389_RESET, ®v); - regcache_cache_bypass(es8389->regmap, false); if (regv == 0xff) es8389_init(component); else es8389_set_bias_level(component, SND_SOC_BIAS_ON); + regcache_cache_bypass(es8389->regmap, false); regcache_sync(es8389->regmap); return 0; -- cgit v1.2.3 From cbc559dd8d46acd0781a4f183d8ec550262714ab Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Tue, 30 Jun 2026 15:23:07 +0800 Subject: ASoC: codecs: ES8389: Fix the issue about mclk_src Fix the issue with incorrect modifications to mclk_src When the system needs to be configured to use the MCLK from the SCLK pin, the code still sets the relevant registers to use the MCLK from the MCLK pin Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20260630072311.8427-3-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8389.c | 2 +- sound/soc/codecs/es8389.h | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c index 10de2143f8d8..890c6b4c15e2 100644 --- a/sound/soc/codecs/es8389.c +++ b/sound/soc/codecs/es8389.c @@ -609,7 +609,7 @@ static int es8389_pcm_hw_params(struct snd_pcm_substream *substream, if (es8389->mclk_src == ES8389_SCLK_PIN) { regmap_update_bits(es8389->regmap, ES8389_MASTER_CLK, - ES8389_MCLK_SOURCE, es8389->mclk_src); + ES8389_MCLK_MASK, ES8389_MCLK_FROM_SCLK); es8389->sysclk = params_channels(params) * params_width(params) * params_rate(params); } diff --git a/sound/soc/codecs/es8389.h b/sound/soc/codecs/es8389.h index d21e72f876a6..57bf7c5f8b57 100644 --- a/sound/soc/codecs/es8389.h +++ b/sound/soc/codecs/es8389.h @@ -116,9 +116,11 @@ #define ES8389_TDM_SLOT (0x70 << 0) #define ES8389_TDM_SHIFT 4 -#define ES8389_MCLK_SOURCE (1 << 6) -#define ES8389_MCLK_PIN (1 << 6) -#define ES8389_SCLK_PIN (0 << 6) +#define ES8389_MCLK_MASK (3 << 6) +#define ES8389_MCLK_FROM_SCLK (1 << 6) +#define ES8389_MCLK_SOURCE ES8389_MCLK_PIN +#define ES8389_MCLK_PIN 0 +#define ES8389_SCLK_PIN 1 /* ES8389_FMT */ #define ES8389_S24_LE (0 << 5) -- cgit v1.2.3 From 9367a244afc24e3863689bef126efa2ad01105db Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Tue, 30 Jun 2026 15:23:08 +0800 Subject: ASoC: codecs: ES8389: Modify the clock table Updated the configuration for certain frequencies Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20260630072311.8427-4-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8389.c | 46 ++++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c index 890c6b4c15e2..9d263927d7b7 100644 --- a/sound/soc/codecs/es8389.c +++ b/sound/soc/codecs/es8389.c @@ -434,52 +434,54 @@ static const struct _coeff_div coeff_div[] = { {36, 576000, 16000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {48, 768000, 16000, 0x02, 0x57, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {50, 800000, 16000, 0x00, 0x7E, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {64, 1024000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {72, 1152000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {64, 1024000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {72, 1152000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {96, 1536000, 16000, 0x02, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {128, 2048000, 16000, 0x00, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {144, 2304000, 16000, 0x00, 0x51, 0x00, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {150, 2400000, 16000, 0x02, 0x7E, 0x01, 0xC9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, {192, 3072000, 16000, 0x02, 0x65, 0x25, 0xE0, 0x00, 0xE1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {256, 4096000, 16000, 0x00, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {256, 4096000, 16000, 0x00, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {300, 4800000, 16000, 0x02, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {384, 6144000, 16000, 0x02, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {512, 8192000, 16000, 0x01, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {512, 8192000, 16000, 0x01, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {750, 12000000, 16000, 0x0E, 0x7E, 0x01, 0xC9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {768, 12288000, 16000, 0x02, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {1024, 16384000, 16000, 0x03, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {768, 12288000, 16000, 0x02, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {1024, 16384000, 16000, 0x03, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {1152, 18432000, 16000, 0x08, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {1200, 19200000, 16000, 0x0B, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {1500, 24000000, 16000, 0x0E, 0x26, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {1536, 24576000, 16000, 0x05, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {1536, 24576000, 16000, 0x05, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {1625, 26000000, 16000, 0x40, 0x6E, 0x05, 0xC8, 0x01, 0xC2, 0x90, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, {800, 19200000, 24000, 0x07, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x1A, 0x49, 0x14, 2, 2}, {375, 12000000, 32000, 0x0E, 0x2E, 0x05, 0xC8, 0x00, 0xC2, 0x80, 0x40, 0x01, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x23, 0x61, 0x1B, 2, 0}, - {600, 19200000, 32000, 0x05, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x23, 0x61, 0x1B, 2, 2}, - {32, 1411200, 44100, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {600, 19200000, 32000, 0x05, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x23, 0x61, 0x1B, 2, 2}, + {32, 1411200, 44100, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, {64, 2822400, 44100, 0x00, 0x51, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {128, 5644800, 44100, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {256, 11289600, 44100, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {512, 22579200, 44100, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {32, 1536000, 48000, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {128, 5644800, 44100, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {256, 11289600, 44100, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {512, 22579200, 44100, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {32, 1536000, 48000, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, {48, 2304000, 48000, 0x02, 0x55, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, {50, 2400000, 48000, 0x00, 0x76, 0x01, 0xC8, 0x10, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, {64, 3072000, 48000, 0x00, 0x51, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {100, 4800000, 48000, 0x00, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {100, 4800000, 48000, 0x00, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, {125, 6000000, 48000, 0x04, 0x6E, 0x05, 0xC8, 0x10, 0xC2, 0x80, 0x00, 0x01, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {128, 6144000, 48000, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {200, 9600000, 48000, 0x01, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {128, 6144000, 48000, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {200, 9600000, 48000, 0x01, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, {250, 12000000, 48000, 0x04, 0x76, 0x01, 0xC8, 0x10, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {256, 12288000, 48000, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {384, 18432000, 48000, 0x02, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {400, 19200000, 48000, 0x03, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {500, 24000000, 48000, 0x04, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {512, 24576000, 48000, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {256, 12288000, 48000, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {384, 18432000, 48000, 0x02, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {400, 19200000, 48000, 0x03, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {500, 24000000, 48000, 0x04, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {512, 24576000, 48000, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, {800, 38400000, 48000, 0x18, 0x45, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, {128, 11289600, 88200, 0x00, 0x50, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x32, 0x89, 0x25, 2, 2}, - {64, 6144000, 96000, 0x00, 0x41, 0x00, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28, 2, 2}, + {64, 6144000, 96000, 0x00, 0x41, 0x00, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28, 2, 2}, {96, 9216000, 96000, 0x02, 0x43, 0x00, 0xC0, 0x10, 0xC0, 0x80, 0x00, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28, 2, 2}, {256, 24576000, 96000, 0x00, 0x40, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28, 2, 2}, {128, 24576000, 192000, 0x00, 0x50, 0x00, 0xC0, 0x18, 0xC1, 0x81, 0xC0, 0x00, 0x8F, 0x7F, 0xBF, 0xC0, 0x3F, 0x7F, 0x80, 0x12, 0xC0, 0x3F, 0xF9, 0x3F, 2, 2}, + {64, 12288000, 192000, 0x00, 0x41, 0x00, 0xC0, 0x18, 0xC1, 0x80, 0x00, 0x00, 0x8F, 0x7F, 0xEF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x3F, 0xF9, 0x3F, 1, 0}, }; static inline int get_coeff(u8 vddd, u8 dmic, int mclk, int rate) -- cgit v1.2.3 From 3bea836903c2b2305c2219e897b217261be8a26a Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Tue, 30 Jun 2026 15:23:09 +0800 Subject: ASoC: codecs: ES8389: Modify the initial configuration Modify the initial configuration Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20260630072311.8427-5-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8389.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c index 9d263927d7b7..26a063186dc5 100644 --- a/sound/soc/codecs/es8389.c +++ b/sound/soc/codecs/es8389.c @@ -792,7 +792,7 @@ static void es8389_init(struct snd_soc_component *component) regmap_read(es8389->regmap, ES8389_MAX_REGISTER, ®); es8389->version = reg; - regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x00); + regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x56); regmap_write(es8389->regmap, ES8389_RESET, 0x7E); regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x38); regmap_write(es8389->regmap, ES8389_ADC_HPF1, 0x64); @@ -844,7 +844,7 @@ static void es8389_init(struct snd_soc_component *component) regmap_write(es8389->regmap, ES8389_SCLK_DIV, 0x04); regmap_write(es8389->regmap, ES8389_LRCK_DIV1, 0x01); regmap_write(es8389->regmap, ES8389_LRCK_DIV2, 0x00); - regmap_write(es8389->regmap, ES8389_OSC_CLK, 0x00); + regmap_write(es8389->regmap, ES8389_OSC_CLK, 0x10); regmap_write(es8389->regmap, ES8389_ADC_OSR, 0x1F); regmap_write(es8389->regmap, ES8389_ADC_DSP, 0x7F); regmap_write(es8389->regmap, ES8389_ADC_MUTE, 0xC0); -- cgit v1.2.3 From 87592da1a490abd2adeb57a49fb200d058403cc5 Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Tue, 30 Jun 2026 15:23:10 +0800 Subject: ASoC: codecs: ES8389: Add private members about HPF Add private members related to HPF. Add Kcontrol for HPF Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20260630072311.8427-6-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8389.c | 122 +++++++++++++++++++++++++++++++++++++++++++++- sound/soc/codecs/es8389.h | 3 ++ 2 files changed, 123 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c index 26a063186dc5..2f9a98a513a1 100644 --- a/sound/soc/codecs/es8389.c +++ b/sound/soc/codecs/es8389.c @@ -36,6 +36,10 @@ struct es8389_private { unsigned int sysclk; int mastermode; + u8 hpfl; + u8 hpfr; + u32 hpf_freq; + u32 capture_rate; u8 mclk_src; u8 vddd; int version; @@ -82,6 +86,92 @@ static const DECLARE_TLV_DB_SCALE(mix_vol_tlv, -9500, 100, 0); static const DECLARE_TLV_DB_SCALE(alc_target_tlv, -3200, 200, 0); static const DECLARE_TLV_DB_SCALE(alc_max_level, -3200, 200, 0); +static const u32 hpf_table[10][10] = { + {1020, 754, 624, 559, 527, 511, 502, 498, 497, 496}, + {754, 495, 368, 306, 274, 259, 251, 247, 246, 244}, + {624, 368, 243, 182, 151, 136, 128, 124, 123, 121}, + {559, 306, 182, 120, 90, 75, 68, 63, 62, 60}, + {527, 274, 151, 90, 60, 45, 38, 33, 32, 31}, + {511, 259, 136, 75, 45, 30, 23, 19, 18, 17}, + {502, 251, 128, 68, 38, 23, 16, 13, 11, 11}, + {498, 247, 124, 63, 33, 19, 13, 10, 8, 8}, + {497, 246, 123, 62, 32, 18, 11, 8, 8, 0}, + {496, 244, 121, 60, 31, 17, 11, 8, 0, 0} +}; + +static bool find_best_hpf_freq(u32 target_hz, u8 *hpf1, u8 *hpf2, u32 *out) +{ + int best_row = -1, best_col = -1; + u32 min_diff = U32_MAX; + u32 f, diff; + int i, j; + + if ((target_hz > 1020) | (target_hz < 0)) + return false; + + for (i = 0; i < 10; i++) { + for (j = i; j < 10; j++) { + f = hpf_table[i][j]; + + diff = (target_hz > f) ? (target_hz - f) : (f - target_hz); + if (diff < min_diff) { + min_diff = diff; + best_row = i; + best_col = j; + *out = f; + } + } + } + + *hpf1 = best_col + ES8389_HPF_OFFSET; + *hpf2 = best_row + ES8389_HPF_OFFSET; + + return true; +} + +static int es8389_hpf_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = es8389->hpf_freq; + return 0; +} + +static int es8389_hpf_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); + u32 freq; + bool hpf; + + if (es8389->hpf_freq == ucontrol->value.integer.value[0]) + return 0; + + if (es8389->capture_rate) { + freq = (ucontrol->value.integer.value[0] * 48000) / es8389->capture_rate; + + hpf = find_best_hpf_freq(freq, &es8389->hpfl, &es8389->hpfr, &es8389->hpf_freq); + if (!hpf) + return -EBUSY; + + if (es8389->hpf_freq != ucontrol->value.integer.value[0]) + dev_dbg(component->dev, "At the %u Hz sampling rate, %ld Hz could not be obtained." + "the frequency has been set to the closest value, %u Hz\n", + es8389->capture_rate, ucontrol->value.integer.value[0], es8389->hpf_freq); + + regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, es8389->hpfl); + regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, es8389->hpfr); + } else { + es8389->hpf_freq = ucontrol->value.integer.value[0]; + dev_dbg(component->dev, "PCM_STREAM_CAPTURE is not active.retain the input frequency\n"); + } + + return 1; +} + static int es8389_dmic_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -259,6 +349,8 @@ static const struct snd_kcontrol_new es8389_snd_controls[] = { SOC_DOUBLE("ADC OSR Volume ON Switch", ES8389_ADC_MUTE, 6, 7, 1, 0), SOC_SINGLE_TLV("ADC OSR Volume", ES8389_OSR_VOL, 0, 0xFF, 0, adc_vol_tlv), SOC_DOUBLE("ADC OUTPUT Invert Switch", ES8389_ADC_HPF2, 5, 6, 1, 0), + SOC_SINGLE_EXT("ADC HPF Freq Select", SND_SOC_NOPM, 0, 1020, 0, + es8389_hpf_get, es8389_hpf_set), SOC_SINGLE_TLV("DACL Playback Volume", ES8389_DACL_VOL, 0, 0xFF, 0, dac_vol_tlv), SOC_SINGLE_TLV("DACR Playback Volume", ES8389_DACR_VOL, 0, 0xFF, 0, dac_vol_tlv), @@ -585,6 +677,8 @@ static int es8389_pcm_hw_params(struct snd_pcm_substream *substream, int coeff, ret; u8 dmic_enable, state = 0; unsigned int regv; + u32 freq; + bool hpf; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: @@ -662,6 +756,28 @@ static int es8389_pcm_hw_params(struct snd_pcm_substream *substream, dev_warn(component->dev, "Clock coefficients do not match"); } + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + es8389->capture_rate = params_rate(params); + freq = (es8389->hpf_freq * 48000) / params_rate(params); + hpf = find_best_hpf_freq(freq, &es8389->hpfl, &es8389->hpfr, &es8389->hpf_freq); + if (!hpf) { + dev_err(component->dev, "The HPF frequency is invalid\n"); + return -EINVAL; + } + } + + return 0; +} + +static int es8389_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + es8389->capture_rate = 0; + return 0; } @@ -742,8 +858,8 @@ static int es8389_mute(struct snd_soc_dai *dai, int mute, int direction) regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE, 0x03, 0x00); } else { - regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, 0x0a); - regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, 0x0a); + regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, es8389->hpfl); + regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, es8389->hpfr); regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE, 0x03, 0x00); } @@ -759,6 +875,7 @@ static int es8389_mute(struct snd_soc_dai *dai, int mute, int direction) static const struct snd_soc_dai_ops es8389_ops = { .hw_params = es8389_pcm_hw_params, + .hw_free = es8389_pcm_hw_free, .set_fmt = es8389_set_dai_fmt, .set_sysclk = es8389_set_dai_sysclk, .set_tdm_slot = es8389_set_tdm_slot, @@ -934,6 +1051,7 @@ static int es8389_probe(struct snd_soc_component *component) return ret; } + es8389->hpf_freq = ES8389_HPF_DEFAULT; es8389_init(component); es8389_set_bias_level(component, SND_SOC_BIAS_STANDBY); diff --git a/sound/soc/codecs/es8389.h b/sound/soc/codecs/es8389.h index 57bf7c5f8b57..de39e9bf96e9 100644 --- a/sound/soc/codecs/es8389.h +++ b/sound/soc/codecs/es8389.h @@ -106,6 +106,9 @@ #define ES8389_MIC_SEL_MASK (7 << 4) #define ES8389_MIC_DEFAULT (1 << 4) +#define ES8389_HPF_DEFAULT 16 +#define ES8389_HPF_OFFSET 4 + #define ES8389_MASTER_MODE_EN (1 << 0) #define ES8389_TDM_OFF (0 << 0) -- cgit v1.2.3 From 2264927316e5312208659fe0b7efd0001c8975d9 Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Tue, 30 Jun 2026 15:23:11 +0800 Subject: ASoC: codecs: ES8389: Add INPUTL MUX and INPUTR MUX Add INPUTL MUX and INPUTR MUX in route Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20260630072311.8427-7-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8389.c | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c index 2f9a98a513a1..0c7567e2ffc2 100644 --- a/sound/soc/codecs/es8389.c +++ b/sound/soc/codecs/es8389.c @@ -252,6 +252,16 @@ static const struct soc_enum alc_ramprate = static const struct soc_enum alc_winsize = SOC_ENUM_SINGLE(ES8389_ALC_CTL, 0, 16, winsize); +static const char *const es8389_adcl_mux_txt[] = { + "Normal", + "ADC2 channel to ADC1 channel", +}; + +static const char *const es8389_adcr_mux_txt[] = { + "Normal", + "ADC1 channel to ADC2 channel", +}; + static const char *const es8389_outl_mux_txt[] = { "Normal", "DAC2 channel to DAC1 channel", @@ -279,6 +289,20 @@ static const unsigned int es8389_pga_values[] = { 1, 5, 6 }; +static const struct soc_enum es8389_adcl_mux_enum = + SOC_ENUM_SINGLE(ES8389_ADC_MODE, 5, + ARRAY_SIZE(es8389_adcl_mux_txt), es8389_adcl_mux_txt); + +static const struct snd_kcontrol_new es8389_adcl_mux_controls = + SOC_DAPM_ENUM("INPUTL MUX", es8389_adcl_mux_enum); + +static const struct soc_enum es8389_adcr_mux_enum = + SOC_ENUM_SINGLE(ES8389_ADC_MODE, 4, + ARRAY_SIZE(es8389_adcr_mux_txt), es8389_adcr_mux_txt); + +static const struct snd_kcontrol_new es8389_adcr_mux_controls = + SOC_DAPM_ENUM("INPUTR MUX", es8389_adcr_mux_enum); + static const struct soc_enum es8389_outl_mux_enum = SOC_ENUM_SINGLE(ES8389_DAC_MIX, 5, ARRAY_SIZE(es8389_outl_mux_txt), es8389_outl_mux_txt); @@ -409,6 +433,8 @@ static const struct snd_soc_dapm_widget es8389_dapm_widgets[] = { &es8389_adc_mixer_controls[0], ARRAY_SIZE(es8389_adc_mixer_controls)), SND_SOC_DAPM_MUX("ADC MUX", SND_SOC_NOPM, 0, 0, &es8389_dmic_mux_controls), + SND_SOC_DAPM_MUX("INPUTL MUX", SND_SOC_NOPM, 0, 0, &es8389_adcl_mux_controls), + SND_SOC_DAPM_MUX("INPUTR MUX", SND_SOC_NOPM, 0, 0, &es8389_adcr_mux_controls), SND_SOC_DAPM_MUX("OUTL MUX", SND_SOC_NOPM, 0, 0, &es8389_outl_mux_controls), SND_SOC_DAPM_MUX("OUTR MUX", SND_SOC_NOPM, 0, 0, &es8389_outr_mux_controls), @@ -422,10 +448,15 @@ static const struct snd_soc_dapm_route es8389_dapm_routes[] = { {"ADCL", NULL, "PGAL"}, {"ADCR", NULL, "PGAR"}, + {"INPUTL MUX", "Normal", "ADCL"}, + {"INPUTL MUX", "ADC2 channel to ADC1 channel", "ADCR"}, + {"INPUTR MUX", "Normal", "ADCR"}, + {"INPUTR MUX", "ADC1 channel to ADC2 channel", "ADCL"}, + {"ADC Mixer", "DACL ADCL Mixer", "DACL"}, {"ADC Mixer", "DACR ADCR Mixer", "DACR"}, - {"ADC Mixer", NULL, "ADCL"}, - {"ADC Mixer", NULL, "ADCR"}, + {"ADC Mixer", NULL, "INPUTL MUX"}, + {"ADC Mixer", NULL, "INPUTR MUX"}, {"ADC MUX", "AMIC", "ADC Mixer"}, {"ADC MUX", "DMIC", "DMIC"}, -- cgit v1.2.3 From e3653722b94df580ab8610f4f38ab43e46f083c0 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 26 Jun 2026 06:04:20 +0000 Subject: ASoC: sof: topology: use for_each_card_rtds() We already have for_each_card_rtds(). Let's use it. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87h5mpet2c.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index f709935593ef..6de8a6c1c127 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1102,7 +1102,7 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp, full = NULL; partial = NULL; - list_for_each_entry(rtd, &card->rtd_list, list) { + for_each_card_rtds(card, rtd) { /* does stream match DAI link ? */ if (rtd->dai_link->stream_name) { if (!strcmp(rtd->dai_link->stream_name, w->sname)) { @@ -1167,7 +1167,7 @@ static void sof_disconnect_dai_widget(struct snd_soc_component *scomp, else return; - list_for_each_entry(rtd, &card->rtd_list, list) { + for_each_card_rtds(card, rtd) { /* does stream match DAI link ? */ if (!rtd->dai_link->stream_name || !strstr(rtd->dai_link->stream_name, sname)) -- cgit v1.2.3 From 02fd694e60a7e2c581c7836f6781c01b9b419c8a Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 1 Jul 2026 11:13:09 +0700 Subject: ASoC: samsung: i2s: Avoid mixing goto with guard() cleanup.h recommends not mixing goto-based error handling with cleanup helpers in the same function. Remove the goto path and rely on guard(pm_runtime) for automatic cleanup instead. Fixes: 3d08517b5c67 ("ASoC: samsung: i2s: Use guard() for spin locks") Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260701041310.230725-2-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/samsung/i2s.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index f80f697a5d55..f80e8d498156 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -512,7 +513,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, u32 mod, mask, val = 0; int ret = 0; - pm_runtime_get_sync(dai->dev); + guard(pm_runtime_active)(dai->dev); scoped_guard(spinlock_irqsave, &priv->lock) mod = readl(priv->addr + I2SMOD); @@ -537,8 +538,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, && (mod & cdcon_mask))))) { dev_err(&i2s->pdev->dev, "%s:%d Other DAI busy\n", __func__, __LINE__); - ret = -EAGAIN; - goto err; + return -EAGAIN; } if (dir == SND_SOC_CLOCK_IN) @@ -566,7 +566,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, } else { priv->rclk_srcrate = clk_get_rate(priv->op_clk); - goto done; + return 0; } } @@ -580,14 +580,14 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, if (WARN_ON(IS_ERR(priv->op_clk))) { ret = PTR_ERR(priv->op_clk); priv->op_clk = NULL; - goto err; + return ret; } ret = clk_prepare_enable(priv->op_clk); if (ret) { clk_put(priv->op_clk); priv->op_clk = NULL; - goto err; + return ret; } priv->rclk_srcrate = clk_get_rate(priv->op_clk); @@ -595,11 +595,10 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, || (clk_id && !(mod & rsrc_mask))) { dev_err(&i2s->pdev->dev, "%s:%d Other DAI busy\n", __func__, __LINE__); - ret = -EAGAIN; - goto err; + return -EAGAIN; } else { /* Call can't be on the active DAI */ - goto done; + return 0; } if (clk_id == 1) @@ -607,8 +606,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, break; default: dev_err(&i2s->pdev->dev, "We don't serve that!\n"); - ret = -EINVAL; - goto err; + return -EINVAL; } scoped_guard(spinlock_irqsave, &priv->lock) { @@ -616,13 +614,8 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, mod = (mod & ~mask) | val; writel(mod, priv->addr + I2SMOD); } -done: - pm_runtime_put(dai->dev); return 0; -err: - pm_runtime_put(dai->dev); - return ret; } static int i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) -- cgit v1.2.3 From 47a0dde9a3bdd01359ce3a5f0b59a9de33b73dce Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 1 Jul 2026 11:13:10 +0700 Subject: ASoC: ti: j721e-evm: Avoid mixing goto with guard() The previous guard(mutex) conversion mixed cleanup helpers with goto-based error handling, which is discouraged by the cleanup.h guidelines. Restore mutex_lock()/mutex_unlock() instead. Fixes: 6f4cf77320ae ("ASoC: ti: j721e-evm: Use guard() for mutex locks") Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260701041310.230725-3-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/ti/j721e-evm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/soc/ti/j721e-evm.c b/sound/soc/ti/j721e-evm.c index c214ae0d7b95..312298e0b004 100644 --- a/sound/soc/ti/j721e-evm.c +++ b/sound/soc/ti/j721e-evm.c @@ -4,6 +4,7 @@ * Author: Peter Ujfalusi */ +#include #include #include #include @@ -263,7 +264,7 @@ static int j721e_audio_startup(struct snd_pcm_substream *substream) int ret = 0; int i; - guard(mutex)(&priv->mutex); + mutex_lock(&priv->mutex); domain->active++; @@ -303,6 +304,7 @@ static int j721e_audio_startup(struct snd_pcm_substream *substream) out: if (ret) domain->active--; + mutex_unlock(&priv->mutex); return ret; } -- cgit v1.2.3 From fb5d1b1c5f8a920ee697545fa6dee16825085717 Mon Sep 17 00:00:00 2001 From: WenTao Liang Date: Sat, 27 Jun 2026 11:52:51 +0800 Subject: ASoC: samsung: aries_audio_probe: double of_node_put due to direct assignment without of_node_get In aries_audio_probe(), aries_dai[0].platforms->of_node is assigned the same pointer as aries_dai[0].cpus->of_node (from of_parse_phandle) without calling of_node_get(). When the sound card is deregistered, the ASoC framework calls of_node_put() on both cpus->of_node and platforms->of_node, causing a double put on the same node and a refcount underflow. Add of_node_get(aries_dai[0].cpus->of_node) before the assignment. Cc: stable@vger.kernel.org Fixes: 7a3a7671fa6c ("ASoC: samsung: Add driver for Aries boards") Signed-off-by: WenTao Liang Link: https://patch.msgid.link/20260627035251.60172-1-vulab@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/samsung/aries_wm8994.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/samsung/aries_wm8994.c b/sound/soc/samsung/aries_wm8994.c index 48ccc1d1854b..6db91b73f25c 100644 --- a/sound/soc/samsung/aries_wm8994.c +++ b/sound/soc/samsung/aries_wm8994.c @@ -658,6 +658,7 @@ static int aries_audio_probe(struct platform_device *pdev) goto out; } + of_node_get(aries_dai[0].cpus->of_node); aries_dai[0].platforms->of_node = aries_dai[0].cpus->of_node; /* Set CPU of_node for BT DAI */ -- cgit v1.2.3 From bb7c62fdbfecfe15c98c83567f6e1de7d02e52fc Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Wed, 1 Jul 2026 15:05:17 +0700 Subject: ASoC: samsung: spdif: Preserve the original clock acquisition error devm_clk_get() may return different error codes, including -EPROBE_DEFER. The current code overwrites the original error with -ENOENT, preventing deferred probing from working correctly. Replace dev_err() with dev_err_probe() so the original error code is preserved and propagated to the caller. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260701080517.298294-1-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- sound/soc/samsung/spdif.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/samsung/spdif.c b/sound/soc/samsung/spdif.c index 7fc46d55c522..53eaabaf8956 100644 --- a/sound/soc/samsung/spdif.c +++ b/sound/soc/samsung/spdif.c @@ -380,8 +380,8 @@ static int spdif_probe(struct platform_device *pdev) spdif->pclk = devm_clk_get(&pdev->dev, "spdif"); if (IS_ERR(spdif->pclk)) { - dev_err(&pdev->dev, "failed to get peri-clock\n"); - ret = -ENOENT; + ret = dev_err_probe(&pdev->dev, PTR_ERR(spdif->pclk), + "failed to get peri-clock\n"); goto err0; } ret = clk_prepare_enable(spdif->pclk); @@ -390,8 +390,8 @@ static int spdif_probe(struct platform_device *pdev) spdif->sclk = devm_clk_get(&pdev->dev, "sclk_spdif"); if (IS_ERR(spdif->sclk)) { - dev_err(&pdev->dev, "failed to get internal source clock\n"); - ret = -ENOENT; + ret = dev_err_probe(&pdev->dev, PTR_ERR(spdif->sclk), + "failed to get internal source clock\n"); goto err1; } ret = clk_prepare_enable(spdif->sclk); -- cgit v1.2.3 From 0eb0e3c623ac1da8b85d518043fef7660af7805d Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Fri, 26 Jun 2026 10:27:03 +0800 Subject: ASoC: loongson: Fix error handling in ACPI property parsing In loongson_card_parse_acpi(), the return value of device_property_read_string() for the `codec-dai-name` property was ignored. If the property is missing or invalid, an uninitialized pointer would be used later, potentially leading to undefined behavior. Fix this by checking the return value and propagating the error appropriately. Cc: stable@vger.kernel.org Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/cover.1780538113.git.zhoubinbin@loongson.cn?part=5 Fixes: ddb538a3004b ("ASoC: loongson: Factor out loongson_card_acpi_find_device() function") Signed-off-by: Binbin Zhou Link: https://patch.msgid.link/08e44a54708eae053be148524346bb8dfcd55b03.1782439646.git.zhoubinbin@loongson.cn Signed-off-by: Mark Brown --- sound/soc/loongson/loongson_card.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sound/soc/loongson/loongson_card.c b/sound/soc/loongson/loongson_card.c index 7910d5d9ac4f..ea895fe6b5e9 100644 --- a/sound/soc/loongson/loongson_card.c +++ b/sound/soc/loongson/loongson_card.c @@ -91,7 +91,7 @@ static int loongson_card_parse_acpi(struct loongson_card_data *data) const char *codec_dai_name; struct acpi_device *adev; struct device *phy_dev; - int i; + int i, ret; /* fixup platform name based on reference node */ adev = loongson_card_acpi_find_device(card, "cpu"); @@ -108,7 +108,9 @@ static int loongson_card_parse_acpi(struct loongson_card_data *data) return -ENOENT; snprintf(codec_name, sizeof(codec_name), "i2c-%s", acpi_dev_name(adev)); - device_property_read_string(card->dev, "codec-dai-name", &codec_dai_name); + ret = device_property_read_string(card->dev, "codec-dai-name", &codec_dai_name); + if (ret) + return ret; for (i = 0; i < card->num_links; i++) { loongson_dai_links[i].platforms->name = dev_name(phy_dev); -- cgit v1.2.3 From 914e95aaec0df7dc2f3e3e5a774b09f92f37cd91 Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Fri, 26 Jun 2026 10:27:04 +0800 Subject: ASoC: dt-bindings: loongson,ls2k1000-i2s: Document Loongson-2K0300 compatible Add a new compatible string `loongson,ls2k0300-i2s` for the I2S controller found on Loongson-2K0300 SoC. Unlike Loongson-2K1000, Loongson-2K0300 does not require the second register region for APB DMA configuration, so update the binding to allow a single reg entry. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Binbin Zhou Link: https://patch.msgid.link/af092f9eabdc170c3d7951b29eee6512f748eb48.1782439646.git.zhoubinbin@loongson.cn Signed-off-by: Mark Brown --- .../bindings/sound/loongson,ls2k1000-i2s.yaml | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/sound/loongson,ls2k1000-i2s.yaml b/Documentation/devicetree/bindings/sound/loongson,ls2k1000-i2s.yaml index da79510bb2d9..51e23c189f7a 100644 --- a/Documentation/devicetree/bindings/sound/loongson,ls2k1000-i2s.yaml +++ b/Documentation/devicetree/bindings/sound/loongson,ls2k1000-i2s.yaml @@ -14,9 +14,12 @@ allOf: properties: compatible: - const: loongson,ls2k1000-i2s + enum: + - loongson,ls2k0300-i2s + - loongson,ls2k1000-i2s reg: + minItems: 1 items: - description: Loongson I2S controller Registers. - description: APB DMA config register for Loongson I2S controller. @@ -49,6 +52,23 @@ required: unevaluatedProperties: false +if: + properties: + compatible: + contains: + enum: + - loongson,ls2k1000-i2s + +then: + properties: + reg: + minItems: 2 + +else: + properties: + reg: + maxItems: 1 + examples: - | #include -- cgit v1.2.3 From 41ab1f8a0edaa98d59162fbe801c1f162fc6cf9a Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Fri, 26 Jun 2026 10:27:05 +0800 Subject: ASoC: loongson: Add Loongson-2K0300 I2S controller support The Loongson-2K0300 I2S interface differs significantly from the Loongson-2K1000. Although both utilize external DMA controllers, the Loongson-2K0300 does not require additional registers for routing configuration. Due to hardware design flaw, an extra controller reset sequence is required during probe. Signed-off-by: Binbin Zhou Link: https://patch.msgid.link/4d3caa62d4275e1495505387198408f4d30d453c.1782439646.git.zhoubinbin@loongson.cn Signed-off-by: Mark Brown --- sound/soc/loongson/loongson_i2s_plat.c | 42 ++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/sound/soc/loongson/loongson_i2s_plat.c b/sound/soc/loongson/loongson_i2s_plat.c index ac054b6ce632..82d95c6644ef 100644 --- a/sound/soc/loongson/loongson_i2s_plat.c +++ b/sound/soc/loongson/loongson_i2s_plat.c @@ -2,7 +2,7 @@ // // Loongson I2S controller master mode dirver(platform device) // -// Copyright (C) 2023-2024 Loongson Technology Corporation Limited +// Copyright (C) 2023-2026 Loongson Technology Corporation Limited // // Author: Yingkun Meng // Binbin Zhou @@ -21,6 +21,7 @@ #include "loongson_i2s.h" #include "loongson_dma.h" +/* Loongson-2K1000 APBDMA routing */ #define LOONGSON_I2S_RX_DMA_OFFSET 21 #define LOONGSON_I2S_TX_DMA_OFFSET 18 @@ -30,6 +31,11 @@ #define LOONGSON_DMA3_CONF 0x3 #define LOONGSON_DMA4_CONF 0x4 +struct loongson_i2s_plat_config { + int rev_id; + int (*i2s_dma_config)(struct platform_device *pdev); +}; + static int loongson_i2s_apbdma_config(struct platform_device *pdev) { int val; @@ -47,8 +53,18 @@ static int loongson_i2s_apbdma_config(struct platform_device *pdev) return 0; } +static const struct loongson_i2s_plat_config ls2k0300_i2s_plat_config = { + .rev_id = 1, +}; + +static const struct loongson_i2s_plat_config ls2k1000_i2s_plat_config = { + .rev_id = 0, + .i2s_dma_config = loongson_i2s_apbdma_config, +}; + static int loongson_i2s_plat_probe(struct platform_device *pdev) { + const struct loongson_i2s_plat_config *plat_config; struct device *dev = &pdev->dev; struct loongson_i2s *i2s; struct resource *res; @@ -59,12 +75,17 @@ static int loongson_i2s_plat_probe(struct platform_device *pdev) if (!i2s) return -ENOMEM; - ret = loongson_i2s_apbdma_config(pdev); - if (ret) - return ret; + plat_config = device_get_match_data(dev); + if (!plat_config) + return -EINVAL; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - i2s->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (plat_config->i2s_dma_config) { + ret = plat_config->i2s_dma_config(pdev); + if (ret) + return ret; + } + + i2s->reg_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(i2s->reg_base)) return dev_err_probe(dev, PTR_ERR(i2s->reg_base), "devm_ioremap_resource failed\n"); @@ -87,11 +108,17 @@ static int loongson_i2s_plat_probe(struct platform_device *pdev) if (IS_ERR(i2s_clk)) return dev_err_probe(dev, PTR_ERR(i2s_clk), "clock property invalid\n"); i2s->clk_rate = clk_get_rate(i2s_clk); + i2s->rev_id = plat_config->rev_id; dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); dev_set_name(dev, LS_I2S_DRVNAME); dev_set_drvdata(dev, i2s); + if (i2s->rev_id == 1) { + regmap_update_bits(i2s->regmap, LS_I2S_CTRL, I2S_CTRL_RESET, I2S_CTRL_RESET); + fsleep(200); + } + ret = devm_snd_soc_register_component(dev, &loongson_i2s_edma_component, &loongson_i2s_dai, 1); if (ret) @@ -102,7 +129,8 @@ static int loongson_i2s_plat_probe(struct platform_device *pdev) } static const struct of_device_id loongson_i2s_ids[] = { - { .compatible = "loongson,ls2k1000-i2s" }, + { .compatible = "loongson,ls2k0300-i2s", .data = &ls2k0300_i2s_plat_config }, + { .compatible = "loongson,ls2k1000-i2s", .data = &ls2k1000_i2s_plat_config }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, loongson_i2s_ids); -- cgit v1.2.3 From 95133a9ac817242dded1d15643a2aa5faaa289d8 Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Fri, 26 Jun 2026 10:27:25 +0800 Subject: ASoC: dt-bindings: loongson,ls-audio-card: Use common sound card Reference the common sound card properties. This allows removing the `model` property and directly using the common `audio-routing` property later on. Acked-by: Rob Herring (Arm) Signed-off-by: Binbin Zhou Link: https://patch.msgid.link/fa72a429a9d076d381f7d514184f19d5a35ffa51.1782439646.git.zhoubinbin@loongson.cn Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/loongson,ls-audio-card.yaml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml b/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml index 61e8babed402..e1b7445a8b22 100644 --- a/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml +++ b/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml @@ -8,20 +8,20 @@ title: Loongson 7axxx/2kxxx ASoC audio sound card driver maintainers: - Yingkun Meng + - Binbin Zhou description: The binding describes the sound card present in loongson 7axxx/2kxxx platform. The sound card is an ASoC component which uses Loongson I2S controller to transfer the audio data. +allOf: + - $ref: sound-card-common.yaml# + properties: compatible: const: loongson,ls-audio-card - model: - $ref: /schemas/types.yaml#/definitions/string - description: User specified audio sound card name - mclk-fs: $ref: simple-card.yaml#/definitions/mclk-fs @@ -47,12 +47,11 @@ properties: required: - compatible - - model - mclk-fs - cpu - codec -additionalProperties: false +unevaluatedProperties: false examples: - | -- cgit v1.2.3 From 5460b4ddc76a04eb4ad05333f904bad67f1730e2 Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Fri, 26 Jun 2026 10:27:26 +0800 Subject: ASoC: dt-bindings: loongson,ls-audio-card: Add ctcisz forever pi compatible Add a new compatible string `loongson,ls2k0300-forever-pi-audio-card` for the audio card on Loongson-2K0300 ctcisz forever pi SoC. It uses a different DAI format compared to existing Loongson platforms. The existing "loongson,ls-audio-card" remains valid for LS7A, Loongson-2K1000 and Loongson-2K2000. Signed-off-by: Binbin Zhou Acked-by: Krzysztof Kozlowski Link: https://patch.msgid.link/183d809cd51874bcb78743273e4b7617f120fedb.1782439646.git.zhoubinbin@loongson.cn Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml b/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml index e1b7445a8b22..8c214e5d04b1 100644 --- a/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml +++ b/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml @@ -20,7 +20,9 @@ allOf: properties: compatible: - const: loongson,ls-audio-card + enum: + - loongson,ls-audio-card # Loongson-2K1000/Loongson-2K2000/LS7A + - loongson,ls2k0300-forever-pi-audio-card # CTCISZ Forever Pi mclk-fs: $ref: simple-card.yaml#/definitions/mclk-fs -- cgit v1.2.3 From da659805e0b055f8f12de022e28a9983d2f3923f Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Fri, 26 Jun 2026 10:27:27 +0800 Subject: ASoC: loongson: Add Loongson-2K0300 CTCISZ Forever Pi sound card support The Loongson-2K0300 audio card uses a different DAI format compared to existing Loongson platforms. Move the dai_fmt setting from the static DAI link to runtime hw_params via snd_soc_runtime_set_dai_fmt(), and pass the correct format through driver match data. Signed-off-by: Binbin Zhou Link: https://patch.msgid.link/ed1314e1d3275fd20aff47397345a88fad6e9368.1782439646.git.zhoubinbin@loongson.cn Signed-off-by: Mark Brown --- sound/soc/loongson/loongson_card.c | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/sound/soc/loongson/loongson_card.c b/sound/soc/loongson/loongson_card.c index ea895fe6b5e9..0e63cbcad57a 100644 --- a/sound/soc/loongson/loongson_card.c +++ b/sound/soc/loongson/loongson_card.c @@ -2,8 +2,9 @@ // // Loongson ASoC Audio Machine driver // -// Copyright (C) 2023 Loongson Technology Corporation Limited +// Copyright (C) 2023-2026 Loongson Technology Corporation Limited // Author: Yingkun Meng +// Binbin Zhou // #include @@ -18,6 +19,19 @@ static char codec_name[SND_ACPI_I2C_ID_LEN]; struct loongson_card_data { struct snd_soc_card snd_card; unsigned int mclk_fs; + const struct loongson_card_config *cfg; +}; + +struct loongson_card_config { + unsigned int fmt; +}; + +static const struct loongson_card_config ls2k1000_card_config = { + .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBC_CFC, +}; + +static const struct loongson_card_config ls2k0300_forever_pi_card_config = { + .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC, }; static int loongson_card_hw_params(struct snd_pcm_substream *substream, @@ -45,7 +59,7 @@ static int loongson_card_hw_params(struct snd_pcm_substream *substream, return ret; } - return 0; + return snd_soc_runtime_set_dai_fmt(rtd, ls_card->cfg->fmt); } static const struct snd_soc_ops loongson_ops = { @@ -61,8 +75,6 @@ static struct snd_soc_dai_link loongson_dai_links[] = { { .name = "Loongson Audio Port", .stream_name = "Loongson Audio", - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_IB_NF - | SND_SOC_DAIFMT_CBC_CFC, SND_SOC_DAILINK_REG(analog), .ops = &loongson_ops, }, @@ -177,6 +189,10 @@ static int loongson_asoc_card_probe(struct platform_device *pdev) if (!ls_priv) return -ENOMEM; + ls_priv->cfg = (const struct loongson_card_config *)device_get_match_data(dev); + if (!ls_priv->cfg) + return -EINVAL; + card = &ls_priv->snd_card; card->dev = dev; @@ -202,7 +218,15 @@ static int loongson_asoc_card_probe(struct platform_device *pdev) } static const struct of_device_id loongson_asoc_dt_ids[] = { - { .compatible = "loongson,ls-audio-card" }, + /* Loongson-2K1000/Loongson-2K2000/LS7A */ + { + .compatible = "loongson,ls-audio-card", + .data = &ls2k1000_card_config + }, + { + .compatible = "loongson,ls2k0300-forever-pi-audio-card", + .data = &ls2k0300_forever_pi_card_config + }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, loongson_asoc_dt_ids); -- cgit v1.2.3 From 3ddae79479f6c96c4083951c7c4511d7236e7982 Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Fri, 26 Jun 2026 10:27:28 +0800 Subject: ASoC: dt-bindings: loongson,ls-audio-card: Add ATK-DL2K0300B compatible Add new compatible for the ATK-DL2K0300B development board based on Loongson-2K0300. Unlike others, this board features GPIO-controlled headphone detection, headphone control, and speaker enable. Signed-off-by: Binbin Zhou Link: https://patch.msgid.link/71430fcee5951fb7a7d52e2091a87707db85e06c.1782439646.git.zhoubinbin@loongson.cn Signed-off-by: Mark Brown --- .../bindings/sound/loongson,ls-audio-card.yaml | 38 ++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml b/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml index 8c214e5d04b1..dc7f4afbb777 100644 --- a/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml +++ b/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml @@ -23,6 +23,7 @@ properties: enum: - loongson,ls-audio-card # Loongson-2K1000/Loongson-2K2000/LS7A - loongson,ls2k0300-forever-pi-audio-card # CTCISZ Forever Pi + - loongson,ls2k0300-dl2k0300b-audio-card # ATK-DL2K0300B mclk-fs: $ref: simple-card.yaml#/definitions/mclk-fs @@ -47,6 +48,18 @@ properties: required: - sound-dai + spkr-en-gpios: + maxItems: 1 + description: The GPIO that enables the speakers + + hp-ctl-gpios: + maxItems: 1 + description: The GPIO that control the headphones + + hp-det-gpios: + maxItems: 1 + description: The GPIO that detect headphones are plugged in + required: - compatible - mclk-fs @@ -69,3 +82,28 @@ examples: sound-dai = <&es8323>; }; }; + + - | + #include + + sound { + compatible = "loongson,ls2k0300-dl2k0300b-audio-card"; + model = "loongson-audio"; + mclk-fs = <512>; + hp-det-gpios = <&gpio 81 GPIO_ACTIVE_HIGH>; + spkr-en-gpios = <&gpio 86 GPIO_ACTIVE_HIGH>; + hp-ctl-gpios = <&gpio 87 GPIO_ACTIVE_HIGH>; + audio-routing = + "Headphone", "LOUT1", + "Headphone", "ROUT1", + "Speaker", "LOUT2", + "Speaker", "ROUT2"; + + cpu { + sound-dai = <&i2s>; + }; + + codec { + sound-dai = <&es8388>; + }; + }; -- cgit v1.2.3 From dde4064a4913319cf9b19e745af3417a35f0f738 Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Fri, 26 Jun 2026 10:27:48 +0800 Subject: ASoC: loongson: Add headphone jack detection and DAPM routing Extend the Loongson audio machine driver with jack detection, DAPM widgets support, enabling proper switching between headphones and speakers on the Loongson-2K0300 ATK-DL2K0300B board. Signed-off-by: Binbin Zhou Link: https://patch.msgid.link/c4b1503220b6fb433ecdcb881556827579041c3d.1782439646.git.zhoubinbin@loongson.cn Signed-off-by: Mark Brown --- sound/soc/loongson/loongson_card.c | 131 +++++++++++++++++++++++++++++++++++-- 1 file changed, 127 insertions(+), 4 deletions(-) diff --git a/sound/soc/loongson/loongson_card.c b/sound/soc/loongson/loongson_card.c index 0e63cbcad57a..25cd12eab4b1 100644 --- a/sound/soc/loongson/loongson_card.c +++ b/sound/soc/loongson/loongson_card.c @@ -7,33 +7,126 @@ // Binbin Zhou // -#include -#include -#include #include +#include +#include #include +#include #include +#include +#include static char codec_name[SND_ACPI_I2C_ID_LEN]; struct loongson_card_data { struct snd_soc_card snd_card; unsigned int mclk_fs; + struct gpio_desc *gpiod_hp_det; + struct gpio_desc *gpiod_hp_ctl; + struct gpio_desc *gpiod_spkr_en; const struct loongson_card_config *cfg; }; struct loongson_card_config { unsigned int fmt; + bool add_hp_jack; + bool add_dapm_widgets; + bool add_dapm_routes; }; static const struct loongson_card_config ls2k1000_card_config = { .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBC_CFC, + .add_hp_jack = false, + .add_dapm_widgets = false, + .add_dapm_routes = false, }; static const struct loongson_card_config ls2k0300_forever_pi_card_config = { .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC, + .add_hp_jack = false, + .add_dapm_widgets = false, + .add_dapm_routes = false, +}; + +static const struct loongson_card_config ls2k0300_dl2k0300b_card_config = { + .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC, + .add_hp_jack = true, + .add_dapm_widgets = true, + .add_dapm_routes = true, +}; + +static int tegra_machine_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm); + struct loongson_card_data *priv = snd_soc_card_get_drvdata(card); + + if (!snd_soc_dapm_widget_name_cmp(w, "Speaker")) + gpiod_set_value_cansleep(priv->gpiod_spkr_en, + SND_SOC_DAPM_EVENT_ON(event)); + + if (!snd_soc_dapm_widget_name_cmp(w, "Headphone")) + gpiod_set_value_cansleep(priv->gpiod_hp_ctl, + SND_SOC_DAPM_EVENT_ON(event)); + + return 0; +} + +static const struct snd_soc_dapm_widget loongson_aosc_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone", tegra_machine_event), + SND_SOC_DAPM_SPK("Speaker", tegra_machine_event), +}; + +/* Headphones Jack */ + +static struct snd_soc_jack loongson_asoc_hp_jack; + +static struct snd_soc_jack_pin loongson_asoc_hp_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE + }, + { + .pin = "Speaker", + .mask = SND_JACK_HEADPHONE, + .invert = 1 + }, +}; + +static struct snd_soc_jack_gpio loongson_asoc_hp_jack_gpio = { + .name = "Headphones detection", + .report = SND_JACK_HEADPHONE, + .debounce_time = 150, }; +static int loongson_asoc_machine_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + struct loongson_card_data *ls_priv = snd_soc_card_get_drvdata(card); + int ret = 0; + + if (!ls_priv->cfg->add_hp_jack || !ls_priv->gpiod_hp_det) + return 0; + + ret = snd_soc_card_jack_new_pins(card, "Headphones Jack", + SND_JACK_HEADPHONE, + &loongson_asoc_hp_jack, + loongson_asoc_hp_jack_pins, + ARRAY_SIZE(loongson_asoc_hp_jack_pins)); + if (ret) { + dev_err(rtd->dev, "Headphones Jack creation failed: %d\n", ret); + return ret; + } + + loongson_asoc_hp_jack_gpio.desc = ls_priv->gpiod_hp_det; + + ret = snd_soc_jack_add_gpios(&loongson_asoc_hp_jack, 1, &loongson_asoc_hp_jack_gpio); + if (ret) + dev_err(rtd->dev, "Headphone GPIO not added: %d\n", ret); + + return ret; +} + static int loongson_card_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -75,6 +168,7 @@ static struct snd_soc_dai_link loongson_dai_links[] = { { .name = "Loongson Audio Port", .stream_name = "Loongson Audio", + .init = loongson_asoc_machine_init, SND_SOC_DAILINK_REG(analog), .ops = &loongson_ops, }, @@ -135,16 +229,35 @@ static int loongson_card_parse_acpi(struct loongson_card_data *data) static int loongson_card_parse_of(struct loongson_card_data *data) { - struct device_node *cpu, *codec; struct snd_soc_card *card = &data->snd_card; + struct device_node *cpu, *codec; struct device *dev = card->dev; int ret, i; + data->gpiod_hp_det = devm_gpiod_get_optional(dev, "hp-det", GPIOD_IN); + if (IS_ERR(data->gpiod_hp_det)) + return PTR_ERR(data->gpiod_hp_det); + + data->gpiod_hp_ctl = devm_gpiod_get_optional(dev, "hp-ctl", GPIOD_OUT_LOW); + if (IS_ERR(data->gpiod_hp_ctl)) + return PTR_ERR(data->gpiod_hp_ctl); + + data->gpiod_spkr_en = devm_gpiod_get_optional(dev, "spkr-en", GPIOD_OUT_LOW); + if (IS_ERR(data->gpiod_spkr_en)) + return PTR_ERR(data->gpiod_spkr_en); + + if (data->cfg->add_dapm_routes) { + ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); + if (ret) + return ret; + } + cpu = of_get_child_by_name(dev->of_node, "cpu"); if (!cpu) { dev_err(dev, "platform property missing or invalid\n"); return -EINVAL; } + codec = of_get_child_by_name(dev->of_node, "codec"); if (!codec) { dev_err(dev, "audio-codec property missing or invalid\n"); @@ -199,6 +312,12 @@ static int loongson_asoc_card_probe(struct platform_device *pdev) card->owner = THIS_MODULE; card->dai_link = loongson_dai_links; card->num_links = ARRAY_SIZE(loongson_dai_links); + + if (ls_priv->cfg->add_dapm_widgets) { + card->dapm_widgets = loongson_aosc_dapm_widgets; + card->num_dapm_widgets = ARRAY_SIZE(loongson_aosc_dapm_widgets); + } + snd_soc_card_set_drvdata(card, ls_priv); ret = device_property_read_string(dev, "model", &card->name); @@ -227,6 +346,10 @@ static const struct of_device_id loongson_asoc_dt_ids[] = { .compatible = "loongson,ls2k0300-forever-pi-audio-card", .data = &ls2k0300_forever_pi_card_config }, + { + .compatible = "loongson,ls2k0300-dl2k0300b-audio-card", + .data = &ls2k0300_dl2k0300b_card_config + }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, loongson_asoc_dt_ids); -- cgit v1.2.3 From 5f988d622318e5f485c60a8be661a81ed9f74c53 Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Fri, 26 Jun 2026 10:27:49 +0800 Subject: ASoC: es8328: Add DAPM routes from MIC inputs to Mic Bias The ES8328 codec has differential/single-ended microphone inputs (LINPUT1/RINPUT1, LINPUT2/RINPUT2) that require connection to the internal Mic Bias generator for proper operation. Currently, these routes are missing, which can cause microphone recording to fail. Add the missing DAPM routes to link the input pins to the Mic Bias supply, ensuring the microphone bias voltage is correctly applied. Signed-off-by: Binbin Zhou Link: https://patch.msgid.link/dcf1f8ae4f1f192a1d63e9fe7044b0218119b5eb.1782439646.git.zhoubinbin@loongson.cn Signed-off-by: Mark Brown --- sound/soc/codecs/es8328.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index 9838fe42cb6f..aaa6646ad4c5 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -405,6 +405,11 @@ static const struct snd_soc_dapm_route es8328_dapm_routes[] = { { "Mic Bias", NULL, "Mic Bias Gen" }, + { "LINPUT1", NULL, "Mic Bias" }, + { "RINPUT1", NULL, "Mic Bias" }, + { "LINPUT2", NULL, "Mic Bias" }, + { "RINPUT2", NULL, "Mic Bias" }, + { "Left Mixer", NULL, "Left DAC" }, { "Left Mixer", "Left Bypass Switch", "Left Line Mux" }, { "Left Mixer", "Right Playback Switch", "Right DAC" }, -- cgit v1.2.3