summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-02-11 22:43:00 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2026-02-11 22:43:00 +0300
commitd061251387903e8502843ac983553f0b2e098ef8 (patch)
treeb3714311b687ef033638b13879c12c16c4393ee1
parent0d6dd4738dbcc32b60c0c0c1388d41e171b76845 (diff)
parentdd03dd60e8cdd5ef0f0cbc18276c45009bcc51f4 (diff)
downloadlinux-d061251387903e8502843ac983553f0b2e098ef8.tar.xz
Merge tag 'sound-7.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai: "It's been relatively calm for a new era; majority of changes are for ASoC, mostly device-specific changes, while there are a bit of cleanups in core stuff. A few SPI API and regmap updates are included to be used by sound drivers, too. Core: - A few trivial cleanups about __free() and runtime PM macros - Convert to new snd_seq_bus binding ASoC: - Generic SDCA support for reporting jack events - Continuing platform support, cleanup and feature improvements for AMD, Intel, Qualcomm and SOF code - Platform description improvements for the Cirrus drivers - Support for NXP i.MX952, Realtek RT1320 and RT5575, and Sophogo CV1800B HD- and USB-audio: - Many quirks as usual" * tag 'sound-7.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (341 commits) ALSA: usb-audio: Add DSD support for iBasso DC04U ALSA: mixer: oss: Add card disconnect checkpoints ASoC: SOF: ipc4-control: Set correct error code in refresh_bytes_control ASoC: SOF: Intel: select CONFIG_SND_HDA_EXT_CORE from SND_SOC_SOF_HDA_COMMON ALSA: usb-audio: Add iface reset and delay quirk for AB13X USB Audio ASoC: amd: maintainer information ALSA: ctxfi: Add quirk for SE-300PCIE variant (160b:0102) ALSA: hda/generic: fix typos in comments ALSA: hda/realtek - Enable mute LEDs on HP ENVY x360 15-es0xxx ALSA: hda/conexant: Add quirk for HP ZBook Studio G4 ASoC: fsl_asrc_dma: allocate memory from dma device ASoC: fsl_asrc: Add support for i.MX952 platform ASoC: fsl_asrc_m2m: Add option to start ASRC before DMA device for M2M ASoC: dt-bindings: fsl,imx-asrc: Add support for i.MX952 platform ALSA: oss: delete self assignment ASoC: rockchip: spdif: Convert to FIELD_PREP ASoC: rockchip: spdif: Fill IEC958 CS info per params ASoC: rockchip: spdif: Add support for format S32_LE ASoC: rockchip: spdif: Add support for set mclk rate ASoC: rockchip: spdif: Swap PCM and DAI component registration order ...
-rw-r--r--Documentation/devicetree/bindings/goldfish/audio.txt17
-rw-r--r--Documentation/devicetree/bindings/sound/awinic,aw87390.yaml34
-rw-r--r--Documentation/devicetree/bindings/sound/awinic,aw88395.yaml13
-rw-r--r--Documentation/devicetree/bindings/sound/davinci-mcasp-audio.yaml71
-rw-r--r--Documentation/devicetree/bindings/sound/everest,es8389.yaml12
-rw-r--r--Documentation/devicetree/bindings/sound/fsl,audmix.yaml16
-rw-r--r--Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml1
-rw-r--r--Documentation/devicetree/bindings/sound/fsl,mqs.yaml12
-rw-r--r--Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml22
-rw-r--r--Documentation/devicetree/bindings/sound/fsl,sai.yaml16
-rw-r--r--Documentation/devicetree/bindings/sound/google,goldfish-audio.yaml38
-rw-r--r--Documentation/devicetree/bindings/sound/mt8192-afe-pcm.yaml176
-rw-r--r--Documentation/devicetree/bindings/sound/realtek,rt5575.yaml61
-rw-r--r--Documentation/devicetree/bindings/sound/realtek,rt5651.yaml100
-rw-r--r--Documentation/devicetree/bindings/sound/richtek,rtq9128.yaml12
-rw-r--r--Documentation/devicetree/bindings/sound/rt5651.txt63
-rw-r--r--Documentation/devicetree/bindings/sound/sophgo,cv1800b-codecs.yaml46
-rw-r--r--Documentation/devicetree/bindings/sound/sophgo,cv1800b-i2s.yaml67
-rw-r--r--Documentation/devicetree/bindings/sound/tas2552.txt36
-rw-r--r--Documentation/devicetree/bindings/sound/ti,tas2552.yaml62
-rw-r--r--Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml7
-rw-r--r--MAINTAINERS9
-rw-r--r--drivers/firmware/cirrus/cs_dsp.c50
-rw-r--r--drivers/firmware/cirrus/test/cs_dsp_mock_bin.c38
-rw-r--r--drivers/firmware/cirrus/test/cs_dsp_mock_mem_maps.c8
-rw-r--r--drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c16
-rw-r--r--drivers/firmware/cirrus/test/cs_dsp_test_bin.c1033
-rw-r--r--drivers/firmware/cirrus/test/cs_dsp_test_bin_error.c14
-rw-r--r--drivers/spi/spi.c23
-rw-r--r--include/linux/firmware/cirrus/cs_dsp_test_utils.h6
-rw-r--r--include/linux/firmware/cirrus/wmfw.h7
-rw-r--r--include/linux/firmware/imx/sm.h2
-rw-r--r--include/linux/pci_ids.h4
-rw-r--r--include/linux/platform_data/davinci_asp.h3
-rw-r--r--include/linux/soc/qcom/apr.h6
-rw-r--r--include/linux/spi/spi.h9
-rw-r--r--include/sound/ak4641.h23
-rw-r--r--include/sound/cs-amp-lib.h3
-rw-r--r--include/sound/cs35l56.h37
-rw-r--r--include/sound/sdca_function.h4
-rw-r--r--include/sound/sdca_interrupts.h7
-rw-r--r--include/sound/sdca_jack.h32
-rw-r--r--include/sound/seq_device.h2
-rw-r--r--include/sound/soc-acpi-intel-ssp-common.h4
-rw-r--r--include/sound/soc-component.h29
-rw-r--r--include/sound/soc-dapm.h44
-rw-r--r--include/sound/soc.h4
-rw-r--r--include/sound/sof.h3
-rw-r--r--include/sound/sof/ipc4/header.h75
-rw-r--r--include/sound/tas2781.h3
-rw-r--r--include/uapi/sound/sof/tokens.h6
-rw-r--r--sound/arm/Kconfig10
-rw-r--r--sound/arm/Makefile3
-rw-r--r--sound/arm/pxa2xx-ac97-lib.c50
-rw-r--r--sound/arm/pxa2xx-ac97.c286
-rw-r--r--sound/core/compress_offload.c28
-rw-r--r--sound/core/control.c12
-rw-r--r--sound/core/control_compat.c21
-rw-r--r--sound/core/control_led.c12
-rw-r--r--sound/core/ctljack.c18
-rw-r--r--sound/core/oss/mixer_oss.c80
-rw-r--r--sound/core/oss/pcm_oss.c19
-rw-r--r--sound/core/pcm.c4
-rw-r--r--sound/core/pcm_compat.c9
-rw-r--r--sound/core/pcm_native.c46
-rw-r--r--sound/core/seq/oss/seq_oss.c4
-rw-r--r--sound/core/seq/oss/seq_oss_init.c4
-rw-r--r--sound/core/seq/oss/seq_oss_midi.c45
-rw-r--r--sound/core/seq/oss/seq_oss_synth.c35
-rw-r--r--sound/core/seq/oss/seq_oss_synth.h4
-rw-r--r--sound/core/seq/seq_clientmgr.c171
-rw-r--r--sound/core/seq/seq_compat.c4
-rw-r--r--sound/core/seq/seq_midi.c25
-rw-r--r--sound/core/seq/seq_ports.c11
-rw-r--r--sound/core/seq/seq_queue.c32
-rw-r--r--sound/core/seq/seq_ump_client.c27
-rw-r--r--sound/core/seq/seq_virmidi.c4
-rw-r--r--sound/core/seq_device.c26
-rw-r--r--sound/core/timer.c12
-rw-r--r--sound/core/vmaster.c12
-rw-r--r--sound/drivers/opl3/opl3_seq.c13
-rw-r--r--sound/drivers/opl4/opl4_seq.c13
-rw-r--r--sound/hda/codecs/cirrus/Kconfig1
-rw-r--r--sound/hda/codecs/cirrus/cs8409-tables.c76
-rw-r--r--sound/hda/codecs/cirrus/cs8409.c172
-rw-r--r--sound/hda/codecs/cirrus/cs8409.h13
-rw-r--r--sound/hda/codecs/cm9825.c290
-rw-r--r--sound/hda/codecs/conexant.c2
-rw-r--r--sound/hda/codecs/generic.c8
-rw-r--r--sound/hda/codecs/realtek/alc269.c274
-rw-r--r--sound/hda/codecs/realtek/realtek.c5
-rw-r--r--sound/hda/codecs/side-codecs/cirrus_scodec_test.c61
-rw-r--r--sound/hda/codecs/side-codecs/cs35l41_hda.c23
-rw-r--r--sound/hda/codecs/side-codecs/cs35l56_hda.c36
-rw-r--r--sound/hda/codecs/side-codecs/tas2781_hda.c9
-rw-r--r--sound/hda/codecs/side-codecs/tas2781_hda_i2c.c23
-rw-r--r--sound/hda/codecs/side-codecs/tas2781_hda_spi.c13
-rw-r--r--sound/hda/common/codec.c4
-rw-r--r--sound/hda/common/hda_jack.h4
-rw-r--r--sound/hda/common/hda_local.h2
-rw-r--r--sound/hda/common/sysfs.c5
-rw-r--r--sound/hda/controllers/intel.c3
-rw-r--r--sound/hda/controllers/tegra.c14
-rw-r--r--sound/hda/core/intel-dsp-config.c8
-rw-r--r--sound/isa/sb/emu8000_synth.c13
-rw-r--r--sound/pci/ctxfi/ctatc.c11
-rw-r--r--sound/pci/echoaudio/echoaudio.c2
-rw-r--r--sound/pci/echoaudio/echoaudio_dsp.c6
-rw-r--r--sound/pci/emu10k1/emu10k1_synth.c13
-rw-r--r--sound/pci/ice1712/ice1724.c2
-rw-r--r--sound/pci/mixart/mixart.h2
-rw-r--r--sound/soc/Kconfig1
-rw-r--r--sound/soc/Makefile1
-rw-r--r--sound/soc/amd/Kconfig2
-rw-r--r--sound/soc/amd/acp/Kconfig9
-rw-r--r--sound/soc/amd/acp/Makefile2
-rw-r--r--sound/soc/amd/acp/acp-sdw-legacy-mach.c8
-rw-r--r--sound/soc/amd/acp/acp-sdw-sof-mach.c8
-rw-r--r--sound/soc/amd/acp/amd-acp70-acpi-match.c380
-rw-r--r--sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.c42
-rw-r--r--sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.h25
-rw-r--r--sound/soc/codecs/Kconfig50
-rw-r--r--sound/soc/codecs/Makefile9
-rw-r--r--sound/soc/codecs/ak4641.c641
-rw-r--r--sound/soc/codecs/alc5623.c5
-rw-r--r--sound/soc/codecs/aw87390.c175
-rw-r--r--sound/soc/codecs/aw87390.h86
-rw-r--r--sound/soc/codecs/aw88261.c16
-rw-r--r--sound/soc/codecs/aw88261.h2
-rw-r--r--sound/soc/codecs/cs-amp-lib-test.c169
-rw-r--r--sound/soc/codecs/cs-amp-lib.c158
-rw-r--r--sound/soc/codecs/cs35l56-shared-test.c680
-rw-r--r--sound/soc/codecs/cs35l56-shared.c172
-rw-r--r--sound/soc/codecs/cs35l56-test.c639
-rw-r--r--sound/soc/codecs/cs35l56.c215
-rw-r--r--sound/soc/codecs/cs35l56.h6
-rw-r--r--sound/soc/codecs/cs4271.c12
-rw-r--r--sound/soc/codecs/da7213.c4
-rw-r--r--sound/soc/codecs/es8323.c294
-rw-r--r--sound/soc/codecs/es8323.h105
-rw-r--r--sound/soc/codecs/es8326.c18
-rw-r--r--sound/soc/codecs/es8326.h3
-rw-r--r--sound/soc/codecs/es8328.c160
-rw-r--r--sound/soc/codecs/es8375.c2
-rw-r--r--sound/soc/codecs/es8389.c246
-rw-r--r--sound/soc/codecs/es8389.h8
-rw-r--r--sound/soc/codecs/max98390.c3
-rw-r--r--sound/soc/codecs/nau8821.c48
-rw-r--r--sound/soc/codecs/nau8821.h1
-rw-r--r--sound/soc/codecs/rt1320-sdw.c1227
-rw-r--r--sound/soc/codecs/rt1320-sdw.h66
-rw-r--r--sound/soc/codecs/rt274.c12
-rw-r--r--sound/soc/codecs/rt286.c12
-rw-r--r--sound/soc/codecs/rt298.c12
-rw-r--r--sound/soc/codecs/rt5575-spi.c118
-rw-r--r--sound/soc/codecs/rt5575-spi.h27
-rw-r--r--sound/soc/codecs/rt5575.c352
-rw-r--r--sound/soc/codecs/rt5575.h58
-rw-r--r--sound/soc/codecs/rt5640.c4
-rw-r--r--sound/soc/codecs/rt5651.c2
-rw-r--r--sound/soc/codecs/rt5659.c2
-rw-r--r--sound/soc/codecs/rt5663.c4
-rw-r--r--sound/soc/codecs/rt5665.c2
-rw-r--r--sound/soc/codecs/rt5668.c2
-rw-r--r--sound/soc/codecs/rt5682-i2c.c2
-rw-r--r--sound/soc/codecs/rtq9128.c114
-rw-r--r--sound/soc/codecs/sdw-mockup.c6
-rw-r--r--sound/soc/codecs/tas2780.c31
-rw-r--r--sound/soc/codecs/tas2781-fmwlib.c138
-rw-r--r--sound/soc/codecs/tas2781-i2c.c11
-rw-r--r--sound/soc/codecs/tas2783-sdw.c356
-rw-r--r--sound/soc/codecs/tas2783.h1
-rw-r--r--sound/soc/codecs/tlv320adcx140.c136
-rw-r--r--sound/soc/codecs/wcd-mbhc-v2.c12
-rw-r--r--sound/soc/codecs/wcd939x-sdw.c19
-rw-r--r--sound/soc/codecs/wm0010.c2
-rw-r--r--sound/soc/codecs/wm8731.c11
-rw-r--r--sound/soc/codecs/wm8962.c47
-rw-r--r--sound/soc/fsl/Kconfig6
-rw-r--r--sound/soc/fsl/fsl_asrc.c38
-rw-r--r--sound/soc/fsl/fsl_asrc.h18
-rw-r--r--sound/soc/fsl/fsl_asrc_common.h4
-rw-r--r--sound/soc/fsl/fsl_asrc_dma.c48
-rw-r--r--sound/soc/fsl/fsl_asrc_m2m.c8
-rw-r--r--sound/soc/fsl/fsl_audmix.c3
-rw-r--r--sound/soc/fsl/fsl_sai.c21
-rw-r--r--sound/soc/fsl/fsl_sai.h4
-rw-r--r--sound/soc/fsl/fsl_ssi.c2
-rw-r--r--sound/soc/fsl/imx-rpmsg.c2
-rw-r--r--sound/soc/intel/avs/board_selection.c2
-rw-r--r--sound/soc/intel/avs/core.c2
-rw-r--r--sound/soc/intel/avs/path.c2
-rw-r--r--sound/soc/intel/boards/Kconfig5
-rw-r--r--sound/soc/intel/boards/Makefile3
-rw-r--r--sound/soc/intel/boards/sof_rt5682.c6
-rw-r--r--sound/soc/intel/boards/sof_sdw.c43
-rw-r--r--sound/soc/intel/boards/sof_ti_common.c77
-rw-r--r--sound/soc/intel/boards/sof_ti_common.h24
-rw-r--r--sound/soc/intel/catpt/core.h7
-rw-r--r--sound/soc/intel/catpt/device.c6
-rw-r--r--sound/soc/intel/catpt/ipc.c12
-rw-r--r--sound/soc/intel/catpt/loader.c8
-rw-r--r--sound/soc/intel/catpt/messages.c89
-rw-r--r--sound/soc/intel/catpt/pcm.c46
-rw-r--r--sound/soc/intel/catpt/sysfs.c2
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-arl-match.c23
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-mtl-match.c14
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-ptl-match.c55
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-ssp-common.c3
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-nau8825.c6
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-dai-etdm.c44
-rw-r--r--sound/soc/pxa/Kconfig1
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.c5
-rw-r--r--sound/soc/qcom/qdsp6/audioreach.c229
-rw-r--r--sound/soc/qcom/qdsp6/audioreach.h55
-rw-r--r--sound/soc/qcom/qdsp6/q6adm.c8
-rw-r--r--sound/soc/qcom/qdsp6/q6afe.c26
-rw-r--r--sound/soc/qcom/qdsp6/q6apm.c19
-rw-r--r--sound/soc/qcom/qdsp6/q6asm.c33
-rw-r--r--sound/soc/qcom/qdsp6/q6core.c6
-rw-r--r--sound/soc/qcom/qdsp6/q6prm.c22
-rw-r--r--sound/soc/qcom/qdsp6/topology.c118
-rw-r--r--sound/soc/renesas/rz-ssi.c143
-rw-r--r--sound/soc/rockchip/Kconfig1
-rw-r--r--sound/soc/rockchip/rockchip_sai.c5
-rw-r--r--sound/soc/rockchip/rockchip_spdif.c225
-rw-r--r--sound/soc/rockchip/rockchip_spdif.h57
-rw-r--r--sound/soc/sdca/Kconfig2
-rw-r--r--sound/soc/sdca/Makefile2
-rw-r--r--sound/soc/sdca/sdca_asoc.c91
-rw-r--r--sound/soc/sdca/sdca_class.c34
-rw-r--r--sound/soc/sdca/sdca_class.h2
-rw-r--r--sound/soc/sdca/sdca_class_function.c141
-rw-r--r--sound/soc/sdca/sdca_fdl.c8
-rw-r--r--sound/soc/sdca/sdca_functions.c75
-rw-r--r--sound/soc/sdca/sdca_interrupts.c188
-rw-r--r--sound/soc/sdca/sdca_jack.c248
-rw-r--r--sound/soc/sdca/sdca_regmap.c14
-rw-r--r--sound/soc/sdca/sdca_ump.c2
-rw-r--r--sound/soc/sdw_utils/soc_sdw_ti_amp.c4
-rw-r--r--sound/soc/sdw_utils/soc_sdw_utils.c39
-rw-r--r--sound/soc/soc-component.c82
-rw-r--r--sound/soc/soc-core.c10
-rw-r--r--sound/soc/soc-dapm.c352
-rw-r--r--sound/soc/sof/amd/acp-ipc.c4
-rw-r--r--sound/soc/sof/compress.c8
-rw-r--r--sound/soc/sof/control.c13
-rw-r--r--sound/soc/sof/core.c1
-rw-r--r--sound/soc/sof/debug.c7
-rw-r--r--sound/soc/sof/imx/imx-common.c8
-rw-r--r--sound/soc/sof/intel/Kconfig1
-rw-r--r--sound/soc/sof/intel/atom.c7
-rw-r--r--sound/soc/sof/intel/bdw.c7
-rw-r--r--sound/soc/sof/intel/cnl.c11
-rw-r--r--sound/soc/sof/intel/hda-codec.c1
-rw-r--r--sound/soc/sof/intel/hda-dai-ops.c25
-rw-r--r--sound/soc/sof/intel/hda-dai.c22
-rw-r--r--sound/soc/sof/intel/hda-ipc.c11
-rw-r--r--sound/soc/sof/intel/hda-loader.c94
-rw-r--r--sound/soc/sof/intel/hda-mlink.c29
-rw-r--r--sound/soc/sof/intel/hda-sdw-bpt.c16
-rw-r--r--sound/soc/sof/intel/hda-stream.c168
-rw-r--r--sound/soc/sof/intel/hda.c46
-rw-r--r--sound/soc/sof/intel/hda.h16
-rw-r--r--sound/soc/sof/intel/mtl.c5
-rw-r--r--sound/soc/sof/intel/nvl.c24
-rw-r--r--sound/soc/sof/intel/pci-apl.c2
-rw-r--r--sound/soc/sof/intel/pci-lnl.c1
-rw-r--r--sound/soc/sof/intel/pci-nvl.c32
-rw-r--r--sound/soc/sof/intel/pci-ptl.c2
-rw-r--r--sound/soc/sof/ipc.c9
-rw-r--r--sound/soc/sof/ipc3-dtrace.c7
-rw-r--r--sound/soc/sof/ipc3-topology.c6
-rw-r--r--sound/soc/sof/ipc3.c8
-rw-r--r--sound/soc/sof/ipc4-control.c198
-rw-r--r--sound/soc/sof/ipc4-loader.c19
-rw-r--r--sound/soc/sof/ipc4-mtrace.c24
-rw-r--r--sound/soc/sof/ipc4-pcm.c3
-rw-r--r--sound/soc/sof/ipc4-topology.c214
-rw-r--r--sound/soc/sof/ipc4-topology.h13
-rw-r--r--sound/soc/sof/ipc4.c64
-rw-r--r--sound/soc/sof/mediatek/mtk-adsp-common.c4
-rw-r--r--sound/soc/sof/ops.c34
-rw-r--r--sound/soc/sof/ops.h10
-rw-r--r--sound/soc/sof/pcm.c91
-rw-r--r--sound/soc/sof/pm.c149
-rw-r--r--sound/soc/sof/sof-audio.c231
-rw-r--r--sound/soc/sof/sof-audio.h20
-rw-r--r--sound/soc/sof/sof-client-ipc-flood-test.c7
-rw-r--r--sound/soc/sof/sof-client-ipc-kernel-injector.c4
-rw-r--r--sound/soc/sof/sof-client-ipc-msg-injector.c14
-rw-r--r--sound/soc/sof/sof-client-probes.c26
-rw-r--r--sound/soc/sof/sof-client.c46
-rw-r--r--sound/soc/sof/sof-client.h3
-rw-r--r--sound/soc/sof/sof-priv.h3
-rw-r--r--sound/soc/sophgo/Kconfig47
-rw-r--r--sound/soc/sophgo/Makefile5
-rw-r--r--sound/soc/sophgo/cv1800b-sound-adc.c319
-rw-r--r--sound/soc/sophgo/cv1800b-sound-dac.c208
-rw-r--r--sound/soc/sophgo/cv1800b-tdm.c716
-rw-r--r--sound/soc/sunxi/sun4i-spdif.c4
-rw-r--r--sound/soc/sunxi/sun50i-dmic.c3
-rw-r--r--sound/soc/tegra/tegra186_asrc.c1
-rw-r--r--sound/soc/tegra/tegra186_dspk.c1
-rw-r--r--sound/soc/tegra/tegra210_admaif.c3
-rw-r--r--sound/soc/tegra/tegra210_adx.c2
-rw-r--r--sound/soc/tegra/tegra210_ahub.c60
-rw-r--r--sound/soc/tegra/tegra210_ahub.h30
-rw-r--r--sound/soc/tegra/tegra210_amx.c3
-rw-r--r--sound/soc/tegra/tegra210_dmic.c1
-rw-r--r--sound/soc/tegra/tegra210_i2s.c2
-rw-r--r--sound/soc/tegra/tegra210_mbdrc.c1
-rw-r--r--sound/soc/tegra/tegra210_mixer.c1
-rw-r--r--sound/soc/tegra/tegra210_mvc.c1
-rw-r--r--sound/soc/tegra/tegra210_ope.c1
-rw-r--r--sound/soc/tegra/tegra210_peq.c1
-rw-r--r--sound/soc/tegra/tegra210_sfc.c1
-rw-r--r--sound/soc/ti/davinci-mcasp.c517
-rw-r--r--sound/soc/ti/davinci-mcasp.h10
-rw-r--r--sound/usb/fcp.c36
-rw-r--r--sound/usb/mixer_quirks.c3
-rw-r--r--sound/usb/mixer_s1810c.c224
-rw-r--r--sound/usb/mixer_scarlett.c476
-rw-r--r--sound/usb/mixer_scarlett.h1
-rw-r--r--sound/usb/mixer_scarlett2.c21
-rw-r--r--sound/usb/quirks.c17
-rw-r--r--sound/usb/usx2y/us144mkii.c4
-rw-r--r--sound/usb/usx2y/us144mkii_controls.c4
-rw-r--r--sound/usb/usx2y/us144mkii_pcm.c4
-rw-r--r--sound/x86/intel_hdmi_audio.c10
330 files changed, 14410 insertions, 4688 deletions
diff --git a/Documentation/devicetree/bindings/goldfish/audio.txt b/Documentation/devicetree/bindings/goldfish/audio.txt
deleted file mode 100644
index d043fda433ba..000000000000
--- a/Documentation/devicetree/bindings/goldfish/audio.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-Android Goldfish Audio
-
-Android goldfish audio device generated by android emulator.
-
-Required properties:
-
-- compatible : should contain "google,goldfish-audio" to match emulator
-- reg : <registers mapping>
-- interrupts : <interrupt mapping>
-
-Example:
-
- goldfish_audio@9030000 {
- compatible = "google,goldfish-audio";
- reg = <0x9030000 0x100>;
- interrupts = <0x4>;
- };
diff --git a/Documentation/devicetree/bindings/sound/awinic,aw87390.yaml b/Documentation/devicetree/bindings/sound/awinic,aw87390.yaml
index ba9d8767c5d5..9c1baae767c4 100644
--- a/Documentation/devicetree/bindings/sound/awinic,aw87390.yaml
+++ b/Documentation/devicetree/bindings/sound/awinic,aw87390.yaml
@@ -15,12 +15,15 @@ description:
sound quallity, which is a new high efficiency, low
noise, constant large volume, 6th Smart K audio amplifier.
-allOf:
- - $ref: dai-common.yaml#
-
properties:
compatible:
- const: awinic,aw87390
+ oneOf:
+ - enum:
+ - awinic,aw87390
+ - items:
+ - enum:
+ - anbernic,rgds-amp
+ - const: awinic,aw87391
reg:
maxItems: 1
@@ -40,10 +43,31 @@ required:
- compatible
- reg
- "#sound-dai-cells"
- - awinic,audio-channel
unevaluatedProperties: false
+allOf:
+ - $ref: dai-common.yaml#
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - awinic,aw87390
+ then:
+ required:
+ - awinic,audio-channel
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - anbernic,rgds-amp
+ then:
+ properties:
+ vdd-supply: true
+
examples:
- |
i2c {
diff --git a/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml b/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml
index bb92d6ca3144..994d68c074a9 100644
--- a/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml
+++ b/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml
@@ -33,6 +33,8 @@ properties:
reset-gpios:
maxItems: 1
+ dvdd-supply: true
+
awinic,audio-channel:
description:
It is used to distinguish multiple PA devices, so that different
@@ -65,6 +67,17 @@ allOf:
then:
properties:
reset-gpios: false
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: awinic,aw88261
+ then:
+ required:
+ - dvdd-supply
+ else:
+ properties:
+ dvdd-supply: false
unevaluatedProperties: false
diff --git a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.yaml b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.yaml
index beef193aaaeb..87559d0d079a 100644
--- a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.yaml
+++ b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.yaml
@@ -40,11 +40,33 @@ properties:
tdm-slots:
$ref: /schemas/types.yaml#/definitions/uint32
description:
- number of channels over one serializer
- the property is ignored in DIT mode
+ Number of channels over one serializer. This property
+ specifies the TX playback TDM slot count, along with default RX slot count
+ if tdm-slots-rx is not specified.
+ The property is ignored in DIT mode.
minimum: 2
maximum: 32
+ tdm-slots-rx:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ Number of RX capture channels over one serializer. If specified,
+ allows independent RX TDM slot count separate from TX. Requires
+ ti,async-mode to be enabled for independent TX/RX clock rates.
+ The property is ignored in DIT mode.
+ minimum: 2
+ maximum: 32
+
+ ti,async-mode:
+ description:
+ Specify to allow independent TX & RX clocking,
+ to enable audio playback & record with different sampling rate,
+ and different number of bits per frame.
+ if property is omitted, TX and RX will share same bit clock and frame clock signals,
+ thus RX need to use same bits per frame and sampling rate as TX in synchronous mode.
+ the property is ignored in DIT mode (as DIT is TX-only)
+ type: boolean
+
serial-dir:
description:
A list of serializer configuration
@@ -125,7 +147,21 @@ properties:
auxclk-fs-ratio:
$ref: /schemas/types.yaml#/definitions/uint32
- description: ratio of AUCLK and FS rate if applicable
+ description:
+ Ratio of AUCLK and FS rate if applicable. This property specifies
+ the TX ratio, along with default RX ratio if auxclk-fs-ratio-rx
+ is not specified.
+ When not specified, the inputted system clock frequency via set_sysclk
+ callback by the machine driver is used for divider calculation.
+
+ auxclk-fs-ratio-rx:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ Ratio of AUCLK and FS rate for RX. If specified, allows
+ for a different RX ratio. Requires ti,async-mode to be
+ enabled when the ratio differs from auxclk-fs-ratio.
+ When not specified, it defaults to the value of auxclk-fs-ratio.
+ The property is ignored in DIT mode.
gpio-controller: true
@@ -170,14 +206,38 @@ allOf:
- $ref: dai-common.yaml#
- if:
properties:
- opmode:
+ op-mode:
enum:
- 0
-
then:
required:
- tdm-slots
+ - if:
+ properties:
+ op-mode:
+ const: 1
+ then:
+ properties:
+ tdm-slots: false
+ tdm-slots-rx: false
+ ti,async-mode: false
+ auxclk-fs-ratio-rx: false
+
+ - if:
+ required:
+ - tdm-slots-rx
+ then:
+ required:
+ - ti,async-mode
+
+ - if:
+ required:
+ - auxclk-fs-ratio-rx
+ then:
+ required:
+ - ti,async-mode
+
unevaluatedProperties: false
examples:
@@ -190,6 +250,7 @@ examples:
interrupt-names = "tx", "rx";
op-mode = <0>; /* MCASP_IIS_MODE */
tdm-slots = <2>;
+ ti,async-mode;
dmas = <&main_udmap 0xc400>, <&main_udmap 0x4400>;
dma-names = "tx", "rx";
serial-dir = <
diff --git a/Documentation/devicetree/bindings/sound/everest,es8389.yaml b/Documentation/devicetree/bindings/sound/everest,es8389.yaml
index a673df485ab3..75ce0bc48904 100644
--- a/Documentation/devicetree/bindings/sound/everest,es8389.yaml
+++ b/Documentation/devicetree/bindings/sound/everest,es8389.yaml
@@ -30,10 +30,20 @@ properties:
"#sound-dai-cells":
const: 0
+ vdda-supply:
+ description:
+ Analogue power supply.
+
+ vddd-supply:
+ description:
+ Interface power supply.
+
required:
- compatible
- reg
- "#sound-dai-cells"
+ - vddd-supply
+ - vdda-supply
additionalProperties: false
@@ -46,5 +56,7 @@ examples:
compatible = "everest,es8389";
reg = <0x10>;
#sound-dai-cells = <0>;
+ vddd-supply = <&vdd3v3>;
+ vdda-supply = <&vdd3v3>;
};
};
diff --git a/Documentation/devicetree/bindings/sound/fsl,audmix.yaml b/Documentation/devicetree/bindings/sound/fsl,audmix.yaml
index 3ad197b3c82c..07b9a38761f2 100644
--- a/Documentation/devicetree/bindings/sound/fsl,audmix.yaml
+++ b/Documentation/devicetree/bindings/sound/fsl,audmix.yaml
@@ -34,7 +34,9 @@ description: |
properties:
compatible:
- const: fsl,imx8qm-audmix
+ enum:
+ - fsl,imx8qm-audmix
+ - fsl,imx952-audmix
reg:
maxItems: 1
@@ -80,7 +82,17 @@ required:
- reg
- clocks
- clock-names
- - power-domains
+
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - fsl,imx8qm-audmix
+ then:
+ required:
+ - power-domains
unevaluatedProperties: false
diff --git a/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml b/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml
index c9152bac7421..608defc93c1e 100644
--- a/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml
+++ b/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml
@@ -25,6 +25,7 @@ properties:
- fsl,imx53-asrc
- fsl,imx8qm-asrc
- fsl,imx8qxp-asrc
+ - fsl,imx952-asrc
- items:
- enum:
- fsl,imx6sx-asrc
diff --git a/Documentation/devicetree/bindings/sound/fsl,mqs.yaml b/Documentation/devicetree/bindings/sound/fsl,mqs.yaml
index 1415247c92c8..bcc265a742c7 100644
--- a/Documentation/devicetree/bindings/sound/fsl,mqs.yaml
+++ b/Documentation/devicetree/bindings/sound/fsl,mqs.yaml
@@ -68,6 +68,16 @@ allOf:
compatible:
contains:
enum:
+ - fsl,imx6sx-mqs
+ - fsl,imx93-mqs
+ then:
+ required:
+ - gpr
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
- fsl,imx8qm-mqs
- fsl,imx8qxp-mqs
then:
@@ -91,8 +101,6 @@ allOf:
clock-names:
items:
- const: mclk
- required:
- - gpr
unevaluatedProperties: false
diff --git a/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml b/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml
index 3d5d435c765b..3a32f7517d0c 100644
--- a/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml
+++ b/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml
@@ -22,14 +22,20 @@ allOf:
properties:
compatible:
- enum:
- - fsl,imx7ulp-rpmsg-audio
- - fsl,imx8mn-rpmsg-audio
- - fsl,imx8mm-rpmsg-audio
- - fsl,imx8mp-rpmsg-audio
- - fsl,imx8ulp-rpmsg-audio
- - fsl,imx93-rpmsg-audio
- - fsl,imx95-rpmsg-audio
+ oneOf:
+ - enum:
+ - fsl,imx7ulp-rpmsg-audio
+ - fsl,imx8mn-rpmsg-audio
+ - fsl,imx8mm-rpmsg-audio
+ - fsl,imx8mp-rpmsg-audio
+ - fsl,imx8ulp-rpmsg-audio
+ - fsl,imx93-rpmsg-audio
+ - fsl,imx95-rpmsg-audio
+ - items:
+ - enum:
+ - fsl,imx94-rpmsg-audio
+ - fsl,imx952-rpmsg-audio
+ - const: fsl,imx95-rpmsg-audio
clocks:
items:
diff --git a/Documentation/devicetree/bindings/sound/fsl,sai.yaml b/Documentation/devicetree/bindings/sound/fsl,sai.yaml
index d838ee0b61cb..83b5ea5f3d70 100644
--- a/Documentation/devicetree/bindings/sound/fsl,sai.yaml
+++ b/Documentation/devicetree/bindings/sound/fsl,sai.yaml
@@ -133,6 +133,13 @@ properties:
- description: dataline mask for 'rx'
- description: dataline mask for 'tx'
+ fsl,sai-amix-mode:
+ $ref: /schemas/types.yaml#/definitions/string
+ description:
+ The audmix module is bypassed from hardware or not.
+ enum: [none, bypass, audmix]
+ default: none
+
fsl,sai-mclk-direction-output:
description: SAI will output the SAI MCLK clock.
type: boolean
@@ -180,6 +187,15 @@ allOf:
properties:
fsl,sai-synchronous-rx: false
+ - if:
+ required:
+ - fsl,sai-amix-mode
+ then:
+ properties:
+ compatible:
+ contains:
+ const: fsl,imx952-sai
+
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/sound/google,goldfish-audio.yaml b/Documentation/devicetree/bindings/sound/google,goldfish-audio.yaml
new file mode 100644
index 000000000000..d395a5cbc945
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/google,goldfish-audio.yaml
@@ -0,0 +1,38 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/google,goldfish-audio.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Android Goldfish Audio
+
+maintainers:
+ - Kuan-Wei Chiu <visitorckw@gmail.com>
+
+description:
+ Android goldfish audio device generated by Android emulator.
+
+properties:
+ compatible:
+ const: google,goldfish-audio
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - interrupts
+
+additionalProperties: false
+
+examples:
+ - |
+ sound@9030000 {
+ compatible = "google,goldfish-audio";
+ reg = <0x9030000 0x100>;
+ interrupts = <4>;
+ };
diff --git a/Documentation/devicetree/bindings/sound/mt8192-afe-pcm.yaml b/Documentation/devicetree/bindings/sound/mt8192-afe-pcm.yaml
index 8ddf49b0040d..16ae3328f70d 100644
--- a/Documentation/devicetree/bindings/sound/mt8192-afe-pcm.yaml
+++ b/Documentation/devicetree/bindings/sound/mt8192-afe-pcm.yaml
@@ -47,16 +47,118 @@ properties:
- description: AFE clock
- description: ADDA DAC clock
- description: ADDA DAC pre-distortion clock
- - description: audio infra sys clock
- - description: audio infra 26M clock
+ - description: ADDA ADC clock
+ - description: ADDA6 ADC clock
+ - description: Audio low-jitter 22.5792m clock
+ - description: Audio low-jitter 24.576m clock
+ - description: Audio PLL1 tuner clock
+ - description: Audio PLL2 tuner clock
+ - description: Audio Time-Division Multiplexing interface clock
+ - description: ADDA ADC Sine Generator clock
+ - description: audio Non-LE clock
+ - description: Audio DAC High-Resolution clock
+ - description: Audio High-Resolution ADC clock
+ - description: Audio High-Resolution ADC SineGen clock
+ - description: Audio ADDA6 High-Resolution ADC clock
+ - description: Tertiary ADDA DAC clock
+ - description: Tertiary ADDA DAC pre-distortion clock
+ - description: Tertiary ADDA DAC Sine Generator clock
+ - description: Tertiary ADDA DAC High-Resolution clock
+ - description: Audio infra sys clock
+ - description: Audio infra 26M clock
+ - description: Mux for audio clock
+ - description: Mux for audio internal bus clock
+ - description: Mux main divider by 4
+ - description: Primary audio mux
+ - description: Primary audio PLL
+ - description: Secondary audio mux
+ - description: Secondary audio PLL
+ - description: Primary audio en-generator clock
+ - description: Primary PLL divider by 4 for IEC
+ - description: Secondary audio en-generator clock
+ - description: Secondary PLL divider by 4 for IEC
+ - description: Mux selector for I2S port 0
+ - description: Mux selector for I2S port 1
+ - description: Mux selector for I2S port 2
+ - description: Mux selector for I2S port 3
+ - description: Mux selector for I2S port 4
+ - description: Mux selector for I2S port 5
+ - description: Mux selector for I2S port 6
+ - description: Mux selector for I2S port 7
+ - description: Mux selector for I2S port 8
+ - description: Mux selector for I2S port 9
+ - description: APLL1 and APLL2 divider for I2S port 0
+ - description: APLL1 and APLL2 divider for I2S port 1
+ - description: APLL1 and APLL2 divider for I2S port 2
+ - description: APLL1 and APLL2 divider for I2S port 3
+ - description: APLL1 and APLL2 divider for I2S port 4
+ - description: APLL1 and APLL2 divider for IEC
+ - description: APLL1 and APLL2 divider for I2S port 5
+ - description: APLL1 and APLL2 divider for I2S port 6
+ - description: APLL1 and APLL2 divider for I2S port 7
+ - description: APLL1 and APLL2 divider for I2S port 8
+ - description: APLL1 and APLL2 divider for I2S port 9
+ - description: Top mux for audio subsystem
+ - description: 26MHz clock for audio subsystem
clock-names:
items:
- const: aud_afe_clk
- const: aud_dac_clk
- const: aud_dac_predis_clk
+ - const: aud_adc_clk
+ - const: aud_adda6_adc_clk
+ - const: aud_apll22m_clk
+ - const: aud_apll24m_clk
+ - const: aud_apll1_tuner_clk
+ - const: aud_apll2_tuner_clk
+ - const: aud_tdm_clk
+ - const: aud_tml_clk
+ - const: aud_nle
+ - const: aud_dac_hires_clk
+ - const: aud_adc_hires_clk
+ - const: aud_adc_hires_tml
+ - const: aud_adda6_adc_hires_clk
+ - const: aud_3rd_dac_clk
+ - const: aud_3rd_dac_predis_clk
+ - const: aud_3rd_dac_tml
+ - const: aud_3rd_dac_hires_clk
- const: aud_infra_clk
- const: aud_infra_26m_clk
+ - const: top_mux_audio
+ - const: top_mux_audio_int
+ - const: top_mainpll_d4_d4
+ - const: top_mux_aud_1
+ - const: top_apll1_ck
+ - const: top_mux_aud_2
+ - const: top_apll2_ck
+ - const: top_mux_aud_eng1
+ - const: top_apll1_d4
+ - const: top_mux_aud_eng2
+ - const: top_apll2_d4
+ - const: top_i2s0_m_sel
+ - const: top_i2s1_m_sel
+ - const: top_i2s2_m_sel
+ - const: top_i2s3_m_sel
+ - const: top_i2s4_m_sel
+ - const: top_i2s5_m_sel
+ - const: top_i2s6_m_sel
+ - const: top_i2s7_m_sel
+ - const: top_i2s8_m_sel
+ - const: top_i2s9_m_sel
+ - const: top_apll12_div0
+ - const: top_apll12_div1
+ - const: top_apll12_div2
+ - const: top_apll12_div3
+ - const: top_apll12_div4
+ - const: top_apll12_divb
+ - const: top_apll12_div5
+ - const: top_apll12_div6
+ - const: top_apll12_div7
+ - const: top_apll12_div8
+ - const: top_apll12_div9
+ - const: top_mux_audio_h
+ - const: top_clk26m_clk
required:
- compatible
@@ -83,23 +185,69 @@ examples:
afe: mt8192-afe-pcm {
compatible = "mediatek,mt8192-audio";
interrupts = <GIC_SPI 202 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&audsys CLK_AUD_AFE>, <&audsys CLK_AUD_DAC>,
+ <&audsys CLK_AUD_DAC_PREDIS>, <&audsys CLK_AUD_ADC>,
+ <&audsys CLK_AUD_ADDA6_ADC>, <&audsys CLK_AUD_22M>,
+ <&audsys CLK_AUD_24M>, <&audsys CLK_AUD_APLL_TUNER>,
+ <&audsys CLK_AUD_APLL2_TUNER>, <&audsys CLK_AUD_TDM>,
+ <&audsys CLK_AUD_TML>, <&audsys CLK_AUD_NLE>,
+ <&audsys CLK_AUD_DAC_HIRES>, <&audsys CLK_AUD_ADC_HIRES>,
+ <&audsys CLK_AUD_ADC_HIRES_TML>, <&audsys CLK_AUD_ADDA6_ADC_HIRES>,
+ <&audsys CLK_AUD_3RD_DAC>, <&audsys CLK_AUD_3RD_DAC_PREDIS>,
+ <&audsys CLK_AUD_3RD_DAC_TML>, <&audsys CLK_AUD_3RD_DAC_HIRES>,
+ <&infracfg CLK_INFRA_AUDIO>, <&infracfg CLK_INFRA_AUDIO_26M_B>,
+ <&topckgen CLK_TOP_AUDIO_SEL>, <&topckgen CLK_TOP_AUD_INTBUS_SEL>,
+ <&topckgen CLK_TOP_MAINPLL_D4_D4>, <&topckgen CLK_TOP_AUD_1_SEL>,
+ <&topckgen CLK_TOP_APLL1>, <&topckgen CLK_TOP_AUD_2_SEL>,
+ <&topckgen CLK_TOP_APLL2>, <&topckgen CLK_TOP_AUD_ENGEN1_SEL>,
+ <&topckgen CLK_TOP_APLL1_D4>, <&topckgen CLK_TOP_AUD_ENGEN2_SEL>,
+ <&topckgen CLK_TOP_APLL2_D4>, <&topckgen CLK_TOP_APLL_I2S0_M_SEL>,
+ <&topckgen CLK_TOP_APLL_I2S1_M_SEL>, <&topckgen CLK_TOP_APLL_I2S2_M_SEL>,
+ <&topckgen CLK_TOP_APLL_I2S3_M_SEL>, <&topckgen CLK_TOP_APLL_I2S4_M_SEL>,
+ <&topckgen CLK_TOP_APLL_I2S5_M_SEL>, <&topckgen CLK_TOP_APLL_I2S6_M_SEL>,
+ <&topckgen CLK_TOP_APLL_I2S7_M_SEL>, <&topckgen CLK_TOP_APLL_I2S8_M_SEL>,
+ <&topckgen CLK_TOP_APLL_I2S9_M_SEL>, <&topckgen CLK_TOP_APLL12_DIV0>,
+ <&topckgen CLK_TOP_APLL12_DIV1>, <&topckgen CLK_TOP_APLL12_DIV2>,
+ <&topckgen CLK_TOP_APLL12_DIV3>, <&topckgen CLK_TOP_APLL12_DIV4>,
+ <&topckgen CLK_TOP_APLL12_DIVB>, <&topckgen CLK_TOP_APLL12_DIV5>,
+ <&topckgen CLK_TOP_APLL12_DIV6>, <&topckgen CLK_TOP_APLL12_DIV7>,
+ <&topckgen CLK_TOP_APLL12_DIV8>, <&topckgen CLK_TOP_APLL12_DIV9>,
+ <&topckgen CLK_TOP_AUDIO_H_SEL>, <&clk26m>;
+ clock-names = "aud_afe_clk", "aud_dac_clk",
+ "aud_dac_predis_clk", "aud_adc_clk",
+ "aud_adda6_adc_clk", "aud_apll22m_clk",
+ "aud_apll24m_clk", "aud_apll1_tuner_clk",
+ "aud_apll2_tuner_clk", "aud_tdm_clk",
+ "aud_tml_clk", "aud_nle",
+ "aud_dac_hires_clk", "aud_adc_hires_clk",
+ "aud_adc_hires_tml", "aud_adda6_adc_hires_clk",
+ "aud_3rd_dac_clk", "aud_3rd_dac_predis_clk",
+ "aud_3rd_dac_tml", "aud_3rd_dac_hires_clk",
+ "aud_infra_clk", "aud_infra_26m_clk",
+ "top_mux_audio", "top_mux_audio_int",
+ "top_mainpll_d4_d4", "top_mux_aud_1",
+ "top_apll1_ck", "top_mux_aud_2",
+ "top_apll2_ck", "top_mux_aud_eng1",
+ "top_apll1_d4", "top_mux_aud_eng2",
+ "top_apll2_d4", "top_i2s0_m_sel",
+ "top_i2s1_m_sel", "top_i2s2_m_sel",
+ "top_i2s3_m_sel", "top_i2s4_m_sel",
+ "top_i2s5_m_sel", "top_i2s6_m_sel",
+ "top_i2s7_m_sel", "top_i2s8_m_sel",
+ "top_i2s9_m_sel", "top_apll12_div0",
+ "top_apll12_div1", "top_apll12_div2",
+ "top_apll12_div3", "top_apll12_div4",
+ "top_apll12_divb", "top_apll12_div5",
+ "top_apll12_div6", "top_apll12_div7",
+ "top_apll12_div8", "top_apll12_div9",
+ "top_mux_audio_h", "top_clk26m_clk";
+ memory-region = <&afe_dma_mem>;
+ power-domains = <&scpsys MT8192_POWER_DOMAIN_AUDIO>;
resets = <&watchdog MT8192_TOPRGU_AUDIO_SW_RST>;
reset-names = "audiosys";
mediatek,apmixedsys = <&apmixedsys>;
mediatek,infracfg = <&infracfg>;
mediatek,topckgen = <&topckgen>;
- power-domains = <&scpsys MT8192_POWER_DOMAIN_AUDIO>;
- clocks = <&audsys CLK_AUD_AFE>,
- <&audsys CLK_AUD_DAC>,
- <&audsys CLK_AUD_DAC_PREDIS>,
- <&infracfg CLK_INFRA_AUDIO>,
- <&infracfg CLK_INFRA_AUDIO_26M_B>;
- clock-names = "aud_afe_clk",
- "aud_dac_clk",
- "aud_dac_predis_clk",
- "aud_infra_clk",
- "aud_infra_26m_clk";
- memory-region = <&afe_dma_mem>;
};
...
diff --git a/Documentation/devicetree/bindings/sound/realtek,rt5575.yaml b/Documentation/devicetree/bindings/sound/realtek,rt5575.yaml
new file mode 100644
index 000000000000..981ebc39b195
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/realtek,rt5575.yaml
@@ -0,0 +1,61 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/realtek,rt5575.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ALC5575 audio CODEC
+
+maintainers:
+ - Oder Chiou <oder_chiou@realtek.com>
+
+description:
+ The device supports both I2C and SPI. I2C is mandatory, while SPI is
+ optional depending on the hardware configuration. SPI is used for
+ firmware loading if present.
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ const: realtek,rt5575
+
+ reg:
+ maxItems: 1
+
+ spi-parent:
+ description:
+ Optional phandle reference to the SPI controller used for firmware
+ loading. The argument specifies the chip select.
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+
+required:
+ - compatible
+ - reg
+
+unevaluatedProperties: false
+
+examples:
+ # I2C-only node
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ codec@57 {
+ compatible = "realtek,rt5575";
+ reg = <0x57>;
+ };
+ };
+
+ # I2C + optional SPI node
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ codec@57 {
+ compatible = "realtek,rt5575";
+ reg = <0x57>;
+ spi-parent = <&spi0 0>; /* chip-select 0 */
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/realtek,rt5651.yaml b/Documentation/devicetree/bindings/sound/realtek,rt5651.yaml
new file mode 100644
index 000000000000..dc4f2eef7cf9
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/realtek,rt5651.yaml
@@ -0,0 +1,100 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/realtek,rt5651.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Realtek RT5651 audio CODEC
+
+maintainers:
+ - Bard Liao <bardliao@realtek.com>
+
+description: >
+ This device supports I2C only.
+
+ Pins on the device (for linking into audio routes) for RT5651:
+
+ * DMIC L1
+ * DMIC R1
+ * IN1P
+ * IN2P
+ * IN2N
+ * IN3P
+ * HPOL
+ * HPOR
+ * LOUTL
+ * LOUTR
+ * PDML
+ * PDMR
+
+allOf:
+ - $ref: /schemas/sound/dai-common.yaml#
+
+properties:
+ compatible:
+ const: realtek,rt5651
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ clock-names:
+ const: mclk
+
+ '#sound-dai-cells':
+ const: 0
+
+ realtek,in2-differential:
+ type: boolean
+ description: Indicate MIC2 input are differential, rather than single-ended.
+
+ realtek,dmic-en:
+ type: boolean
+ description: Indicates DMIC is used.
+
+ realtek,jack-detect-source:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: Select jack-detect input pin.
+ enum: [1, 2, 3]
+
+ realtek,jack-detect-not-inverted:
+ type: boolean
+ description:
+ Normal jack-detect switches give an inverted (active-low) signal. Set this
+ bool in the rare case you've a jack-detect switch which is not inverted.
+
+ realtek,over-current-threshold-microamp:
+ description: Micbias over-current detection threshold in µA.
+ enum: [600, 1500, 2000]
+
+ realtek,over-current-scale-factor:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: >
+ Micbias over-current detection scale factor:
+
+ 0: scale current by 0.5
+ 1: scale current by 0.75
+ 2: scale current by 1.0
+ 3: scale current by 1.5
+ enum: [0, 1, 2, 3]
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ codec@1a {
+ compatible = "realtek,rt5651";
+ reg = <0x1a>;
+ realtek,dmic-en;
+ realtek,in2-differential;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/richtek,rtq9128.yaml b/Documentation/devicetree/bindings/sound/richtek,rtq9128.yaml
index d54686a19ab7..a125663988a5 100644
--- a/Documentation/devicetree/bindings/sound/richtek,rtq9128.yaml
+++ b/Documentation/devicetree/bindings/sound/richtek,rtq9128.yaml
@@ -14,13 +14,21 @@ description:
class-D audio power amplifier and delivering 4x75W into 4OHm at 10%
THD+N from a 25V supply in automotive applications.
+ The RTQ9154 is the family series of RTQ9128. The major change is to modify
+ the package size. Beside this, whole functions are almost all the same.
+
allOf:
- $ref: dai-common.yaml#
properties:
compatible:
- enum:
- - richtek,rtq9128
+ oneOf:
+ - enum:
+ - richtek,rtq9128
+ - items:
+ - enum:
+ - richtek,rtq9154
+ - const: richtek,rtq9128
reg:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/sound/rt5651.txt b/Documentation/devicetree/bindings/sound/rt5651.txt
deleted file mode 100644
index 56e736a1cba9..000000000000
--- a/Documentation/devicetree/bindings/sound/rt5651.txt
+++ /dev/null
@@ -1,63 +0,0 @@
-RT5651 audio CODEC
-
-This device supports I2C only.
-
-Required properties:
-
-- compatible : "realtek,rt5651".
-
-- reg : The I2C address of the device.
-
-Optional properties:
-
-- realtek,in2-differential
- Boolean. Indicate MIC2 input are differential, rather than single-ended.
-
-- realtek,dmic-en
- Boolean. true if dmic is used.
-
-- realtek,jack-detect-source
- u32. Valid values:
- 1: Use JD1_1 pin for jack-detect
- 2: Use JD1_2 pin for jack-detect
- 3: Use JD2 pin for jack-detect
-
-- realtek,jack-detect-not-inverted
- bool. Normal jack-detect switches give an inverted (active-low) signal,
- set this bool in the rare case you've a jack-detect switch which is not
- inverted.
-
-- realtek,over-current-threshold-microamp
- u32, micbias over-current detection threshold in µA, valid values are
- 600, 1500 and 2000µA.
-
-- realtek,over-current-scale-factor
- u32, micbias over-current detection scale-factor, valid values are:
- 0: Scale current by 0.5
- 1: Scale current by 0.75
- 2: Scale current by 1.0
- 3: Scale current by 1.5
-
-Pins on the device (for linking into audio routes) for RT5651:
-
- * DMIC L1
- * DMIC R1
- * IN1P
- * IN2P
- * IN2N
- * IN3P
- * HPOL
- * HPOR
- * LOUTL
- * LOUTR
- * PDML
- * PDMR
-
-Example:
-
-rt5651: codec@1a {
- compatible = "realtek,rt5651";
- reg = <0x1a>;
- realtek,dmic-en = "true";
- realtek,in2-diff = "false";
-};
diff --git a/Documentation/devicetree/bindings/sound/sophgo,cv1800b-codecs.yaml b/Documentation/devicetree/bindings/sound/sophgo,cv1800b-codecs.yaml
new file mode 100644
index 000000000000..7293a98e98c5
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/sophgo,cv1800b-codecs.yaml
@@ -0,0 +1,46 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/sophgo,cv1800b-codecs.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sophgo CV1800B Internal ADC/DAC Codec
+
+maintainers:
+ - Anton D. Stavinskii <stavinsky@gmail.com>
+
+description:
+ Internal ADC and DAC audio codecs integrated in the Sophgo CV1800B SoC.
+ Codecs expose a single DAI and are intended to be connected
+ to an I2S/TDM controller via an ASoC machine driver.
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ enum:
+ - sophgo,cv1800b-sound-adc
+ - sophgo,cv1800b-sound-dac
+
+ reg:
+ maxItems: 1
+
+ "#sound-dai-cells":
+ const: 0
+
+required:
+ - compatible
+ - reg
+ - "#sound-dai-cells"
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ audio-codec@300a100 {
+ compatible = "sophgo,cv1800b-sound-adc";
+ reg = <0x0300a100 0x100>;
+ #sound-dai-cells = <0>;
+ };
+...
diff --git a/Documentation/devicetree/bindings/sound/sophgo,cv1800b-i2s.yaml b/Documentation/devicetree/bindings/sound/sophgo,cv1800b-i2s.yaml
new file mode 100644
index 000000000000..f08362b0ca5e
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/sophgo,cv1800b-i2s.yaml
@@ -0,0 +1,67 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/sophgo,cv1800b-i2s.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sophgo CV1800B I2S/TDM controller
+
+maintainers:
+ - Anton D. Stavinskii <stavinsky@gmail.com>
+
+description: I2S/TDM controller found in CV1800B / Sophgo SG2002/SG2000 SoCs.
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ const: sophgo,cv1800b-i2s
+
+ reg:
+ maxItems: 1
+
+ "#sound-dai-cells":
+ const: 0
+
+ clocks:
+ maxItems: 2
+
+ clock-names:
+ items:
+ - const: i2s
+ - const: mclk
+
+ dmas:
+ minItems: 1
+ maxItems: 2
+
+ dma-names:
+ minItems: 1
+ items:
+ - enum: [rx, tx]
+ - const: tx
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - "#sound-dai-cells"
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/sophgo,cv1800.h>
+
+ i2s@4110000 {
+ compatible = "sophgo,cv1800b-i2s";
+ reg = <0x04110000 0x10000>;
+ clocks = <&clk CLK_APB_I2S1>, <&clk CLK_SDMA_AUD1>;
+ clock-names = "i2s", "mclk";
+ dmas = <&dmamux 2 1>, <&dmamux 3 1>;
+ dma-names = "rx", "tx";
+ #sound-dai-cells = <0>;
+ };
+...
diff --git a/Documentation/devicetree/bindings/sound/tas2552.txt b/Documentation/devicetree/bindings/sound/tas2552.txt
deleted file mode 100644
index a7eecad83db1..000000000000
--- a/Documentation/devicetree/bindings/sound/tas2552.txt
+++ /dev/null
@@ -1,36 +0,0 @@
-Texas Instruments - tas2552 Codec module
-
-The tas2552 serial control bus communicates through I2C protocols
-
-Required properties:
- - compatible - One of:
- "ti,tas2552" - TAS2552
- - reg - I2C slave address: it can be 0x40 if ADDR pin is 0
- or 0x41 if ADDR pin is 1.
- - supply-*: Required supply regulators are:
- "vbat" battery voltage
- "iovdd" I/O Voltage
- "avdd" Analog DAC Voltage
-
-Optional properties:
- - enable-gpio - gpio pin to enable/disable the device
-
-tas2552 can receive its reference clock via MCLK, BCLK, IVCLKIN pin or use the
-internal 1.8MHz. This CLKIN is used by the PLL. In addition to PLL, the PDM
-reference clock is also selectable: PLL, IVCLKIN, BCLK or MCLK.
-For system integration the dt-bindings/sound/tas2552.h header file provides
-defined values to select and configure the PLL and PDM reference clocks.
-
-Example:
-
-tas2552: tas2552@41 {
- compatible = "ti,tas2552";
- reg = <0x41>;
- vbat-supply = <&reg_vbat>;
- iovdd-supply = <&reg_iovdd>;
- avdd-supply = <&reg_avdd>;
- enable-gpio = <&gpio4 2 GPIO_ACTIVE_HIGH>;
-};
-
-For more product information please see the link below:
-https://www.ti.com/product/TAS2552
diff --git a/Documentation/devicetree/bindings/sound/ti,tas2552.yaml b/Documentation/devicetree/bindings/sound/ti,tas2552.yaml
new file mode 100644
index 000000000000..10369aa5f0a8
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/ti,tas2552.yaml
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/ti,tas2552.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments TAS2552 Codec
+
+maintainers:
+ - Shenghao Ding <shenghao-ding@ti.com>
+ - Kevin Lu <kevin-lu@ti.com>
+ - Baojun Xu <baojun.xu@ti.com>
+
+description: >
+ The TAS2552 can receive its reference clock via MCLK, BCLK, IVCLKIN pin or
+ use the internal 1.8MHz. This CLKIN is used by the PLL. In addition to PLL,
+ the PDM reference clock is also selectable: PLL, IVCLKIN, BCLK or MCLK.
+
+ For system integration the dt-bindings/sound/tas2552.h header file provides
+ defined values to select and configure the PLL and PDM reference clocks.
+
+properties:
+ compatible:
+ const: ti,tas2552
+
+ reg:
+ maxItems: 1
+
+ vbat-supply: true
+ iovdd-supply: true
+ avdd-supply: true
+
+ enable-gpio:
+ maxItems: 1
+ description: gpio pin to enable/disable the device
+
+required:
+ - compatible
+ - reg
+ - vbat-supply
+ - iovdd-supply
+ - avdd-supply
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ audio-codec@41 {
+ compatible = "ti,tas2552";
+ reg = <0x41>;
+ vbat-supply = <&reg_vbat>;
+ iovdd-supply = <&reg_iovdd>;
+ avdd-supply = <&reg_avdd>;
+ enable-gpio = <&gpio4 2 GPIO_ACTIVE_HIGH>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml b/Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml
index 876fa97bfbcd..a93de2debbb4 100644
--- a/Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml
+++ b/Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml
@@ -41,8 +41,11 @@ properties:
areg-supply:
description: |
- Regulator with AVDD at 3.3V. If not defined then the internal regulator
- is enabled.
+ External supply of 1.8V. If not defined then the internal regulator is
+ enabled instead.
+
+ avdd-supply: true
+ iovdd-supply: true
ti,mic-bias-source:
description: |
diff --git a/MAINTAINERS b/MAINTAINERS
index 1a5a59957bde..0a16baff1936 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1032,6 +1032,13 @@ L: dmaengine@vger.kernel.org
S: Supported
F: drivers/dma/amd/ae4dma/
+AMD ASoC DRIVERS
+M: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
+R: Venkata Prasad Potturu <venkataprasad.potturu@amd.com>
+L: linux-sound@vger.kernel.org
+S: Supported
+F: sound/soc/amd/
+
AMD AXI W1 DRIVER
M: Kris Chaplin <kris.chaplin@amd.com>
R: Thomas Delev <thomas.delev@amd.com>
@@ -25898,7 +25905,7 @@ M: Kevin Lu <kevin-lu@ti.com>
M: Baojun Xu <baojun.xu@ti.com>
L: linux-sound@vger.kernel.org
S: Maintained
-F: Documentation/devicetree/bindings/sound/tas2552.txt
+F: Documentation/devicetree/bindings/sound/ti,tas2552.yaml
F: Documentation/devicetree/bindings/sound/ti,tas2562.yaml
F: Documentation/devicetree/bindings/sound/ti,tas2770.yaml
F: Documentation/devicetree/bindings/sound/ti,tas27xx.yaml
diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c
index 73d201e7d992..9fdb50f3fec6 100644
--- a/drivers/firmware/cirrus/cs_dsp.c
+++ b/drivers/firmware/cirrus/cs_dsp.c
@@ -412,18 +412,23 @@ static ssize_t cs_dsp_debugfs_string_read(struct cs_dsp *dsp,
size_t count, loff_t *ppos,
const char **pstr)
{
- const char *str __free(kfree) = NULL;
+ const char *str;
+ ssize_t ret = 0;
scoped_guard(mutex, &dsp->pwr_lock) {
- if (!*pstr)
- return 0;
-
- str = kasprintf(GFP_KERNEL, "%s\n", *pstr);
- if (!str)
- return -ENOMEM;
-
- return simple_read_from_buffer(user_buf, count, ppos, str, strlen(str));
+ if (*pstr) {
+ str = kasprintf(GFP_KERNEL, "%s\n", *pstr);
+ if (str) {
+ ret = simple_read_from_buffer(user_buf, count,
+ ppos, str, strlen(str));
+ kfree(str);
+ } else {
+ ret = -ENOMEM;
+ }
+ }
}
+
+ return ret;
}
static ssize_t cs_dsp_debugfs_wmfw_read(struct file *file,
@@ -1483,7 +1488,7 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware,
const struct wmfw_region *region;
const struct cs_dsp_region *mem;
const char *region_name;
- u8 *buf __free(kfree) = NULL;
+ u8 *buf = NULL;
size_t buf_len = 0;
size_t region_len;
unsigned int reg;
@@ -1638,6 +1643,8 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware,
ret = 0;
out_fw:
+ kfree(buf);
+
if (ret == -EOVERFLOW)
cs_dsp_err(dsp, "%s: file content overflows file data\n", file);
@@ -2168,8 +2175,9 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
const struct cs_dsp_region *mem;
struct cs_dsp_alg_region *alg_region;
const char *region_name;
- int ret, pos, blocks, type, offset, reg, version;
- u8 *buf __free(kfree) = NULL;
+ int ret, pos, blocks, type, version;
+ unsigned int offset, reg;
+ u8 *buf = NULL;
size_t buf_len = 0;
size_t region_len;
@@ -2193,6 +2201,7 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
switch (be32_to_cpu(hdr->rev) & 0xff) {
case 1:
case 2:
+ case 3:
break;
default:
cs_dsp_err(dsp, "%s: Unsupported coefficient file format %d\n",
@@ -2201,7 +2210,8 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
goto out_fw;
}
- cs_dsp_info(dsp, "%s: v%d.%d.%d\n", file,
+ cs_dsp_info(dsp, "%s (v%d): v%d.%d.%d\n", file,
+ be32_to_cpu(hdr->rev) & 0xff,
(le32_to_cpu(hdr->ver) >> 16) & 0xff,
(le32_to_cpu(hdr->ver) >> 8) & 0xff,
le32_to_cpu(hdr->ver) & 0xff);
@@ -2232,8 +2242,9 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
(le32_to_cpu(blk->ver) >> 16) & 0xff,
(le32_to_cpu(blk->ver) >> 8) & 0xff,
le32_to_cpu(blk->ver) & 0xff);
- cs_dsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n",
- file, blocks, le32_to_cpu(blk->len), offset, type);
+ cs_dsp_dbg(dsp, "%s.%d: %d bytes off:%#x off32:%#x in %#x\n",
+ file, blocks, le32_to_cpu(blk->len), offset,
+ le32_to_cpu(blk->offset32), type);
reg = 0;
region_name = "Unknown";
@@ -2266,6 +2277,13 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
}
break;
+ case WMFW_ADSP2_XM_LONG:
+ case WMFW_ADSP2_YM_LONG:
+ case WMFW_HALO_XM_PACKED_LONG:
+ case WMFW_HALO_YM_PACKED_LONG:
+ offset = le32_to_cpu(blk->offset32);
+ type &= 0xff; /* strip extended block type flags */
+ fallthrough;
case WMFW_ADSP1_DM:
case WMFW_ADSP1_ZM:
case WMFW_ADSP2_XM:
@@ -2348,6 +2366,8 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
ret = 0;
out_fw:
+ kfree(buf);
+
if (ret == -EOVERFLOW)
cs_dsp_err(dsp, "%s: file content overflows file data\n", file);
diff --git a/drivers/firmware/cirrus/test/cs_dsp_mock_bin.c b/drivers/firmware/cirrus/test/cs_dsp_mock_bin.c
index 3f8777ee4dc0..635e917e0516 100644
--- a/drivers/firmware/cirrus/test/cs_dsp_mock_bin.c
+++ b/drivers/firmware/cirrus/test/cs_dsp_mock_bin.c
@@ -56,13 +56,14 @@ EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_get_firmware, "FW_CS_DSP_KUNIT_TEST_UTILS")
* @alg_id: Algorithm ID.
* @alg_ver: Algorithm version.
* @type: Type of the block.
- * @offset: Offset.
+ * @offset: 16-bit offset.
+ * @offset32: 32-bit offset (sample rate on V1 and V2 file formats).
* @payload_data: Pointer to buffer containing the payload data.
* @payload_len_bytes: Length of payload data in bytes.
*/
void cs_dsp_mock_bin_add_raw_block(struct cs_dsp_mock_bin_builder *builder,
unsigned int alg_id, unsigned int alg_ver,
- int type, unsigned int offset,
+ int type, u16 offset, u32 offset32,
const void *payload_data, size_t payload_len_bytes)
{
struct wmfw_coeff_item *item;
@@ -75,6 +76,7 @@ void cs_dsp_mock_bin_add_raw_block(struct cs_dsp_mock_bin_builder *builder,
item = builder->write_p;
item->offset = cpu_to_le16(offset);
+ item->offset32 = cpu_to_le32(offset32);
item->type = cpu_to_le16(type);
item->id = cpu_to_le32(alg_id);
item->ver = cpu_to_le32(alg_ver << 8);
@@ -104,7 +106,7 @@ static void cs_dsp_mock_bin_add_name_or_info(struct cs_dsp_mock_bin_builder *bui
info = tmp;
}
- cs_dsp_mock_bin_add_raw_block(builder, 0, 0, WMFW_INFO_TEXT, 0, info, info_len);
+ cs_dsp_mock_bin_add_raw_block(builder, 0, 0, WMFW_INFO_TEXT, 0, 0, info, info_len);
kunit_kfree(builder->test_priv->test, tmp);
}
@@ -156,12 +158,40 @@ void cs_dsp_mock_bin_add_patch(struct cs_dsp_mock_bin_builder *builder,
KUNIT_ASSERT_EQ(builder->test_priv->test, payload_len_bytes % 4, 0);
cs_dsp_mock_bin_add_raw_block(builder, alg_id, alg_ver,
- mem_region, reg_addr_offset,
+ mem_region, (u16)reg_addr_offset, 0,
payload_data, payload_len_bytes);
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_patch, "FW_CS_DSP_KUNIT_TEST_UTILS");
/**
+ * cs_dsp_mock_bin_add_patch_off32() - Add a patch data block with 32-bit offset.
+ *
+ * @builder: Pointer to struct cs_dsp_mock_bin_builder.
+ * @alg_id: Algorithm ID for the patch.
+ * @alg_ver: Algorithm version for the patch.
+ * @mem_region: Memory region for the patch.
+ * @reg_addr_offset: Offset to start of data in register addresses.
+ * @payload_data: Pointer to buffer containing the payload data.
+ * @payload_len_bytes: Length of payload data in bytes.
+ */
+void cs_dsp_mock_bin_add_patch_off32(struct cs_dsp_mock_bin_builder *builder,
+ unsigned int alg_id, unsigned int alg_ver,
+ int mem_region, unsigned int reg_addr_offset,
+ const void *payload_data, size_t payload_len_bytes)
+{
+ /* Payload length must be a multiple of 4 */
+ KUNIT_ASSERT_EQ(builder->test_priv->test, payload_len_bytes % 4, 0);
+
+ /* Mark the block as using the 32-bit offset */
+ mem_region |= 0xf400;
+
+ cs_dsp_mock_bin_add_raw_block(builder, alg_id, alg_ver,
+ mem_region, 0, reg_addr_offset,
+ payload_data, payload_len_bytes);
+}
+EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_patch_off32, "FW_CS_DSP_KUNIT_TEST_UTILS");
+
+/**
* cs_dsp_mock_bin_init() - Initialize a struct cs_dsp_mock_bin_builder.
*
* @priv: Pointer to struct cs_dsp_test.
diff --git a/drivers/firmware/cirrus/test/cs_dsp_mock_mem_maps.c b/drivers/firmware/cirrus/test/cs_dsp_mock_mem_maps.c
index 95946fac5563..7fb404425fd6 100644
--- a/drivers/firmware/cirrus/test/cs_dsp_mock_mem_maps.c
+++ b/drivers/firmware/cirrus/test/cs_dsp_mock_mem_maps.c
@@ -23,10 +23,10 @@ EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_halo_dsp1_regions, "FW_CS_DSP_KUNIT_TEST_UTILS"
/* List of sizes in bytes, for each entry above */
const unsigned int cs_dsp_mock_halo_dsp1_region_sizes[] = {
0x5000, /* PM_PACKED */
- 0x6000, /* XM_PACKED */
- 0x47F4, /* YM_PACKED */
- 0x8000, /* XM_UNPACKED_24 */
- 0x5FF8, /* YM_UNPACKED_24 */
+ 0x8fff4, /* XM_PACKED */
+ 0x8fff4, /* YM_PACKED */
+ 0xbfff8, /* XM_UNPACKED_24 */
+ 0xbfff8, /* YM_UNPACKED_24 */
0 /* terminator */
};
diff --git a/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c b/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c
index fb8e4a5d189a..5167305521cd 100644
--- a/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c
+++ b/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c
@@ -157,22 +157,22 @@ static const struct reg_default halo_register_defaults[] = {
};
static const struct regmap_range halo_readable_registers[] = {
- regmap_reg_range(0x2000000, 0x2005fff), /* XM_PACKED */
+ regmap_reg_range(0x2000000, 0x208fff0), /* XM_PACKED */
regmap_reg_range(0x25e0000, 0x25e004f), /* SYSINFO */
regmap_reg_range(0x25e2000, 0x25e2047), /* SYSINFO */
- regmap_reg_range(0x2800000, 0x2807fff), /* XM */
+ regmap_reg_range(0x2800000, 0x28bfff4), /* XM */
regmap_reg_range(0x2b80000, 0x2bc700b), /* CORE CTRL */
- regmap_reg_range(0x2c00000, 0x2c047f3), /* YM_PACKED */
- regmap_reg_range(0x3400000, 0x3405ff7), /* YM */
+ regmap_reg_range(0x2c00000, 0x2c8fff0), /* YM_PACKED */
+ regmap_reg_range(0x3400000, 0x34bfff4), /* YM */
regmap_reg_range(0x3800000, 0x3804fff), /* PM_PACKED */
};
static const struct regmap_range halo_writeable_registers[] = {
- regmap_reg_range(0x2000000, 0x2005fff), /* XM_PACKED */
- regmap_reg_range(0x2800000, 0x2807fff), /* XM */
+ regmap_reg_range(0x2000000, 0x208fff0), /* XM_PACKED */
+ regmap_reg_range(0x2800000, 0x28bfff4), /* XM */
regmap_reg_range(0x2b80000, 0x2bc700b), /* CORE CTRL */
- regmap_reg_range(0x2c00000, 0x2c047f3), /* YM_PACKED */
- regmap_reg_range(0x3400000, 0x3405ff7), /* YM */
+ regmap_reg_range(0x2c00000, 0x2c8fff0), /* YM_PACKED */
+ regmap_reg_range(0x3400000, 0x34bfff4), /* YM */
regmap_reg_range(0x3800000, 0x3804fff), /* PM_PACKED */
};
diff --git a/drivers/firmware/cirrus/test/cs_dsp_test_bin.c b/drivers/firmware/cirrus/test/cs_dsp_test_bin.c
index 2c6486fa9575..66140caeebb5 100644
--- a/drivers/firmware/cirrus/test/cs_dsp_test_bin.c
+++ b/drivers/firmware/cirrus/test/cs_dsp_test_bin.c
@@ -71,6 +71,10 @@ struct bin_test_param {
int mem_type;
unsigned int offset_words;
int alg_idx;
+ void (*add_patch)(struct cs_dsp_mock_bin_builder *builder,
+ unsigned int alg_id, unsigned int alg_ver,
+ int mem_region, unsigned int reg_addr_offset,
+ const void *payload_data, size_t payload_len_bytes);
};
static const struct cs_dsp_mock_alg_def bin_test_mock_algs[] = {
@@ -130,12 +134,12 @@ static void bin_patch_one_word(struct kunit *test)
bin_test_mock_algs[param->alg_idx].id,
param->mem_type);
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- param->offset_words * reg_inc_per_word,
- &payload_data, sizeof(payload_data));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ param->offset_words * reg_inc_per_word,
+ &payload_data, sizeof(payload_data));
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
KUNIT_ASSERT_EQ(test,
@@ -179,12 +183,12 @@ static void bin_patch_one_multiword(struct kunit *test)
bin_test_mock_algs[param->alg_idx].id,
param->mem_type);
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- param->offset_words * reg_inc_per_word,
- payload_data, sizeof(payload_data));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ param->offset_words * reg_inc_per_word,
+ payload_data, sizeof(payload_data));
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
KUNIT_ASSERT_EQ(test,
@@ -230,12 +234,12 @@ static void bin_patch_multi_oneword(struct kunit *test)
/* Add one payload per word */
for (i = 0; i < ARRAY_SIZE(payload_data); ++i) {
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- (param->offset_words + i) * reg_inc_per_word,
- &payload_data[i], sizeof(payload_data[i]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ (param->offset_words + i) * reg_inc_per_word,
+ &payload_data[i], sizeof(payload_data[i]));
}
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
@@ -287,13 +291,13 @@ static void bin_patch_multi_oneword_unordered(struct kunit *test)
/* Add one payload per word */
for (i = 0; i < ARRAY_SIZE(word_order); ++i) {
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- (param->offset_words + word_order[i]) *
- reg_inc_per_word,
- &payload_data[word_order[i]], sizeof(payload_data[0]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ (param->offset_words + word_order[i]) *
+ reg_inc_per_word,
+ &payload_data[word_order[i]], sizeof(payload_data[0]));
}
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
@@ -348,12 +352,12 @@ static void bin_patch_multi_oneword_sparse_unordered(struct kunit *test)
/* Add one payload per word */
for (i = 0; i < ARRAY_SIZE(word_offsets); ++i) {
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- word_offsets[i] * reg_inc_per_word,
- &payload_data[i], sizeof(payload_data[i]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ word_offsets[i] * reg_inc_per_word,
+ &payload_data[i], sizeof(payload_data[i]));
}
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
@@ -415,27 +419,27 @@ static void bin_patch_one_word_multiple_mems(struct kunit *test)
}
/* Add words to XM, YM and ZM */
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- WMFW_ADSP2_XM,
- param->offset_words * reg_inc_per_word,
- &payload_data[0], sizeof(payload_data[0]));
-
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- WMFW_ADSP2_YM,
- param->offset_words * reg_inc_per_word,
- &payload_data[1], sizeof(payload_data[1]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ WMFW_ADSP2_XM,
+ param->offset_words * reg_inc_per_word,
+ &payload_data[0], sizeof(payload_data[0]));
+
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ WMFW_ADSP2_YM,
+ param->offset_words * reg_inc_per_word,
+ &payload_data[1], sizeof(payload_data[1]));
if (cs_dsp_mock_has_zm(priv)) {
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- WMFW_ADSP2_ZM,
- param->offset_words * reg_inc_per_word,
- &payload_data[2], sizeof(payload_data[2]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ WMFW_ADSP2_ZM,
+ param->offset_words * reg_inc_per_word,
+ &payload_data[2], sizeof(payload_data[2]));
}
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
@@ -504,12 +508,12 @@ static void bin_patch_one_word_multiple_algs(struct kunit *test)
for (i = 0; i < ARRAY_SIZE(bin_test_mock_algs); ++i) {
reg_inc_per_word = cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv);
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[i].id,
- bin_test_mock_algs[i].ver,
- param->mem_type,
- param->offset_words * reg_inc_per_word,
- &payload_data[i], sizeof(payload_data[i]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[i].id,
+ bin_test_mock_algs[i].ver,
+ param->mem_type,
+ param->offset_words * reg_inc_per_word,
+ &payload_data[i], sizeof(payload_data[i]));
}
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
@@ -567,12 +571,12 @@ static void bin_patch_one_word_multiple_algs_unordered(struct kunit *test)
alg_idx = alg_order[i];
reg_inc_per_word = cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv);
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[alg_idx].id,
- bin_test_mock_algs[alg_idx].ver,
- param->mem_type,
- param->offset_words * reg_inc_per_word,
- &payload_data[i], sizeof(payload_data[i]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[alg_idx].id,
+ bin_test_mock_algs[alg_idx].ver,
+ param->mem_type,
+ param->offset_words * reg_inc_per_word,
+ &payload_data[i], sizeof(payload_data[i]));
}
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
@@ -630,12 +634,12 @@ static void bin_patch_1_packed(struct kunit *test)
patch_pos_words = round_up(alg_base_words + param->offset_words, 4);
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words);
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
- packed_payload, sizeof(packed_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
+ packed_payload, sizeof(packed_payload));
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
KUNIT_ASSERT_EQ(test,
@@ -690,20 +694,20 @@ static void bin_patch_1_packed_1_single_trailing(struct kunit *test)
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words);
/* Patch packed block */
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
- &packed_payload, sizeof(packed_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
+ &packed_payload, sizeof(packed_payload));
/* ... and the unpacked word following that */
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- unpacked_mem_type,
- ((patch_pos_words + 4) - alg_base_words) * 4,
- unpacked_payload, sizeof(unpacked_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ unpacked_mem_type,
+ ((patch_pos_words + 4) - alg_base_words) * 4,
+ unpacked_payload, sizeof(unpacked_payload));
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
KUNIT_ASSERT_EQ(test,
@@ -770,27 +774,27 @@ static void bin_patch_1_packed_2_single_trailing(struct kunit *test)
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words);
/* Patch packed block */
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
- &packed_payload, sizeof(packed_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
+ &packed_payload, sizeof(packed_payload));
/* ... and the unpacked words following that */
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- unpacked_mem_type,
- ((patch_pos_words + 4) - alg_base_words) * 4,
- &unpacked_payloads[0], sizeof(unpacked_payloads[0]));
-
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- unpacked_mem_type,
- ((patch_pos_words + 5) - alg_base_words) * 4,
- &unpacked_payloads[1], sizeof(unpacked_payloads[1]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ unpacked_mem_type,
+ ((patch_pos_words + 4) - alg_base_words) * 4,
+ &unpacked_payloads[0], sizeof(unpacked_payloads[0]));
+
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ unpacked_mem_type,
+ ((patch_pos_words + 5) - alg_base_words) * 4,
+ &unpacked_payloads[1], sizeof(unpacked_payloads[1]));
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
KUNIT_ASSERT_EQ(test,
@@ -859,34 +863,34 @@ static void bin_patch_1_packed_3_single_trailing(struct kunit *test)
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words);
/* Patch packed block */
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
- &packed_payload, sizeof(packed_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
+ &packed_payload, sizeof(packed_payload));
/* ... and the unpacked words following that */
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- unpacked_mem_type,
- ((patch_pos_words + 4) - alg_base_words) * 4,
- &unpacked_payloads[0], sizeof(unpacked_payloads[0]));
-
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- unpacked_mem_type,
- ((patch_pos_words + 5) - alg_base_words) * 4,
- &unpacked_payloads[1], sizeof(unpacked_payloads[1]));
-
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- unpacked_mem_type,
- ((patch_pos_words + 6) - alg_base_words) * 4,
- &unpacked_payloads[2], sizeof(unpacked_payloads[2]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ unpacked_mem_type,
+ ((patch_pos_words + 4) - alg_base_words) * 4,
+ &unpacked_payloads[0], sizeof(unpacked_payloads[0]));
+
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ unpacked_mem_type,
+ ((patch_pos_words + 5) - alg_base_words) * 4,
+ &unpacked_payloads[1], sizeof(unpacked_payloads[1]));
+
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ unpacked_mem_type,
+ ((patch_pos_words + 6) - alg_base_words) * 4,
+ &unpacked_payloads[2], sizeof(unpacked_payloads[2]));
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
KUNIT_ASSERT_EQ(test,
@@ -955,20 +959,20 @@ static void bin_patch_1_packed_2_trailing(struct kunit *test)
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words);
/* Patch packed block */
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
- &packed_payload, sizeof(packed_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
+ &packed_payload, sizeof(packed_payload));
/* ... and the unpacked words following that */
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- unpacked_mem_type,
- ((patch_pos_words + 4) - alg_base_words) * 4,
- unpacked_payload, sizeof(unpacked_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ unpacked_mem_type,
+ ((patch_pos_words + 4) - alg_base_words) * 4,
+ unpacked_payload, sizeof(unpacked_payload));
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
KUNIT_ASSERT_EQ(test,
@@ -1037,20 +1041,20 @@ static void bin_patch_1_packed_3_trailing(struct kunit *test)
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words);
/* Patch packed block */
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
- &packed_payload, sizeof(packed_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
+ &packed_payload, sizeof(packed_payload));
/* ... and the unpacked words following that */
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- unpacked_mem_type,
- ((patch_pos_words + 4) - alg_base_words) * 4,
- unpacked_payload, sizeof(unpacked_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ unpacked_mem_type,
+ ((patch_pos_words + 4) - alg_base_words) * 4,
+ unpacked_payload, sizeof(unpacked_payload));
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
KUNIT_ASSERT_EQ(test,
@@ -1119,20 +1123,20 @@ static void bin_patch_1_single_leading_1_packed(struct kunit *test)
packed_patch_pos_words = round_up(alg_base_words + param->offset_words, 4) + 4;
/* Patch the leading unpacked word */
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- unpacked_mem_type,
- ((packed_patch_pos_words - 1) - alg_base_words) * 4,
- unpacked_payload, sizeof(unpacked_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ unpacked_mem_type,
+ ((packed_patch_pos_words - 1) - alg_base_words) * 4,
+ unpacked_payload, sizeof(unpacked_payload));
/* ... then the packed block */
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(packed_patch_pos_words);
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
- &packed_payload, sizeof(packed_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
+ &packed_payload, sizeof(packed_payload));
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
KUNIT_ASSERT_EQ(test,
@@ -1198,26 +1202,26 @@ static void bin_patch_2_single_leading_1_packed(struct kunit *test)
packed_patch_pos_words = round_up(alg_base_words + param->offset_words, 4) + 4;
/* Patch the leading unpacked words */
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- unpacked_mem_type,
- ((packed_patch_pos_words - 2) - alg_base_words) * 4,
- &unpacked_payload[0], sizeof(unpacked_payload[0]));
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- unpacked_mem_type,
- ((packed_patch_pos_words - 1) - alg_base_words) * 4,
- &unpacked_payload[1], sizeof(unpacked_payload[1]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ unpacked_mem_type,
+ ((packed_patch_pos_words - 2) - alg_base_words) * 4,
+ &unpacked_payload[0], sizeof(unpacked_payload[0]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ unpacked_mem_type,
+ ((packed_patch_pos_words - 1) - alg_base_words) * 4,
+ &unpacked_payload[1], sizeof(unpacked_payload[1]));
/* ... then the packed block */
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(packed_patch_pos_words);
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
- &packed_payload, sizeof(packed_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
+ &packed_payload, sizeof(packed_payload));
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
KUNIT_ASSERT_EQ(test,
@@ -1285,20 +1289,20 @@ static void bin_patch_2_leading_1_packed(struct kunit *test)
packed_patch_pos_words = round_up(alg_base_words + param->offset_words, 4) + 4;
/* Patch the leading unpacked words */
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- unpacked_mem_type,
- ((packed_patch_pos_words - 2) - alg_base_words) * 4,
- unpacked_payload, sizeof(unpacked_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ unpacked_mem_type,
+ ((packed_patch_pos_words - 2) - alg_base_words) * 4,
+ unpacked_payload, sizeof(unpacked_payload));
/* ... then the packed block */
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(packed_patch_pos_words);
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
- &packed_payload, sizeof(packed_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
+ &packed_payload, sizeof(packed_payload));
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
KUNIT_ASSERT_EQ(test,
@@ -1366,32 +1370,32 @@ static void bin_patch_3_single_leading_1_packed(struct kunit *test)
packed_patch_pos_words = round_up(alg_base_words + param->offset_words, 4) + 4;
/* Patch the leading unpacked words */
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- unpacked_mem_type,
- ((packed_patch_pos_words - 3) - alg_base_words) * 4,
- &unpacked_payload[0], sizeof(unpacked_payload[0]));
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- unpacked_mem_type,
- ((packed_patch_pos_words - 2) - alg_base_words) * 4,
- &unpacked_payload[1], sizeof(unpacked_payload[1]));
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- unpacked_mem_type,
- ((packed_patch_pos_words - 1) - alg_base_words) * 4,
- &unpacked_payload[2], sizeof(unpacked_payload[2]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ unpacked_mem_type,
+ ((packed_patch_pos_words - 3) - alg_base_words) * 4,
+ &unpacked_payload[0], sizeof(unpacked_payload[0]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ unpacked_mem_type,
+ ((packed_patch_pos_words - 2) - alg_base_words) * 4,
+ &unpacked_payload[1], sizeof(unpacked_payload[1]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ unpacked_mem_type,
+ ((packed_patch_pos_words - 1) - alg_base_words) * 4,
+ &unpacked_payload[2], sizeof(unpacked_payload[2]));
/* ... then the packed block */
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(packed_patch_pos_words);
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
- &packed_payload, sizeof(packed_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
+ &packed_payload, sizeof(packed_payload));
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
KUNIT_ASSERT_EQ(test,
@@ -1459,20 +1463,20 @@ static void bin_patch_3_leading_1_packed(struct kunit *test)
packed_patch_pos_words = round_up(alg_base_words + param->offset_words, 4) + 4;
/* Patch the leading unpacked words */
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- unpacked_mem_type,
- ((packed_patch_pos_words - 3) - alg_base_words) * 4,
- unpacked_payload, sizeof(unpacked_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ unpacked_mem_type,
+ ((packed_patch_pos_words - 3) - alg_base_words) * 4,
+ unpacked_payload, sizeof(unpacked_payload));
/* ... then the packed block */
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(packed_patch_pos_words);
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
- &packed_payload, sizeof(packed_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
+ &packed_payload, sizeof(packed_payload));
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
KUNIT_ASSERT_EQ(test,
@@ -1539,12 +1543,12 @@ static void bin_patch_multi_onepacked(struct kunit *test)
for (i = 0; i < ARRAY_SIZE(packed_payloads); ++i) {
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words + (i * 4));
payload_offset = (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4;
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- payload_offset,
- &packed_payloads[i], sizeof(packed_payloads[i]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ payload_offset,
+ &packed_payloads[i], sizeof(packed_payloads[i]));
}
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
@@ -1604,13 +1608,13 @@ static void bin_patch_multi_onepacked_unordered(struct kunit *test)
patch_pos_in_packed_regs =
_num_words_to_num_packed_regs(patch_pos_words + (payload_order[i] * 4));
payload_offset = (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4;
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- payload_offset,
- &packed_payloads[payload_order[i]],
- sizeof(packed_payloads[0]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ payload_offset,
+ &packed_payloads[payload_order[i]],
+ sizeof(packed_payloads[0]));
}
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
@@ -1667,13 +1671,13 @@ static void bin_patch_multi_onepacked_sparse_unordered(struct kunit *test)
patch_pos_words = round_up(alg_base_words + word_offsets[i], 4);
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words);
payload_offset = (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4;
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- payload_offset,
- &packed_payloads[i],
- sizeof(packed_payloads[0]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ payload_offset,
+ &packed_payloads[i],
+ sizeof(packed_payloads[0]));
}
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
@@ -1739,21 +1743,21 @@ static void bin_patch_1_packed_multiple_mems(struct kunit *test)
/* Add XM and YM patches */
alg_base_in_packed_regs = _num_words_to_num_packed_regs(alg_xm_base_words);
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(xm_patch_pos_words);
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- WMFW_HALO_XM_PACKED,
- (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
- packed_xm_payload, sizeof(packed_xm_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ WMFW_HALO_XM_PACKED,
+ (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
+ packed_xm_payload, sizeof(packed_xm_payload));
alg_base_in_packed_regs = _num_words_to_num_packed_regs(alg_ym_base_words);
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(ym_patch_pos_words);
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- WMFW_HALO_YM_PACKED,
- (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
- packed_ym_payload, sizeof(packed_ym_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ WMFW_HALO_YM_PACKED,
+ (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
+ packed_ym_payload, sizeof(packed_ym_payload));
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
KUNIT_ASSERT_EQ(test,
@@ -1823,12 +1827,12 @@ static void bin_patch_1_packed_multiple_algs(struct kunit *test)
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words);
payload_offset = (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4;
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[i].id,
- bin_test_mock_algs[i].ver,
- param->mem_type,
- payload_offset,
- packed_payload[i], sizeof(packed_payload[i]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[i].id,
+ bin_test_mock_algs[i].ver,
+ param->mem_type,
+ payload_offset,
+ packed_payload[i], sizeof(packed_payload[i]));
}
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
@@ -1909,12 +1913,12 @@ static void bin_patch_1_packed_multiple_algs_unordered(struct kunit *test)
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words);
payload_offset = (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4;
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[alg_idx].id,
- bin_test_mock_algs[alg_idx].ver,
- param->mem_type,
- payload_offset,
- packed_payload[i], sizeof(packed_payload[i]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[alg_idx].id,
+ bin_test_mock_algs[alg_idx].ver,
+ param->mem_type,
+ payload_offset,
+ packed_payload[i], sizeof(packed_payload[i]));
}
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
@@ -2006,22 +2010,22 @@ static void bin_patch_mixed_packed_unpacked_random(struct kunit *test)
alg_base_in_packed_regs = _num_words_to_num_packed_regs(alg_base_words);
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words);
payload_offset = (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4;
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[0].id,
- bin_test_mock_algs[0].ver,
- param->mem_type,
- payload_offset,
- payload->packed[i],
- sizeof(payload->packed[i]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[0].id,
+ bin_test_mock_algs[0].ver,
+ param->mem_type,
+ payload_offset,
+ payload->packed[i],
+ sizeof(payload->packed[i]));
} else {
payload_offset = offset_words[i] * 4;
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[0].id,
- bin_test_mock_algs[0].ver,
- unpacked_mem_type,
- payload_offset,
- &payload->unpacked[i],
- sizeof(payload->unpacked[i]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[0].id,
+ bin_test_mock_algs[0].ver,
+ unpacked_mem_type,
+ payload_offset,
+ &payload->unpacked[i],
+ sizeof(payload->unpacked[i]));
}
}
@@ -2151,7 +2155,8 @@ static void bin_patch_name_and_info(struct kunit *test)
KUNIT_EXPECT_EQ(test, reg_val, payload_data);
}
-static int cs_dsp_bin_test_common_init(struct kunit *test, struct cs_dsp *dsp)
+static int cs_dsp_bin_test_common_init(struct kunit *test, struct cs_dsp *dsp,
+ int wmdr_ver)
{
struct cs_dsp_test *priv;
struct cs_dsp_mock_xm_header *xm_hdr;
@@ -2199,7 +2204,7 @@ static int cs_dsp_bin_test_common_init(struct kunit *test, struct cs_dsp *dsp)
KUNIT_ASSERT_EQ(test, ret, 0);
priv->local->bin_builder =
- cs_dsp_mock_bin_init(priv, 1,
+ cs_dsp_mock_bin_init(priv, wmdr_ver,
cs_dsp_mock_xm_header_get_fw_version(xm_hdr));
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->local->bin_builder);
@@ -2244,7 +2249,7 @@ static void cs_dsp_bin_test_exit(struct kunit *test)
cs_dsp_suppress_info_messages = false;
}
-static int cs_dsp_bin_test_halo_init(struct kunit *test)
+static int cs_dsp_bin_test_halo_init_common(struct kunit *test, int wmdr_ver)
{
struct cs_dsp *dsp;
@@ -2260,7 +2265,17 @@ static int cs_dsp_bin_test_halo_init(struct kunit *test)
dsp->base = cs_dsp_mock_halo_core_base;
dsp->base_sysinfo = cs_dsp_mock_halo_sysinfo_base;
- return cs_dsp_bin_test_common_init(test, dsp);
+ return cs_dsp_bin_test_common_init(test, dsp, wmdr_ver);
+}
+
+static int cs_dsp_bin_test_halo_init(struct kunit *test)
+{
+ return cs_dsp_bin_test_halo_init_common(test, 1);
+}
+
+static int cs_dsp_bin_test_halo_wmdr3_init(struct kunit *test)
+{
+ return cs_dsp_bin_test_halo_init_common(test, 3);
}
static int cs_dsp_bin_test_adsp2_32bit_init(struct kunit *test)
@@ -2279,7 +2294,7 @@ static int cs_dsp_bin_test_adsp2_32bit_init(struct kunit *test)
dsp->num_mems = cs_dsp_mock_count_regions(cs_dsp_mock_adsp2_32bit_dsp1_region_sizes);
dsp->base = cs_dsp_mock_adsp2_32bit_sysbase;
- return cs_dsp_bin_test_common_init(test, dsp);
+ return cs_dsp_bin_test_common_init(test, dsp, 1);
}
static int cs_dsp_bin_test_adsp2_16bit_init(struct kunit *test)
@@ -2298,70 +2313,152 @@ static int cs_dsp_bin_test_adsp2_16bit_init(struct kunit *test)
dsp->num_mems = cs_dsp_mock_count_regions(cs_dsp_mock_adsp2_16bit_dsp1_region_sizes);
dsp->base = cs_dsp_mock_adsp2_16bit_sysbase;
- return cs_dsp_bin_test_common_init(test, dsp);
+ return cs_dsp_bin_test_common_init(test, dsp, 1);
}
+#define WMDR_PATCH_SHORT .add_patch = cs_dsp_mock_bin_add_patch
+#define WMDR_PATCH_LONG .add_patch = cs_dsp_mock_bin_add_patch_off32
+
/* Parameterize on choice of XM or YM with a range of word offsets */
static const struct bin_test_param x_or_y_and_offset_param_cases[] = {
- { .mem_type = WMFW_ADSP2_XM, .offset_words = 0 },
- { .mem_type = WMFW_ADSP2_XM, .offset_words = 1 },
- { .mem_type = WMFW_ADSP2_XM, .offset_words = 2 },
- { .mem_type = WMFW_ADSP2_XM, .offset_words = 3 },
- { .mem_type = WMFW_ADSP2_XM, .offset_words = 4 },
- { .mem_type = WMFW_ADSP2_XM, .offset_words = 23 },
- { .mem_type = WMFW_ADSP2_XM, .offset_words = 22 },
- { .mem_type = WMFW_ADSP2_XM, .offset_words = 21 },
- { .mem_type = WMFW_ADSP2_XM, .offset_words = 20 },
-
- { .mem_type = WMFW_ADSP2_YM, .offset_words = 0 },
- { .mem_type = WMFW_ADSP2_YM, .offset_words = 1 },
- { .mem_type = WMFW_ADSP2_YM, .offset_words = 2 },
- { .mem_type = WMFW_ADSP2_YM, .offset_words = 3 },
- { .mem_type = WMFW_ADSP2_YM, .offset_words = 4 },
- { .mem_type = WMFW_ADSP2_YM, .offset_words = 23 },
- { .mem_type = WMFW_ADSP2_YM, .offset_words = 22 },
- { .mem_type = WMFW_ADSP2_YM, .offset_words = 21 },
- { .mem_type = WMFW_ADSP2_YM, .offset_words = 20 },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 0, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 1, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 2, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 3, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 4, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 23, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 22, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 21, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 20, WMDR_PATCH_SHORT },
+
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 0, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 1, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 2, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 3, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 4, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 23, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 22, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 21, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 20, WMDR_PATCH_SHORT },
+};
+
+static const struct bin_test_param x_or_y_and_long_offset_param_cases[] = {
+ /* Offset < 0xffff in long-offset block type */
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 0, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 1, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 2, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 3, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 4, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 23, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 22, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 21, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 20, WMDR_PATCH_LONG },
+
+ /* Offset < 0xffff in long-offset block type */
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 0, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 1, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 2, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 3, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 4, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 23, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 22, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 21, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 20, WMDR_PATCH_LONG },
+
+ /* Offset > 0xffff in long-offset block type */
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 0x10000, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 0x10001, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 0x10002, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 0x10003, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 0x10004, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 0x2f003, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 0x2f002, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 0x2f001, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 0x2f000, WMDR_PATCH_LONG },
+
+ /* Offset > 0xffff in long-offset block type */
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 0x10000, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 0x10001, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 0x10002, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 0x10003, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 0x10004, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 0x2f003, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 0x2f002, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 0x2f001, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 0x2f000, WMDR_PATCH_LONG },
};
/* Parameterize on ZM with a range of word offsets */
static const struct bin_test_param z_and_offset_param_cases[] = {
- { .mem_type = WMFW_ADSP2_ZM, .offset_words = 0 },
- { .mem_type = WMFW_ADSP2_ZM, .offset_words = 1 },
- { .mem_type = WMFW_ADSP2_ZM, .offset_words = 2 },
- { .mem_type = WMFW_ADSP2_ZM, .offset_words = 3 },
- { .mem_type = WMFW_ADSP2_ZM, .offset_words = 4 },
- { .mem_type = WMFW_ADSP2_ZM, .offset_words = 23 },
- { .mem_type = WMFW_ADSP2_ZM, .offset_words = 22 },
- { .mem_type = WMFW_ADSP2_ZM, .offset_words = 21 },
- { .mem_type = WMFW_ADSP2_ZM, .offset_words = 20 },
+ { .mem_type = WMFW_ADSP2_ZM, .offset_words = 0, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_ZM, .offset_words = 1, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_ZM, .offset_words = 2, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_ZM, .offset_words = 3, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_ZM, .offset_words = 4, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_ZM, .offset_words = 23, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_ZM, .offset_words = 22, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_ZM, .offset_words = 21, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_ZM, .offset_words = 20, WMDR_PATCH_SHORT },
};
/* Parameterize on choice of packed XM or YM with a range of word offsets */
static const struct bin_test_param packed_x_or_y_and_offset_param_cases[] = {
- { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0 },
- { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 4 },
- { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 8 },
- { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 12 },
-
- { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0 },
- { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 4 },
- { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 8 },
- { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 12 },
+ { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 4, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 8, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 12, WMDR_PATCH_SHORT },
+
+ { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 4, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 8, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 12, WMDR_PATCH_SHORT },
+};
+
+static const struct bin_test_param packed_x_or_y_and_long_offset_param_cases[] = {
+ /* Offset < 0xffff in long-offset block type */
+ { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 4, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 8, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 12, WMDR_PATCH_LONG },
+
+ /* Offset < 0xffff in long-offset block type */
+ { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 4, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 8, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 12, WMDR_PATCH_LONG },
+
+ /* Offset > 0xffff in long-offset block type */
+ { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0x10000, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0x10004, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0x10008, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0x2f000, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0x2f004, WMDR_PATCH_LONG },
+
+ /* Offset > 0xffff in long-offset block type */
+ { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0x10000, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0x10004, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0x10008, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0x2f000, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0x2f004, WMDR_PATCH_LONG },
};
static void x_or_y_or_z_and_offset_param_desc(const struct bin_test_param *param,
char *desc)
{
- snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s@%u",
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s@%u %s",
cs_dsp_mem_region_name(param->mem_type),
- param->offset_words);
+ param->offset_words,
+ (param->add_patch == cs_dsp_mock_bin_add_patch_off32) ? "offs32" : "");
}
KUNIT_ARRAY_PARAM(x_or_y_and_offset,
x_or_y_and_offset_param_cases,
x_or_y_or_z_and_offset_param_desc);
+KUNIT_ARRAY_PARAM(x_or_y_and_long_offset,
+ x_or_y_and_long_offset_param_cases,
+ x_or_y_or_z_and_offset_param_desc);
+
KUNIT_ARRAY_PARAM(z_and_offset,
z_and_offset_param_cases,
x_or_y_or_z_and_offset_param_desc);
@@ -2370,103 +2467,181 @@ KUNIT_ARRAY_PARAM(packed_x_or_y_and_offset,
packed_x_or_y_and_offset_param_cases,
x_or_y_or_z_and_offset_param_desc);
+KUNIT_ARRAY_PARAM(packed_x_or_y_and_long_offset,
+ packed_x_or_y_and_long_offset_param_cases,
+ x_or_y_or_z_and_offset_param_desc);
+
/* Parameterize on choice of packed XM or YM */
static const struct bin_test_param packed_x_or_y_param_cases[] = {
- { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0 },
- { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0 },
+ { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0, WMDR_PATCH_SHORT },
+};
+
+static const struct bin_test_param packed_x_or_y_long_param_cases[] = {
+ { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0, WMDR_PATCH_LONG },
};
static void x_or_y_or_z_param_desc(const struct bin_test_param *param,
char *desc)
{
- snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s", cs_dsp_mem_region_name(param->mem_type));
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s %s",
+ cs_dsp_mem_region_name(param->mem_type),
+ (param->add_patch == cs_dsp_mock_bin_add_patch_off32) ? "offs32" : "");
}
KUNIT_ARRAY_PARAM(packed_x_or_y, packed_x_or_y_param_cases, x_or_y_or_z_param_desc);
+KUNIT_ARRAY_PARAM(packed_x_or_y_long, packed_x_or_y_long_param_cases, x_or_y_or_z_param_desc);
static const struct bin_test_param offset_param_cases[] = {
- { .offset_words = 0 },
- { .offset_words = 1 },
- { .offset_words = 2 },
- { .offset_words = 3 },
- { .offset_words = 4 },
- { .offset_words = 23 },
- { .offset_words = 22 },
- { .offset_words = 21 },
- { .offset_words = 20 },
+ { .offset_words = 0, WMDR_PATCH_SHORT },
+ { .offset_words = 1, WMDR_PATCH_SHORT },
+ { .offset_words = 2, WMDR_PATCH_SHORT },
+ { .offset_words = 3, WMDR_PATCH_SHORT },
+ { .offset_words = 4, WMDR_PATCH_SHORT },
+ { .offset_words = 23, WMDR_PATCH_SHORT },
+ { .offset_words = 22, WMDR_PATCH_SHORT },
+ { .offset_words = 21, WMDR_PATCH_SHORT },
+ { .offset_words = 20, WMDR_PATCH_SHORT },
+};
+
+static const struct bin_test_param long_offset_param_cases[] = {
+ /* Offset < 0xffff in long-offset block type */
+ { .offset_words = 0, WMDR_PATCH_LONG },
+ { .offset_words = 1, WMDR_PATCH_LONG },
+ { .offset_words = 2, WMDR_PATCH_LONG },
+ { .offset_words = 3, WMDR_PATCH_LONG },
+ { .offset_words = 4, WMDR_PATCH_LONG },
+ { .offset_words = 23, WMDR_PATCH_LONG },
+ { .offset_words = 22, WMDR_PATCH_LONG },
+ { .offset_words = 21, WMDR_PATCH_LONG },
+ { .offset_words = 20, WMDR_PATCH_LONG },
+
+ /* Offset > 0xffff in long-offset block type */
+ { .offset_words = 0x10000, WMDR_PATCH_LONG },
+ { .offset_words = 0x10001, WMDR_PATCH_LONG },
+ { .offset_words = 0x10002, WMDR_PATCH_LONG },
+ { .offset_words = 0x10003, WMDR_PATCH_LONG },
+ { .offset_words = 0x10004, WMDR_PATCH_LONG },
+ { .offset_words = 0x2f000, WMDR_PATCH_LONG },
+ { .offset_words = 0x2f001, WMDR_PATCH_LONG },
+ { .offset_words = 0x2f002, WMDR_PATCH_LONG },
+ { .offset_words = 0x2f003, WMDR_PATCH_LONG },
};
static void offset_param_desc(const struct bin_test_param *param, char *desc)
{
- snprintf(desc, KUNIT_PARAM_DESC_SIZE, "@%u", param->offset_words);
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "@%u %s",
+ param->offset_words,
+ (param->add_patch == cs_dsp_mock_bin_add_patch_off32) ? "offs32" : "");
}
KUNIT_ARRAY_PARAM(offset, offset_param_cases, offset_param_desc);
+KUNIT_ARRAY_PARAM(long_offset, long_offset_param_cases, offset_param_desc);
static const struct bin_test_param alg_param_cases[] = {
- { .alg_idx = 0 },
- { .alg_idx = 1 },
- { .alg_idx = 2 },
- { .alg_idx = 3 },
+ { .alg_idx = 0, WMDR_PATCH_SHORT },
+ { .alg_idx = 1, WMDR_PATCH_SHORT },
+ { .alg_idx = 2, WMDR_PATCH_SHORT },
+ { .alg_idx = 3, WMDR_PATCH_SHORT },
+};
+
+static const struct bin_test_param alg_long_param_cases[] = {
+ { .alg_idx = 0, WMDR_PATCH_LONG },
+ { .alg_idx = 1, WMDR_PATCH_LONG },
+ { .alg_idx = 2, WMDR_PATCH_LONG },
+ { .alg_idx = 3, WMDR_PATCH_LONG },
};
static void alg_param_desc(const struct bin_test_param *param, char *desc)
{
WARN_ON(param->alg_idx >= ARRAY_SIZE(bin_test_mock_algs));
- snprintf(desc, KUNIT_PARAM_DESC_SIZE, "alg[%u] (%#x)",
- param->alg_idx, bin_test_mock_algs[param->alg_idx].id);
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "alg[%u] (%#x) %s",
+ param->alg_idx, bin_test_mock_algs[param->alg_idx].id,
+ (param->add_patch == cs_dsp_mock_bin_add_patch_off32) ? "offs32" : "");
}
KUNIT_ARRAY_PARAM(alg, alg_param_cases, alg_param_desc);
+KUNIT_ARRAY_PARAM(alg_long, alg_long_param_cases, alg_param_desc);
static const struct bin_test_param x_or_y_and_alg_param_cases[] = {
- { .mem_type = WMFW_ADSP2_XM, .alg_idx = 0 },
- { .mem_type = WMFW_ADSP2_XM, .alg_idx = 1 },
- { .mem_type = WMFW_ADSP2_XM, .alg_idx = 2 },
- { .mem_type = WMFW_ADSP2_XM, .alg_idx = 3 },
-
- { .mem_type = WMFW_ADSP2_YM, .alg_idx = 0 },
- { .mem_type = WMFW_ADSP2_YM, .alg_idx = 1 },
- { .mem_type = WMFW_ADSP2_YM, .alg_idx = 2 },
- { .mem_type = WMFW_ADSP2_YM, .alg_idx = 3 },
+ { .mem_type = WMFW_ADSP2_XM, .alg_idx = 0, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_XM, .alg_idx = 1, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_XM, .alg_idx = 2, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_XM, .alg_idx = 3, WMDR_PATCH_SHORT },
+
+ { .mem_type = WMFW_ADSP2_YM, .alg_idx = 0, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_YM, .alg_idx = 1, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_YM, .alg_idx = 2, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_YM, .alg_idx = 3, WMDR_PATCH_SHORT },
+};
+
+static const struct bin_test_param x_or_y_and_alg_long_param_cases[] = {
+ { .mem_type = WMFW_ADSP2_XM, .alg_idx = 0, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .alg_idx = 1, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .alg_idx = 2, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .alg_idx = 3, WMDR_PATCH_LONG },
+
+ { .mem_type = WMFW_ADSP2_YM, .alg_idx = 0, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .alg_idx = 1, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .alg_idx = 2, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .alg_idx = 3, WMDR_PATCH_LONG },
};
static void x_or_y_or_z_and_alg_param_desc(const struct bin_test_param *param, char *desc)
{
WARN_ON(param->alg_idx >= ARRAY_SIZE(bin_test_mock_algs));
- snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s alg[%u] (%#x)",
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s alg[%u] (%#x) %s",
cs_dsp_mem_region_name(param->mem_type),
- param->alg_idx, bin_test_mock_algs[param->alg_idx].id);
+ param->alg_idx, bin_test_mock_algs[param->alg_idx].id,
+ (param->add_patch == cs_dsp_mock_bin_add_patch_off32) ? "offs32" : "");
}
KUNIT_ARRAY_PARAM(x_or_y_and_alg, x_or_y_and_alg_param_cases, x_or_y_or_z_and_alg_param_desc);
+KUNIT_ARRAY_PARAM(x_or_y_and_alg_long, x_or_y_and_alg_long_param_cases,
+ x_or_y_or_z_and_alg_param_desc);
static const struct bin_test_param z_and_alg_param_cases[] = {
- { .mem_type = WMFW_ADSP2_ZM, .alg_idx = 0 },
- { .mem_type = WMFW_ADSP2_ZM, .alg_idx = 1 },
- { .mem_type = WMFW_ADSP2_ZM, .alg_idx = 2 },
- { .mem_type = WMFW_ADSP2_ZM, .alg_idx = 3 },
+ { .mem_type = WMFW_ADSP2_ZM, .alg_idx = 0, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_ZM, .alg_idx = 1, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_ZM, .alg_idx = 2, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_ZM, .alg_idx = 3, WMDR_PATCH_SHORT },
};
KUNIT_ARRAY_PARAM(z_and_alg, z_and_alg_param_cases, x_or_y_or_z_and_alg_param_desc);
static const struct bin_test_param packed_x_or_y_and_alg_param_cases[] = {
- { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 0 },
- { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 1 },
- { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 2 },
- { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 3 },
-
- { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 0 },
- { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 1 },
- { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 2 },
- { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 3 },
+ { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 0, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 1, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 2, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 3, WMDR_PATCH_SHORT },
+
+ { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 0, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 1, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 2, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 3, WMDR_PATCH_SHORT },
+};
+
+static const struct bin_test_param packed_x_or_y_and_alg_long_param_cases[] = {
+ { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 0, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 1, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 2, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 3, WMDR_PATCH_LONG },
+
+ { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 0, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 1, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 2, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 3, WMDR_PATCH_LONG },
};
KUNIT_ARRAY_PARAM(packed_x_or_y_and_alg, packed_x_or_y_and_alg_param_cases,
x_or_y_or_z_and_alg_param_desc);
+KUNIT_ARRAY_PARAM(packed_x_or_y_and_alg_long, packed_x_or_y_and_alg_long_param_cases,
+ x_or_y_or_z_and_alg_param_desc);
+
static struct kunit_case cs_dsp_bin_test_cases_halo[] = {
/* Unpacked memory */
KUNIT_CASE_PARAM(bin_patch_one_word, x_or_y_and_offset_gen_params),
@@ -2522,6 +2697,111 @@ static struct kunit_case cs_dsp_bin_test_cases_halo[] = {
{ } /* terminator */
};
+static struct kunit_case cs_dsp_bin_test_cases_halo_wmdr3[] = {
+ /* Unpacked memory */
+ KUNIT_CASE_PARAM(bin_patch_one_word, x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_one_multiword, x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_multi_oneword, x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_multi_oneword_unordered, x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_one_word_multiple_mems, offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_one_word_multiple_mems, alg_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_multi_oneword_sparse_unordered, x_or_y_and_alg_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_one_word_multiple_algs, x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_one_word_multiple_algs_unordered, x_or_y_and_offset_gen_params),
+
+ /* Packed memory tests */
+ KUNIT_CASE_PARAM(bin_patch_1_packed,
+ packed_x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_1_single_trailing,
+ packed_x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_2_single_trailing,
+ packed_x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_3_single_trailing,
+ packed_x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_2_trailing,
+ packed_x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_3_trailing,
+ packed_x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_single_leading_1_packed,
+ packed_x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_2_single_leading_1_packed,
+ packed_x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_2_leading_1_packed,
+ packed_x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_3_single_leading_1_packed,
+ packed_x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_3_leading_1_packed,
+ packed_x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_multi_onepacked,
+ packed_x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_multi_onepacked_unordered,
+ packed_x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_multiple_mems, offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_multiple_mems, alg_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_multi_onepacked_sparse_unordered,
+ packed_x_or_y_and_alg_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_multiple_algs,
+ packed_x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_multiple_algs_unordered,
+ packed_x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_mixed_packed_unpacked_random,
+ packed_x_or_y_gen_params),
+
+ /* Unpacked memory with long-offset blocks */
+ KUNIT_CASE_PARAM(bin_patch_one_word, x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_one_multiword, x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_multi_oneword, x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_multi_oneword_unordered, x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_one_word_multiple_mems, long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_one_word_multiple_mems, alg_long_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_multi_oneword_sparse_unordered, x_or_y_and_alg_long_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_one_word_multiple_algs, x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_one_word_multiple_algs_unordered,
+ x_or_y_and_long_offset_gen_params),
+
+ /* Packed memory tests with long offset blocks */
+ KUNIT_CASE_PARAM(bin_patch_1_packed,
+ packed_x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_1_single_trailing,
+ packed_x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_2_single_trailing,
+ packed_x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_3_single_trailing,
+ packed_x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_2_trailing,
+ packed_x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_3_trailing,
+ packed_x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_single_leading_1_packed,
+ packed_x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_2_single_leading_1_packed,
+ packed_x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_2_leading_1_packed,
+ packed_x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_3_single_leading_1_packed,
+ packed_x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_3_leading_1_packed,
+ packed_x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_multi_onepacked,
+ packed_x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_multi_onepacked_unordered,
+ packed_x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_multiple_mems, long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_multiple_mems, alg_long_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_multi_onepacked_sparse_unordered,
+ packed_x_or_y_and_alg_long_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_multiple_algs,
+ packed_x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_multiple_algs_unordered,
+ packed_x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_mixed_packed_unpacked_random,
+ packed_x_or_y_long_gen_params),
+
+ KUNIT_CASE(bin_patch_name_and_info),
+
+ { } /* terminator */
+};
+
static struct kunit_case cs_dsp_bin_test_cases_adsp2[] = {
/* XM and YM */
KUNIT_CASE_PARAM(bin_patch_one_word, x_or_y_and_offset_gen_params),
@@ -2557,6 +2837,12 @@ static struct kunit_suite cs_dsp_bin_test_halo = {
.test_cases = cs_dsp_bin_test_cases_halo,
};
+static struct kunit_suite cs_dsp_bin_test_halo_wmdr3 = {
+ .name = "cs_dsp_bin_halo_wmdr_v3",
+ .init = cs_dsp_bin_test_halo_wmdr3_init,
+ .test_cases = cs_dsp_bin_test_cases_halo_wmdr3,
+};
+
static struct kunit_suite cs_dsp_bin_test_adsp2_32bit = {
.name = "cs_dsp_bin_adsp2_32bit",
.init = cs_dsp_bin_test_adsp2_32bit_init,
@@ -2572,5 +2858,6 @@ static struct kunit_suite cs_dsp_bin_test_adsp2_16bit = {
};
kunit_test_suites(&cs_dsp_bin_test_halo,
+ &cs_dsp_bin_test_halo_wmdr3,
&cs_dsp_bin_test_adsp2_32bit,
&cs_dsp_bin_test_adsp2_16bit);
diff --git a/drivers/firmware/cirrus/test/cs_dsp_test_bin_error.c b/drivers/firmware/cirrus/test/cs_dsp_test_bin_error.c
index 631b9cb9eb25..9b2763b36970 100644
--- a/drivers/firmware/cirrus/test/cs_dsp_test_bin_error.c
+++ b/drivers/firmware/cirrus/test/cs_dsp_test_bin_error.c
@@ -68,24 +68,24 @@ static void bin_load_with_unknown_blocks(struct kunit *test)
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
cs_dsp_bin_err_test_mock_algs[0].id,
cs_dsp_bin_err_test_mock_algs[0].ver,
- 0xf5, 0,
+ 0xf5, 0, 0,
random_data, sizeof(random_data));
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
cs_dsp_bin_err_test_mock_algs[0].id,
cs_dsp_bin_err_test_mock_algs[0].ver,
- 0xf500, 0,
+ 0xf500, 0, 0,
random_data, sizeof(random_data));
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
cs_dsp_bin_err_test_mock_algs[0].id,
cs_dsp_bin_err_test_mock_algs[0].ver,
- 0xc300, 0,
+ 0xc300, 0, 0,
random_data, sizeof(random_data));
/* Add a single payload to be written to DSP memory */
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
cs_dsp_bin_err_test_mock_algs[0].id,
cs_dsp_bin_err_test_mock_algs[0].ver,
- WMFW_ADSP2_YM, 0,
+ WMFW_ADSP2_YM, 0, 0,
payload_data, payload_size_bytes);
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
@@ -279,7 +279,7 @@ static void bin_too_short_for_block_header(struct kunit *test)
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
cs_dsp_bin_err_test_mock_algs[0].id,
cs_dsp_bin_err_test_mock_algs[0].ver,
- param->block_type, 0,
+ param->block_type, 0, 0,
NULL, 0);
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
@@ -311,7 +311,7 @@ static void bin_too_short_for_block_payload(struct kunit *test)
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
cs_dsp_bin_err_test_mock_algs[0].id,
cs_dsp_bin_err_test_mock_algs[0].ver,
- param->block_type, 0,
+ param->block_type, 0, 0,
payload, sizeof(payload));
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
@@ -343,7 +343,7 @@ static void bin_block_payload_len_garbage(struct kunit *test)
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
cs_dsp_bin_err_test_mock_algs[0].id,
cs_dsp_bin_err_test_mock_algs[0].ver,
- param->block_type, 0,
+ param->block_type, 0, 0,
&payload, sizeof(payload));
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index a24de5c98399..3887fcf8ec86 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -4889,17 +4889,9 @@ EXPORT_SYMBOL_GPL(spi_write_then_read);
/*-------------------------------------------------------------------------*/
-#if IS_ENABLED(CONFIG_OF_DYNAMIC)
-/* Must call put_device() when done with returned spi_device device */
-static struct spi_device *of_find_spi_device_by_node(struct device_node *node)
-{
- struct device *dev = bus_find_device_by_of_node(&spi_bus_type, node);
-
- return dev ? to_spi_device(dev) : NULL;
-}
-
+#if IS_ENABLED(CONFIG_OF)
/* The spi controllers are not using spi_bus, so we find it with another way */
-static struct spi_controller *of_find_spi_controller_by_node(struct device_node *node)
+struct spi_controller *of_find_spi_controller_by_node(struct device_node *node)
{
struct device *dev;
@@ -4912,6 +4904,17 @@ static struct spi_controller *of_find_spi_controller_by_node(struct device_node
/* Reference got in class_find_device */
return container_of(dev, struct spi_controller, dev);
}
+EXPORT_SYMBOL_GPL(of_find_spi_controller_by_node);
+#endif
+
+#if IS_ENABLED(CONFIG_OF_DYNAMIC)
+/* Must call put_device() when done with returned spi_device device */
+static struct spi_device *of_find_spi_device_by_node(struct device_node *node)
+{
+ struct device *dev = bus_find_device_by_of_node(&spi_bus_type, node);
+
+ return dev ? to_spi_device(dev) : NULL;
+}
static int of_spi_notify(struct notifier_block *nb, unsigned long action,
void *arg)
diff --git a/include/linux/firmware/cirrus/cs_dsp_test_utils.h b/include/linux/firmware/cirrus/cs_dsp_test_utils.h
index 1f97764fdfd7..51e99f47e90e 100644
--- a/include/linux/firmware/cirrus/cs_dsp_test_utils.h
+++ b/include/linux/firmware/cirrus/cs_dsp_test_utils.h
@@ -126,7 +126,7 @@ struct cs_dsp_mock_bin_builder *cs_dsp_mock_bin_init(struct cs_dsp_test *priv,
unsigned int fw_version);
void cs_dsp_mock_bin_add_raw_block(struct cs_dsp_mock_bin_builder *builder,
unsigned int alg_id, unsigned int alg_ver,
- int type, unsigned int offset,
+ int type, u16 offset, u32 offset32,
const void *payload_data, size_t payload_len_bytes);
void cs_dsp_mock_bin_add_info(struct cs_dsp_mock_bin_builder *builder,
const char *info);
@@ -136,6 +136,10 @@ void cs_dsp_mock_bin_add_patch(struct cs_dsp_mock_bin_builder *builder,
unsigned int alg_id, unsigned int alg_ver,
int mem_region, unsigned int reg_addr_offset,
const void *payload_data, size_t payload_len_bytes);
+void cs_dsp_mock_bin_add_patch_off32(struct cs_dsp_mock_bin_builder *builder,
+ unsigned int alg_id, unsigned int alg_ver,
+ int mem_region, unsigned int reg_addr_offset,
+ const void *payload_data, size_t payload_len_bytes);
struct firmware *cs_dsp_mock_bin_get_firmware(struct cs_dsp_mock_bin_builder *builder);
struct cs_dsp_mock_wmfw_builder *cs_dsp_mock_wmfw_init(struct cs_dsp_test *priv,
diff --git a/include/linux/firmware/cirrus/wmfw.h b/include/linux/firmware/cirrus/wmfw.h
index 74e5a4f6c13a..eae24dde9e41 100644
--- a/include/linux/firmware/cirrus/wmfw.h
+++ b/include/linux/firmware/cirrus/wmfw.h
@@ -172,7 +172,7 @@ struct wmfw_coeff_item {
__le16 type;
__le32 id;
__le32 ver;
- __le32 sr;
+ __le32 offset32;
__le32 len;
u8 data[];
} __packed;
@@ -200,4 +200,9 @@ struct wmfw_coeff_item {
#define WMFW_HALO_XM_PACKED 0x11
#define WMFW_HALO_YM_PACKED 0x12
+#define WMFW_ADSP2_XM_LONG 0xf405
+#define WMFW_ADSP2_YM_LONG 0xf406
+#define WMFW_HALO_XM_PACKED_LONG 0xf411
+#define WMFW_HALO_YM_PACKED_LONG 0xf412
+
#endif
diff --git a/include/linux/firmware/imx/sm.h b/include/linux/firmware/imx/sm.h
index a33b45027356..ba5d93bd6158 100644
--- a/include/linux/firmware/imx/sm.h
+++ b/include/linux/firmware/imx/sm.h
@@ -26,6 +26,8 @@
#define SCMI_IMX94_CTRL_SAI3_MCLK 5U /*!< WAKE SAI3 MCLK */
#define SCMI_IMX94_CTRL_SAI4_MCLK 6U /*!< WAKE SAI4 MCLK */
+#define SCMI_IMX952_CTRL_BYPASS_AUDMIX 8U /* WAKE AUDMIX */
+
#if IS_ENABLED(CONFIG_IMX_SCMI_MISC_DRV)
int scmi_imx_misc_ctrl_get(u32 id, u32 *num, u32 *val);
int scmi_imx_misc_ctrl_set(u32 id, u32 val);
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index a9a089566b7c..5ed7846639bf 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -2950,7 +2950,8 @@
#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_ADDR_REV2 0x2db1
#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_RANK_REV2 0x2db2
#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_TC_REV2 0x2db3
-#define PCI_DEVICE_ID_INTEL_HDA_GML 0x3198
+/* In a few of the Intel documents the GML acronym is used for Gemini Lake */
+#define PCI_DEVICE_ID_INTEL_HDA_GLK 0x3198
#define PCI_DEVICE_ID_INTEL_82855PM_HB 0x3340
#define PCI_DEVICE_ID_INTEL_IOAT_TBG4 0x3429
#define PCI_DEVICE_ID_INTEL_IOAT_TBG5 0x342a
@@ -3143,6 +3144,7 @@
#define PCI_DEVICE_ID_INTEL_HDA_CML_S 0xa3f0
#define PCI_DEVICE_ID_INTEL_HDA_LNL_P 0xa828
#define PCI_DEVICE_ID_INTEL_S21152BB 0xb152
+#define PCI_DEVICE_ID_INTEL_HDA_NVL 0xd328
#define PCI_DEVICE_ID_INTEL_HDA_BMG 0xe2f7
#define PCI_DEVICE_ID_INTEL_HDA_PTL_H 0xe328
#define PCI_DEVICE_ID_INTEL_HDA_PTL 0xe428
diff --git a/include/linux/platform_data/davinci_asp.h b/include/linux/platform_data/davinci_asp.h
index b9c8520b4bd3..509c5592aab0 100644
--- a/include/linux/platform_data/davinci_asp.h
+++ b/include/linux/platform_data/davinci_asp.h
@@ -59,7 +59,8 @@ struct davinci_mcasp_pdata {
bool i2s_accurate_sck;
/* McASP specific fields */
- int tdm_slots;
+ int tdm_slots_tx;
+ int tdm_slots_rx;
u8 op_mode;
u8 dismod;
u8 num_serializer;
diff --git a/include/linux/soc/qcom/apr.h b/include/linux/soc/qcom/apr.h
index a532d1e4b1f4..6e1b1202e818 100644
--- a/include/linux/soc/qcom/apr.h
+++ b/include/linux/soc/qcom/apr.h
@@ -122,7 +122,7 @@ struct gpr_ibasic_rsp_accepted_t {
#define APR_SVC_MAJOR_VERSION(v) ((v >> 16) & 0xFF)
#define APR_SVC_MINOR_VERSION(v) (v & 0xFF)
-typedef int (*gpr_port_cb) (struct gpr_resp_pkt *d, void *priv, int op);
+typedef int (*gpr_port_cb) (const struct gpr_resp_pkt *d, void *priv, int op);
struct packet_router;
struct pkt_router_svc {
struct device *dev;
@@ -155,8 +155,8 @@ struct apr_driver {
int (*probe)(struct apr_device *sl);
void (*remove)(struct apr_device *sl);
int (*callback)(struct apr_device *a,
- struct apr_resp_pkt *d);
- int (*gpr_callback)(struct gpr_resp_pkt *d, void *data, int op);
+ const struct apr_resp_pkt *d);
+ gpr_port_cb gpr_callback;
struct device_driver driver;
const struct apr_device_id *id_table;
};
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 39681f7e063b..fd8dce4169f7 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -904,6 +904,15 @@ extern int devm_spi_register_controller(struct device *dev,
struct spi_controller *ctlr);
extern void spi_unregister_controller(struct spi_controller *ctlr);
+#if IS_ENABLED(CONFIG_OF)
+extern struct spi_controller *of_find_spi_controller_by_node(struct device_node *node);
+#else
+static inline struct spi_controller *of_find_spi_controller_by_node(struct device_node *node)
+{
+ return NULL;
+}
+#endif
+
#if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_SPI_MASTER)
extern struct spi_controller *acpi_spi_find_controller_by_adev(struct acpi_device *adev);
extern struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr,
diff --git a/include/sound/ak4641.h b/include/sound/ak4641.h
deleted file mode 100644
index 8b1941bbde52..000000000000
--- a/include/sound/ak4641.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * AK4641 ALSA SoC Codec driver
- *
- * Copyright 2009 Philipp Zabel
- */
-
-#ifndef __AK4641_H
-#define __AK4641_H
-
-/**
- * struct ak4641_platform_data - platform specific AK4641 configuration
- * @gpio_power: GPIO to control external power to AK4641
- * @gpio_npdn: GPIO connected to AK4641 nPDN pin
- *
- * Both GPIO parameters are optional.
- */
-struct ak4641_platform_data {
- int gpio_power;
- int gpio_npdn;
-};
-
-#endif /* __AK4641_H */
diff --git a/include/sound/cs-amp-lib.h b/include/sound/cs-amp-lib.h
index 61e00017c9aa..e9aa86d76049 100644
--- a/include/sound/cs-amp-lib.h
+++ b/include/sound/cs-amp-lib.h
@@ -58,6 +58,9 @@ int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_
int cs_amp_set_efi_calibration_data(struct device *dev, int amp_index, int num_amps,
const struct cirrus_amp_cal_data *in_data);
int cs_amp_get_vendor_spkid(struct device *dev);
+const char *cs_amp_devm_get_vendor_specific_variant_id(struct device *dev,
+ int ssid_vendor,
+ int ssid_device);
struct dentry *cs_amp_create_debugfs(struct device *dev);
static inline u64 cs_amp_cal_target_u64(const struct cirrus_amp_cal_data *data)
diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h
index 5928af539c46..ae1e1489b671 100644
--- a/include/sound/cs35l56.h
+++ b/include/sound/cs35l56.h
@@ -9,6 +9,7 @@
#ifndef __CS35L56_H
#define __CS35L56_H
+#include <linux/bits.h>
#include <linux/debugfs.h>
#include <linux/firmware/cirrus/cs_dsp.h>
#include <linux/regulator/consumer.h>
@@ -26,6 +27,9 @@ struct snd_ctl_elem_value;
#define CS35L56_GLOBAL_ENABLES 0x0002014
#define CS35L56_BLOCK_ENABLES 0x0002018
#define CS35L56_BLOCK_ENABLES2 0x000201C
+#define CS35L56_SYNC_GPIO1_CFG 0x0002410
+#define CS35L56_ASP2_DIO_GPIO13_CFG 0x0002440
+#define CS35L56_UPDATE_REGS 0x0002A0C
#define CS35L56_REFCLK_INPUT 0x0002C04
#define CS35L56_GLOBAL_SAMPLE_RATE 0x0002C0C
#define CS35L56_OTP_MEM_53 0x00300D4
@@ -65,6 +69,9 @@ struct snd_ctl_elem_value;
#define CS35L56_IRQ1_MASK_8 0x000E0AC
#define CS35L56_IRQ1_MASK_18 0x000E0D4
#define CS35L56_IRQ1_MASK_20 0x000E0DC
+#define CS35L56_GPIO_STATUS1 0x000F000
+#define CS35L56_GPIO1_CTRL1 0x000F008
+#define CS35L56_GPIO13_CTRL1 0x000F038
#define CS35L56_MIXER_NGATE_CH1_CFG 0x0010004
#define CS35L56_MIXER_NGATE_CH2_CFG 0x0010008
#define CS35L56_DSP_MBOX_1_RAW 0x0011000
@@ -130,6 +137,17 @@ struct snd_ctl_elem_value;
#define CS35L56_MTLREVID_MASK 0x0000000F
#define CS35L56_REVID_B0 0x000000B0
+/* PAD_INTF */
+#define CS35L56_PAD_GPIO_PULL_MASK GENMASK(3, 2)
+#define CS35L56_PAD_GPIO_IE BIT(0)
+
+#define CS35L56_PAD_PULL_NONE 0
+#define CS35L56_PAD_PULL_UP 1
+#define CS35L56_PAD_PULL_DOWN 2
+
+/* UPDATE_REGS */
+#define CS35L56_UPDT_GPIO_PRES BIT(6)
+
/* ASP_ENABLES1 */
#define CS35L56_ASP_RX2_EN_SHIFT 17
#define CS35L56_ASP_RX1_EN_SHIFT 16
@@ -185,6 +203,12 @@ struct snd_ctl_elem_value;
/* MIXER_NGATE_CHn_CFG */
#define CS35L56_AUX_NGATE_CHn_EN 0x00000001
+/* GPIOn_CTRL1 */
+#define CS35L56_GPIO_DIR_MASK BIT(31)
+#define CS35L56_GPIO_FN_MASK GENMASK(2, 0)
+
+#define CS35L56_GPIO_FN_GPIO 0x00000001
+
/* Mixer input sources */
#define CS35L56_INPUT_SRC_NONE 0x00
#define CS35L56_INPUT_SRC_ASP1RX1 0x08
@@ -279,6 +303,7 @@ struct snd_ctl_elem_value;
#define CS35L56_HALO_STATE_TIMEOUT_US 250000
#define CS35L56_RESET_PULSE_MIN_US 1100
#define CS35L56_WAKE_HOLD_TIME_US 1000
+#define CS35L56_PAD_PULL_SETTLE_US 10
#define CS35L56_CALIBRATION_POLL_US (100 * USEC_PER_MSEC)
#define CS35L56_CALIBRATION_TIMEOUT_US (5 * USEC_PER_SEC)
@@ -289,6 +314,9 @@ struct snd_ctl_elem_value;
#define CS35L56_NUM_BULK_SUPPLIES 3
#define CS35L56_NUM_DSP_REGIONS 5
+#define CS35L56_MAX_GPIO 13
+#define CS35L63_MAX_GPIO 9
+
/* Additional margin for SYSTEM_RESET to control port ready on SPI */
#define CS35L56_SPI_RESET_TO_PORT_READY_US (CS35L56_CONTROL_PORT_READY_US + 2500)
@@ -338,6 +366,10 @@ struct cs35l56_base {
const struct cirrus_amp_cal_controls *calibration_controls;
struct dentry *debugfs;
u64 silicon_uid;
+ u8 onchip_spkid_gpios[5];
+ u8 num_onchip_spkid_gpios;
+ u8 onchip_spkid_pulls[5];
+ u8 num_onchip_spkid_pulls;
};
static inline bool cs35l56_is_otp_register(unsigned int reg)
@@ -413,6 +445,11 @@ void cs35l56_warn_if_firmware_missing(struct cs35l56_base *cs35l56_base);
void cs35l56_log_tuning(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp);
int cs35l56_hw_init(struct cs35l56_base *cs35l56_base);
int cs35l56_get_speaker_id(struct cs35l56_base *cs35l56_base);
+int cs35l56_check_and_save_onchip_spkid_gpios(struct cs35l56_base *cs35l56_base,
+ const u32 *gpios, int num_gpios,
+ const u32 *pulls, int num_pulls);
+int cs35l56_configure_onchip_spkid_pads(struct cs35l56_base *cs35l56_base);
+int cs35l56_read_onchip_spkid(struct cs35l56_base *cs35l56_base);
int cs35l56_get_bclk_freq_id(unsigned int freq);
void cs35l56_fill_supply_names(struct regulator_bulk_data *data);
diff --git a/include/sound/sdca_function.h b/include/sound/sdca_function.h
index 6e9391b3816c..79bd5a7a0f88 100644
--- a/include/sound/sdca_function.h
+++ b/include/sound/sdca_function.h
@@ -798,6 +798,7 @@ struct sdca_control_range {
* @sel: Identifier used for addressing.
* @nbits: Number of bits used in the Control.
* @values: Holds the Control value for constants and defaults.
+ * @reset: Defined reset value for the Control.
* @cn_list: A bitmask showing the valid Control Numbers within this Control,
* Control Numbers typically represent channels.
* @interrupt_position: SCDA interrupt line that will alert to changes on this
@@ -808,6 +809,7 @@ struct sdca_control_range {
* @layers: Bitmask of access layers of the Control.
* @deferrable: Indicates if the access to the Control can be deferred.
* @has_default: Indicates the Control has a default value to be written.
+ * @has_reset: Indicates the Control has a defined reset value.
* @has_fixed: Indicates the Control only supports a single value.
*/
struct sdca_control {
@@ -816,6 +818,7 @@ struct sdca_control {
int nbits;
int *values;
+ int reset;
u64 cn_list;
int interrupt_position;
@@ -827,6 +830,7 @@ struct sdca_control {
bool deferrable;
bool is_volatile;
bool has_default;
+ bool has_reset;
bool has_fixed;
};
diff --git a/include/sound/sdca_interrupts.h b/include/sound/sdca_interrupts.h
index 8f13417d129a..9bcb5d8fd592 100644
--- a/include/sound/sdca_interrupts.h
+++ b/include/sound/sdca_interrupts.h
@@ -84,4 +84,11 @@ int sdca_irq_populate(struct sdca_function_data *function,
struct sdca_interrupt_info *sdca_irq_allocate(struct device *dev,
struct regmap *regmap, int irq);
+void sdca_irq_enable_early(struct sdca_function_data *function,
+ struct sdca_interrupt_info *info);
+void sdca_irq_enable(struct sdca_function_data *function,
+ struct sdca_interrupt_info *info);
+void sdca_irq_disable(struct sdca_function_data *function,
+ struct sdca_interrupt_info *info);
+
#endif
diff --git a/include/sound/sdca_jack.h b/include/sound/sdca_jack.h
new file mode 100644
index 000000000000..3ec22046d3eb
--- /dev/null
+++ b/include/sound/sdca_jack.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * The MIPI SDCA specification is available for public downloads at
+ * https://www.mipi.org/mipi-sdca-v1-0-download
+ *
+ * Copyright (C) 2025 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef __SDCA_JACK_H__
+#define __SDCA_JACK_H__
+
+struct sdca_interrupt;
+struct snd_kcontrol;
+struct snd_soc_jack;
+
+/**
+ * struct jack_state - Jack state structure to keep data between interrupts
+ * @kctl: Pointer to the ALSA control attached to this jack
+ * @jack: Pointer to the ASoC jack struct for this jack
+ */
+struct jack_state {
+ struct snd_kcontrol *kctl;
+ struct snd_soc_jack *jack;
+};
+
+int sdca_jack_alloc_state(struct sdca_interrupt *interrupt);
+int sdca_jack_process(struct sdca_interrupt *interrupt);
+int sdca_jack_set_jack(struct sdca_interrupt_info *info, struct snd_soc_jack *jack);
+int sdca_jack_report(struct sdca_interrupt *interrupt);
+
+#endif // __SDCA_JACK_H__
diff --git a/include/sound/seq_device.h b/include/sound/seq_device.h
index dead74b022f4..a72380c202e9 100644
--- a/include/sound/seq_device.h
+++ b/include/sound/seq_device.h
@@ -43,6 +43,8 @@ struct snd_seq_device {
* Typically, call snd_device_free(dev->card, dev->driver_data)
*/
struct snd_seq_driver {
+ int (*probe)(struct snd_seq_device *dev);
+ void (*remove)(struct snd_seq_device *dev);
struct device_driver driver;
char *id;
int argsize;
diff --git a/include/sound/soc-acpi-intel-ssp-common.h b/include/sound/soc-acpi-intel-ssp-common.h
index b4597c8dac78..fdb2fce42115 100644
--- a/include/sound/soc-acpi-intel-ssp-common.h
+++ b/include/sound/soc-acpi-intel-ssp-common.h
@@ -37,6 +37,9 @@
#define RT5682_ACPI_HID "10EC5682"
#define RT5682S_ACPI_HID "RTL5682"
+/* Texas Instruments */
+#define TAS2563_ACPI_HID "TXNW2563"
+
enum snd_soc_acpi_intel_codec {
CODEC_NONE,
@@ -63,6 +66,7 @@ enum snd_soc_acpi_intel_codec {
CODEC_RT1015P,
CODEC_RT1019P,
CODEC_RT1308,
+ CODEC_TAS2563,
};
enum snd_soc_acpi_intel_codec
diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h
index d78cda866888..2a2b74b24a60 100644
--- a/include/sound/soc-component.h
+++ b/include/sound/soc-component.h
@@ -237,8 +237,7 @@ struct snd_soc_component {
* the driver will be marked as BROKEN when these fields are removed.
*/
- /* Don't use these, use snd_soc_component_get_dapm() */
- struct snd_soc_dapm_context dapm;
+ struct snd_soc_dapm_context *dapm;
/* machine specific init */
int (*init)(struct snd_soc_component *component);
@@ -268,12 +267,9 @@ struct snd_soc_component {
static inline struct snd_soc_dapm_context *snd_soc_component_to_dapm(
struct snd_soc_component *component)
{
- return &component->dapm;
+ return component->dapm;
}
-// FIXME
-#define snd_soc_component_get_dapm snd_soc_component_to_dapm
-
/**
* snd_soc_component_cache_sync() - Sync the register cache with the hardware
* @component: COMPONENT to sync
@@ -368,27 +364,6 @@ snd_soc_component_active(struct snd_soc_component *component)
return component->active;
}
-/* component pin */
-int snd_soc_component_enable_pin(struct snd_soc_component *component,
- const char *pin);
-int snd_soc_component_enable_pin_unlocked(struct snd_soc_component *component,
- const char *pin);
-int snd_soc_component_disable_pin(struct snd_soc_component *component,
- const char *pin);
-int snd_soc_component_disable_pin_unlocked(struct snd_soc_component *component,
- const char *pin);
-int snd_soc_component_nc_pin(struct snd_soc_component *component,
- const char *pin);
-int snd_soc_component_nc_pin_unlocked(struct snd_soc_component *component,
- const char *pin);
-int snd_soc_component_get_pin_status(struct snd_soc_component *component,
- const char *pin);
-int snd_soc_component_force_enable_pin(struct snd_soc_component *component,
- const char *pin);
-int snd_soc_component_force_enable_pin_unlocked(
- struct snd_soc_component *component,
- const char *pin);
-
/* component controls */
struct snd_kcontrol *snd_soc_component_get_kcontrol(struct snd_soc_component *component,
const char * const ctl);
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index 75941324886b..49f0fe05db01 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -20,6 +20,7 @@ struct regulator;
struct soc_enum;
struct snd_pcm_substream;
struct snd_soc_pcm_runtime;
+struct snd_soc_dapm_context;
/* widget has no PM register bit */
#define SND_SOC_NOPM -1
@@ -579,28 +580,6 @@ struct snd_soc_dapm_update {
bool has_second_set;
};
-/* DAPM context */
-struct snd_soc_dapm_context {
- enum snd_soc_bias_level bias_level;
-
- bool idle_bias; /* Use BIAS_OFF instead of STANDBY when false */
-
- struct device *dev; /* from parent - for debug */ /* REMOVE ME */
- struct snd_soc_component *component; /* parent component */
- struct snd_soc_card *card; /* parent card */
-
- /* used during DAPM updates */
- enum snd_soc_bias_level target_bias_level;
- struct list_head list;
-
- struct snd_soc_dapm_widget *wcache_sink;
- struct snd_soc_dapm_widget *wcache_source;
-
-#ifdef CONFIG_DEBUG_FS
- struct dentry *debugfs_dapm;
-#endif
-};
-
/* A list of widgets associated with an object, typically a snd_kcontrol */
struct snd_soc_dapm_widget_list {
int num_widgets;
@@ -628,6 +607,8 @@ enum snd_soc_dapm_direction {
#define SND_SOC_DAPM_EP_SOURCE SND_SOC_DAPM_DIR_TO_EP(SND_SOC_DAPM_DIR_IN)
#define SND_SOC_DAPM_EP_SINK SND_SOC_DAPM_DIR_TO_EP(SND_SOC_DAPM_DIR_OUT)
+struct snd_soc_dapm_context *snd_soc_dapm_alloc(struct device *dev);
+
int snd_soc_dapm_regulator_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event);
int snd_soc_dapm_clock_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event);
int snd_soc_dapm_pinctrl_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event);
@@ -705,16 +686,6 @@ int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, co
int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, const char *pin);
void snd_soc_dapm_mark_endpoints_dirty(struct snd_soc_card *card);
-/*
- * Marks the specified pin as being not connected, disabling it along
- * any parent or child widgets. At present this is identical to
- * snd_soc_dapm_disable_pin[_unlocked]() but in future it will be extended to do
- * additional things such as disabling controls which only affect
- * paths through the pin.
- */
-#define snd_soc_dapm_nc_pin snd_soc_dapm_disable_pin
-#define snd_soc_dapm_nc_pin_unlocked snd_soc_dapm_disable_pin_unlocked
-
/* dapm path query */
int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
struct snd_soc_dapm_widget_list **list,
@@ -730,15 +701,6 @@ int snd_soc_dapm_force_bias_level(struct snd_soc_dapm_context *dapm, enum snd_so
enum snd_soc_bias_level snd_soc_dapm_get_bias_level(struct snd_soc_dapm_context *dapm);
void snd_soc_dapm_init_bias_level(struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level);
-// REMOVE ME !!
-#define snd_soc_component_force_bias_level(c, l) snd_soc_dapm_force_bias_level(&(c)->dapm, l)
-#define snd_soc_component_get_bias_level(c) snd_soc_dapm_get_bias_level(&(c)->dapm)
-#define snd_soc_component_init_bias_level(c, l) snd_soc_dapm_init_bias_level(&(c)->dapm, l)
-#define snd_soc_dapm_kcontrol_widget snd_soc_dapm_kcontrol_to_widget
-#define snd_soc_dapm_kcontrol_dapm snd_soc_dapm_kcontrol_to_dapm
-#define dapm_kcontrol_get_value snd_soc_dapm_kcontrol_get_value
-#define snd_soc_dapm_kcontrol_component snd_soc_dapm_kcontrol_to_component
-
#define for_each_dapm_widgets(list, i, widget) \
for ((i) = 0; \
(i) < list->num_widgets && (widget = list->widgets[i]); \
diff --git a/include/sound/soc.h b/include/sound/soc.h
index aa0fe6b80293..7d8376c8e1be 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -1076,7 +1076,7 @@ struct snd_soc_card {
struct list_head dobj_list;
/* Generic DAPM context for the card */
- struct snd_soc_dapm_context dapm;
+ struct snd_soc_dapm_context *dapm;
struct snd_soc_dapm_stats dapm_stats;
#ifdef CONFIG_DEBUG_FS
@@ -1136,7 +1136,7 @@ static inline int snd_soc_card_is_instantiated(struct snd_soc_card *card)
static inline struct snd_soc_dapm_context *snd_soc_card_to_dapm(struct snd_soc_card *card)
{
- return &card->dapm;
+ return card->dapm;
}
/* SoC machine DAI configuration, glues a codec and cpu DAI together */
diff --git a/include/sound/sof.h b/include/sound/sof.h
index eddea82c7b5a..38d6c8cb5e83 100644
--- a/include/sound/sof.h
+++ b/include/sound/sof.h
@@ -159,6 +159,9 @@ struct sof_dev_desc {
/* The platform supports DSPless mode */
bool dspless_mode_supported;
+ /* On demand DSP booting is possible on the platform */
+ bool on_demand_dsp_boot;
+
/* defaults paths for firmware, library and topology files */
const char *default_fw_path[SOF_IPC_TYPE_COUNT];
const char *default_lib_path[SOF_IPC_TYPE_COUNT];
diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h
index 15fac532688e..4554e5e8cab5 100644
--- a/include/sound/sof/ipc4/header.h
+++ b/include/sound/sof/ipc4/header.h
@@ -352,6 +352,10 @@ struct sof_ipc4_base_module_cfg {
#define SOF_IPC4_MOD_EXT_DOMAIN_MASK BIT(28)
#define SOF_IPC4_MOD_EXT_DOMAIN(x) ((x) << SOF_IPC4_MOD_EXT_DOMAIN_SHIFT)
+#define SOF_IPC4_MOD_EXT_EXTENDED_INIT_SHIFT 29
+#define SOF_IPC4_MOD_EXT_EXTENDED_INIT_MASK BIT(29)
+#define SOF_IPC4_MOD_EXT_EXTENDED_INIT(x) ((x) << SOF_IPC4_MOD_EXT_EXTENDED_SHIFT)
+
/* bind/unbind module ipc msg */
#define SOF_IPC4_MOD_EXT_DST_MOD_ID_SHIFT 0
#define SOF_IPC4_MOD_EXT_DST_MOD_ID_MASK GENMASK(15, 0)
@@ -586,6 +590,77 @@ struct sof_ipc4_notify_module_data {
#define SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_MAGIC_VAL 0xA15A0000
#define SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_PARAMID_MASK GENMASK(15, 0)
+/*
+ * Macros for creating struct sof_ipc4_module_init_ext_init payload
+ * with its associated data. ext_init payload should be the first
+ * piece of payload following SOF_IPC4_MOD_INIT_INSTANCE msg, and its
+ * existence is indicated with SOF_IPC4_MOD_EXT_EXTENDED-bit.
+ *
+ * The macros below apply to sof_ipc4_module_init_ext_init.word0
+ */
+#define SOF_IPC4_MOD_INIT_EXT_RTOS_DOMAIN_SHIFT 0
+#define SOF_IPC4_MOD_INIT_EXT_RTOS_DOMAIN_MASK BIT(0)
+#define SOF_IPC4_MOD_INIT_EXT_RTOS_DOMAIN(x) ((x) << SOF_IPC4_MOD_INIT_EXT_RTOS_DOMAIN_SHIFT)
+
+#define SOF_IPC4_MOD_INIT_EXT_GNA_USED_SHIFT 1
+#define SOF_IPC4_MOD_INIT_EXT_GNA_USED_MASK BIT(1)
+#define SOF_IPC4_MOD_INIT_EXT_GNA_USED(x) ((x) << SOF_IPC4_MOD_INIT_EXT_GNA_USED_SHIFT)
+
+#define SOF_IPC4_MOD_INIT_EXT_OBJ_ARRAY_SHIFT 2
+#define SOF_IPC4_MOD_INIT_EXT_OBJ_ARRAY_MASK BIT(2)
+#define SOF_IPC4_MOD_INIT_EXT_DATA_ARRAY(x) ((x) << SOF_IPC4_MOD_INIT_EXT_OBJ_ARRAY_SHIFT)
+
+struct sof_ipc4_module_init_ext_init {
+ u32 word0;
+ u32 rsvd1;
+ u32 rsvd2;
+} __packed __aligned(4);
+
+/*
+ * SOF_IPC4_MOD_EXT_EXTENDED payload may be followed by arbitrary
+ * number of object array objects. SOF_IPC4_MOD_INIT_EXT_DATA_ARRAY
+ * -bit indicates that an array object follows struct
+ * sof_ipc4_module_init_ext_init.
+ *
+ * The object header's SOF_IPC4_MOD_INIT_EXT_OBJ_LAST-bit in struct
+ * sof_ipc4_module_init_ext_object indicates if the array is continued
+ * with another object. The header has also fields to identify the
+ * object, SOF_IPC4_MOD_INIT_EXT_OBJ_ID, and to indicate the object's
+ * size in 32-bit words, SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS, not
+ * including the header itself.
+ *
+ * The macros below apply to sof_ipc4_module_init_ext_object.header
+ */
+#define SOF_IPC4_MOD_INIT_EXT_OBJ_LAST_SHIFT 0
+#define SOF_IPC4_MOD_INIT_EXT_OBJ_LAST_MASK BIT(0)
+#define SOF_IPC4_MOD_INIT_EXT_OBJ_LAST(x) ((x) << SOF_IPC4_MOD_INIT_EXT_OBJ_LAST_SHIFT)
+
+#define SOF_IPC4_MOD_INIT_EXT_OBJ_ID_SHIFT 1
+#define SOF_IPC4_MOD_INIT_EXT_OBJ_ID_MASK GENMASK(15, 1)
+#define SOF_IPC4_MOD_INIT_EXT_OBJ_ID(x) ((x) << SOF_IPC4_MOD_INIT_EXT_OBJ_ID_SHIFT)
+
+#define SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS_SHIFT 16
+#define SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS_MASK GENMASK(31, 16)
+#define SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS(x) ((x) << SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS_SHIFT)
+
+struct sof_ipc4_module_init_ext_object {
+ u32 header;
+ u32 data[];
+} __packed __aligned(4);
+
+enum sof_ipc4_mod_init_ext_obj_id {
+ SOF_IPC4_MOD_INIT_DATA_ID_INVALID = 0,
+ SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA,
+ SOF_IPC4_MOD_INIT_DATA_ID_MAX = SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA,
+};
+
+/* DP module memory configuration data object for ext_init object array */
+struct sof_ipc4_mod_init_ext_dp_memory_data {
+ u32 domain_id; /* userspace domain ID */
+ u32 stack_bytes; /* stack size in bytes, 0 means default size */
+ u32 heap_bytes; /* stack size in bytes, 0 means default size */
+} __packed __aligned(4);
+
/** @}*/
#endif
diff --git a/include/sound/tas2781.h b/include/sound/tas2781.h
index 9d3c54cb8223..7c03bdc951bb 100644
--- a/include/sound/tas2781.h
+++ b/include/sound/tas2781.h
@@ -2,7 +2,7 @@
//
// ALSA SoC Texas Instruments TAS2563/TAS2781 Audio Smart Amplifier
//
-// Copyright (C) 2022 - 2025 Texas Instruments Incorporated
+// Copyright (C) 2022 - 2026 Texas Instruments Incorporated
// https://www.ti.com
//
// The TAS2563/TAS2781 driver implements a flexible and configurable
@@ -233,7 +233,6 @@ struct tasdevice_priv {
bool playback_started;
bool isacpi;
bool isspi;
- bool is_user_space_calidata;
unsigned int global_addr;
int (*fw_parse_variable_header)(struct tasdevice_priv *tas_priv,
diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h
index 9ce72fbd6f11..f4a7baadb44d 100644
--- a/include/uapi/sound/sof/tokens.h
+++ b/include/uapi/sound/sof/tokens.h
@@ -56,6 +56,9 @@
#define SOF_TKN_SCHED_LP_MODE 207
#define SOF_TKN_SCHED_MEM_USAGE 208
#define SOF_TKN_SCHED_USE_CHAIN_DMA 209
+#define SOF_TKN_SCHED_KCPS 210
+#define SOF_TKN_SCHED_DIRECTION 211
+#define SOF_TKN_SCHED_DIRECTION_VALID 212
/* volume */
#define SOF_TKN_VOLUME_RAMP_STEP_TYPE 250
@@ -107,6 +110,9 @@
#define SOF_TKN_COMP_NO_WNAME_IN_KCONTROL_NAME 417
#define SOF_TKN_COMP_SCHED_DOMAIN 418
+#define SOF_TKN_COMP_DOMAIN_ID 419
+#define SOF_TKN_COMP_HEAP_BYTES_REQUIREMENT 420
+#define SOF_TKN_COMP_STACK_BYTES_REQUIREMENT 421
/* SSP */
#define SOF_TKN_INTEL_SSP_CLKS_CONTROL 500
diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig
index dea2c661b353..e4d7288d1e1e 100644
--- a/sound/arm/Kconfig
+++ b/sound/arm/Kconfig
@@ -18,16 +18,6 @@ config SND_ARMAACI
select SND_PCM
select SND_AC97_CODEC
-config SND_PXA2XX_AC97
- tristate "AC97 driver for the Intel PXA2xx chip"
- depends on ARCH_PXA
- select SND_AC97_CODEC
- select SND_PXA2XX_LIB
- select SND_PXA2XX_LIB_AC97
- help
- Say Y or M if you want to support any AC97 codec attached to
- the PXA2xx AC97 interface.
-
endif # SND_ARM
config SND_PXA2XX_LIB
diff --git a/sound/arm/Makefile b/sound/arm/Makefile
index 899edb4bb278..99325a66cf77 100644
--- a/sound/arm/Makefile
+++ b/sound/arm/Makefile
@@ -9,6 +9,3 @@ snd-aaci-y := aaci.o
obj-$(CONFIG_SND_PXA2XX_LIB) += snd-pxa2xx-lib.o
snd-pxa2xx-lib-y := pxa2xx-pcm-lib.o
snd-pxa2xx-lib-$(CONFIG_SND_PXA2XX_LIB_AC97) += pxa2xx-ac97-lib.o
-
-obj-$(CONFIG_SND_PXA2XX_AC97) += snd-pxa2xx-ac97.o
-snd-pxa2xx-ac97-y := pxa2xx-ac97.o
diff --git a/sound/arm/pxa2xx-ac97-lib.c b/sound/arm/pxa2xx-ac97-lib.c
index 64510318091f..1e114dbcf93c 100644
--- a/sound/arm/pxa2xx-ac97-lib.c
+++ b/sound/arm/pxa2xx-ac97-lib.c
@@ -13,10 +13,9 @@
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/io.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
#include <linux/soc/pxa/cpu.h>
#include <sound/pxa2xx-lib.h>
@@ -31,6 +30,7 @@ static volatile long gsr_bits;
static struct clk *ac97_clk;
static struct clk *ac97conf_clk;
static int reset_gpio;
+struct gpio_desc *rst_gpio;
static void __iomem *ac97_reg_base;
/*
@@ -321,7 +321,6 @@ int pxa2xx_ac97_hw_probe(struct platform_device *dev)
{
int ret;
int irq;
- pxa2xx_audio_ops_t *pdata = dev->dev.platform_data;
ac97_reg_base = devm_platform_ioremap_resource(dev, 0);
if (IS_ERR(ac97_reg_base)) {
@@ -329,32 +328,15 @@ int pxa2xx_ac97_hw_probe(struct platform_device *dev)
return PTR_ERR(ac97_reg_base);
}
- if (pdata) {
- switch (pdata->reset_gpio) {
- case 95:
- case 113:
- reset_gpio = pdata->reset_gpio;
- break;
- case 0:
- reset_gpio = 113;
- break;
- case -1:
- break;
- default:
- dev_err(&dev->dev, "Invalid reset GPIO %d\n",
- pdata->reset_gpio);
- }
- } else if (!pdata && dev->dev.of_node) {
- pdata = devm_kzalloc(&dev->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
- pdata->reset_gpio = of_get_named_gpio(dev->dev.of_node,
- "reset-gpios", 0);
- if (pdata->reset_gpio == -ENOENT)
- pdata->reset_gpio = -1;
- else if (pdata->reset_gpio < 0)
- return pdata->reset_gpio;
- reset_gpio = pdata->reset_gpio;
+ if (dev->dev.of_node) {
+ /* Assert reset using GPIOD_OUT_HIGH, because reset is GPIO_ACTIVE_LOW */
+ rst_gpio = devm_gpiod_get(&dev->dev, "reset", GPIOD_OUT_HIGH);
+ ret = PTR_ERR(rst_gpio);
+ if (ret == -ENOENT)
+ reset_gpio = -1;
+ else if (ret)
+ return ret;
+ reset_gpio = desc_to_gpio(rst_gpio);
} else {
if (cpu_is_pxa27x())
reset_gpio = 113;
@@ -367,13 +349,7 @@ int pxa2xx_ac97_hw_probe(struct platform_device *dev)
* here so that it is an output driven high when switching from
* AC97_nRESET alt function to generic gpio.
*/
- ret = gpio_request_one(reset_gpio, GPIOF_OUT_INIT_HIGH,
- "pxa27x ac97 reset");
- if (ret < 0) {
- pr_err("%s: gpio_request_one() failed: %d\n",
- __func__, ret);
- goto err_conf;
- }
+ gpiod_set_consumer_name(rst_gpio, "pxa27x ac97 reset");
pxa27x_configure_ac97reset(reset_gpio, false);
ac97conf_clk = clk_get(&dev->dev, "AC97CONFCLK");
@@ -424,8 +400,6 @@ EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_probe);
void pxa2xx_ac97_hw_remove(struct platform_device *dev)
{
- if (cpu_is_pxa27x())
- gpio_free(reset_gpio);
writel(readl(ac97_reg_base + GCR) | (GCR_ACLINK_OFF), ac97_reg_base + GCR);
free_irq(platform_get_irq(dev, 0), NULL);
if (ac97conf_clk) {
diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c
deleted file mode 100644
index 77b11616a7ee..000000000000
--- a/sound/arm/pxa2xx-ac97.c
+++ /dev/null
@@ -1,286 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * linux/sound/pxa2xx-ac97.c -- AC97 support for the Intel PXA2xx chip.
- *
- * Author: Nicolas Pitre
- * Created: Dec 02, 2004
- * Copyright: MontaVista Software Inc.
- */
-
-#include <linux/init.h>
-#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/dmaengine.h>
-#include <linux/dma-mapping.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/ac97_codec.h>
-#include <sound/initval.h>
-#include <sound/pxa2xx-lib.h>
-#include <sound/dmaengine_pcm.h>
-
-#include <linux/platform_data/asoc-pxa.h>
-
-static void pxa2xx_ac97_legacy_reset(struct snd_ac97 *ac97)
-{
- if (!pxa2xx_ac97_try_cold_reset())
- pxa2xx_ac97_try_warm_reset();
-
- pxa2xx_ac97_finish_reset();
-}
-
-static unsigned short pxa2xx_ac97_legacy_read(struct snd_ac97 *ac97,
- unsigned short reg)
-{
- int ret;
-
- ret = pxa2xx_ac97_read(ac97->num, reg);
- if (ret < 0)
- return 0;
- else
- return (unsigned short)(ret & 0xffff);
-}
-
-static void pxa2xx_ac97_legacy_write(struct snd_ac97 *ac97,
- unsigned short reg, unsigned short val)
-{
- pxa2xx_ac97_write(ac97->num, reg, val);
-}
-
-static const struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
- .read = pxa2xx_ac97_legacy_read,
- .write = pxa2xx_ac97_legacy_write,
- .reset = pxa2xx_ac97_legacy_reset,
-};
-
-static struct snd_pcm *pxa2xx_ac97_pcm;
-static struct snd_ac97 *pxa2xx_ac97_ac97;
-
-static int pxa2xx_ac97_pcm_open(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- pxa2xx_audio_ops_t *platform_ops;
- int ret, i;
-
- ret = pxa2xx_pcm_open(substream);
- if (ret)
- return ret;
-
- runtime->hw.channels_min = 2;
- runtime->hw.channels_max = 2;
-
- i = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
- AC97_RATES_FRONT_DAC : AC97_RATES_ADC;
- runtime->hw.rates = pxa2xx_ac97_ac97->rates[i];
- snd_pcm_limit_hw_rates(runtime);
-
- platform_ops = substream->pcm->card->dev->platform_data;
- if (platform_ops && platform_ops->startup) {
- ret = platform_ops->startup(substream, platform_ops->priv);
- if (ret < 0)
- pxa2xx_pcm_close(substream);
- }
-
- return ret;
-}
-
-static int pxa2xx_ac97_pcm_close(struct snd_pcm_substream *substream)
-{
- pxa2xx_audio_ops_t *platform_ops;
-
- platform_ops = substream->pcm->card->dev->platform_data;
- if (platform_ops && platform_ops->shutdown)
- platform_ops->shutdown(substream, platform_ops->priv);
-
- return 0;
-}
-
-static int pxa2xx_ac97_pcm_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
- AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE;
- int ret;
-
- ret = pxa2xx_pcm_prepare(substream);
- if (ret < 0)
- return ret;
-
- return snd_ac97_set_rate(pxa2xx_ac97_ac97, reg, runtime->rate);
-}
-
-static int pxa2xx_ac97_do_suspend(struct snd_card *card)
-{
- pxa2xx_audio_ops_t *platform_ops = card->dev->platform_data;
-
- snd_power_change_state(card, SNDRV_CTL_POWER_D3cold);
- snd_ac97_suspend(pxa2xx_ac97_ac97);
- if (platform_ops && platform_ops->suspend)
- platform_ops->suspend(platform_ops->priv);
-
- return pxa2xx_ac97_hw_suspend();
-}
-
-static int pxa2xx_ac97_do_resume(struct snd_card *card)
-{
- pxa2xx_audio_ops_t *platform_ops = card->dev->platform_data;
- int rc;
-
- rc = pxa2xx_ac97_hw_resume();
- if (rc)
- return rc;
-
- if (platform_ops && platform_ops->resume)
- platform_ops->resume(platform_ops->priv);
- snd_ac97_resume(pxa2xx_ac97_ac97);
- snd_power_change_state(card, SNDRV_CTL_POWER_D0);
-
- return 0;
-}
-
-static int pxa2xx_ac97_suspend(struct device *dev)
-{
- struct snd_card *card = dev_get_drvdata(dev);
- int ret = 0;
-
- if (card)
- ret = pxa2xx_ac97_do_suspend(card);
-
- return ret;
-}
-
-static int pxa2xx_ac97_resume(struct device *dev)
-{
- struct snd_card *card = dev_get_drvdata(dev);
- int ret = 0;
-
- if (card)
- ret = pxa2xx_ac97_do_resume(card);
-
- return ret;
-}
-
-static DEFINE_SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops, pxa2xx_ac97_suspend, pxa2xx_ac97_resume);
-
-static const struct snd_pcm_ops pxa2xx_ac97_pcm_ops = {
- .open = pxa2xx_ac97_pcm_open,
- .close = pxa2xx_ac97_pcm_close,
- .hw_params = pxa2xx_pcm_hw_params,
- .prepare = pxa2xx_ac97_pcm_prepare,
- .trigger = pxa2xx_pcm_trigger,
- .pointer = pxa2xx_pcm_pointer,
-};
-
-
-static int pxa2xx_ac97_pcm_new(struct snd_card *card)
-{
- struct snd_pcm *pcm;
- int ret;
-
- ret = snd_pcm_new(card, "PXA2xx-PCM", 0, 1, 1, &pcm);
- if (ret)
- goto out;
-
- ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
- if (ret)
- goto out;
-
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pxa2xx_ac97_pcm_ops);
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pxa2xx_ac97_pcm_ops);
- ret = pxa2xx_pcm_preallocate_dma_buffer(pcm);
- if (ret)
- goto out;
-
- pxa2xx_ac97_pcm = pcm;
- ret = 0;
-
- out:
- return ret;
-}
-
-static int pxa2xx_ac97_probe(struct platform_device *dev)
-{
- struct snd_card *card;
- struct snd_ac97_bus *ac97_bus;
- struct snd_ac97_template ac97_template;
- int ret;
- pxa2xx_audio_ops_t *pdata = dev->dev.platform_data;
-
- if (dev->id >= 0) {
- dev_err(&dev->dev, "PXA2xx has only one AC97 port.\n");
- ret = -ENXIO;
- goto err_dev;
- }
-
- ret = snd_card_new(&dev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
- THIS_MODULE, 0, &card);
- if (ret < 0)
- goto err;
-
- strscpy(card->driver, dev->dev.driver->name, sizeof(card->driver));
-
- ret = pxa2xx_ac97_pcm_new(card);
- if (ret)
- goto err;
-
- ret = pxa2xx_ac97_hw_probe(dev);
- if (ret)
- goto err;
-
- ret = snd_ac97_bus(card, 0, &pxa2xx_ac97_ops, NULL, &ac97_bus);
- if (ret)
- goto err_remove;
- memset(&ac97_template, 0, sizeof(ac97_template));
- ret = snd_ac97_mixer(ac97_bus, &ac97_template, &pxa2xx_ac97_ac97);
- if (ret)
- goto err_remove;
-
- snprintf(card->shortname, sizeof(card->shortname),
- "%s", snd_ac97_get_short_name(pxa2xx_ac97_ac97));
- snprintf(card->longname, sizeof(card->longname),
- "%s (%s)", dev->dev.driver->name, card->mixername);
-
- if (pdata && pdata->codec_pdata[0])
- snd_ac97_dev_add_pdata(ac97_bus->codec[0], pdata->codec_pdata[0]);
- ret = snd_card_register(card);
- if (ret == 0) {
- platform_set_drvdata(dev, card);
- return 0;
- }
-
-err_remove:
- pxa2xx_ac97_hw_remove(dev);
-err:
- if (card)
- snd_card_free(card);
-err_dev:
- return ret;
-}
-
-static void pxa2xx_ac97_remove(struct platform_device *dev)
-{
- struct snd_card *card = platform_get_drvdata(dev);
-
- if (card) {
- snd_card_free(card);
- pxa2xx_ac97_hw_remove(dev);
- }
-}
-
-static struct platform_driver pxa2xx_ac97_driver = {
- .probe = pxa2xx_ac97_probe,
- .remove = pxa2xx_ac97_remove,
- .driver = {
- .name = "pxa2xx-ac97",
- .pm = &pxa2xx_ac97_pm_ops,
- },
-};
-
-module_platform_driver(pxa2xx_ac97_driver);
-
-MODULE_AUTHOR("Nicolas Pitre");
-MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:pxa2xx-ac97");
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
index da514fef45bc..ed2eeb914c6d 100644
--- a/sound/core/compress_offload.c
+++ b/sound/core/compress_offload.c
@@ -514,12 +514,12 @@ static int
snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg)
{
int retval;
- struct snd_compr_codec_caps *caps __free(kfree) = NULL;
if (!stream->ops->get_codec_caps)
return -ENXIO;
- caps = kzalloc(sizeof(*caps), GFP_KERNEL);
+ struct snd_compr_codec_caps *caps __free(kfree) =
+ kzalloc(sizeof(*caps), GFP_KERNEL);
if (!caps)
return -ENOMEM;
@@ -647,7 +647,6 @@ snd_compress_check_input(struct snd_compr_stream *stream, struct snd_compr_param
static int
snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)
{
- struct snd_compr_params *params __free(kfree) = NULL;
int retval;
if (stream->runtime->state == SNDRV_PCM_STATE_OPEN || stream->next_track) {
@@ -655,7 +654,9 @@ snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)
* we should allow parameter change only when stream has been
* opened not in other cases
*/
- params = memdup_user((void __user *)arg, sizeof(*params));
+ struct snd_compr_params *params __free(kfree) =
+ memdup_user((void __user *)arg, sizeof(*params));
+
if (IS_ERR(params))
return PTR_ERR(params);
@@ -687,13 +688,13 @@ snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)
static int
snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg)
{
- struct snd_codec *params __free(kfree) = NULL;
int retval;
if (!stream->ops->get_params)
return -EBADFD;
- params = kzalloc(sizeof(*params), GFP_KERNEL);
+ struct snd_codec *params __free(kfree) =
+ kzalloc(sizeof(*params), GFP_KERNEL);
if (!params)
return -ENOMEM;
retval = stream->ops->get_params(stream, params);
@@ -1104,12 +1105,13 @@ cleanup:
static int snd_compr_task_create(struct snd_compr_stream *stream, unsigned long arg)
{
- struct snd_compr_task *task __free(kfree) = NULL;
int retval;
if (stream->runtime->state != SNDRV_PCM_STATE_SETUP)
return -EPERM;
- task = memdup_user((void __user *)arg, sizeof(*task));
+
+ struct snd_compr_task *task __free(kfree) =
+ memdup_user((void __user *)arg, sizeof(*task));
if (IS_ERR(task))
return PTR_ERR(task);
retval = snd_compr_task_new(stream, task);
@@ -1165,12 +1167,13 @@ static int snd_compr_task_start(struct snd_compr_stream *stream, struct snd_comp
static int snd_compr_task_start_ioctl(struct snd_compr_stream *stream, unsigned long arg)
{
- struct snd_compr_task *task __free(kfree) = NULL;
int retval;
if (stream->runtime->state != SNDRV_PCM_STATE_SETUP)
return -EPERM;
- task = memdup_user((void __user *)arg, sizeof(*task));
+
+ struct snd_compr_task *task __free(kfree) =
+ memdup_user((void __user *)arg, sizeof(*task));
if (IS_ERR(task))
return PTR_ERR(task);
retval = snd_compr_task_start(stream, task);
@@ -1256,12 +1259,13 @@ static int snd_compr_task_status(struct snd_compr_stream *stream,
static int snd_compr_task_status_ioctl(struct snd_compr_stream *stream, unsigned long arg)
{
- struct snd_compr_task_status *status __free(kfree) = NULL;
int retval;
if (stream->runtime->state != SNDRV_PCM_STATE_SETUP)
return -EPERM;
- status = memdup_user((void __user *)arg, sizeof(*status));
+
+ struct snd_compr_task_status *status __free(kfree) =
+ memdup_user((void __user *)arg, sizeof(*status));
if (IS_ERR(status))
return PTR_ERR(status);
retval = snd_compr_task_status(stream, status);
diff --git a/sound/core/control.c b/sound/core/control.c
index 9c3fd5113a61..486d1bc4dac2 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -867,9 +867,9 @@ EXPORT_SYMBOL(snd_ctl_find_id);
static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl,
unsigned int cmd, void __user *arg)
{
- struct snd_ctl_card_info *info __free(kfree) = NULL;
+ struct snd_ctl_card_info *info __free(kfree) =
+ kzalloc(sizeof(*info), GFP_KERNEL);
- info = kzalloc(sizeof(*info), GFP_KERNEL);
if (! info)
return -ENOMEM;
scoped_guard(rwsem_read, &snd_ioctl_rwsem) {
@@ -1244,10 +1244,10 @@ static int snd_ctl_elem_read(struct snd_card *card,
static int snd_ctl_elem_read_user(struct snd_card *card,
struct snd_ctl_elem_value __user *_control)
{
- struct snd_ctl_elem_value *control __free(kfree) = NULL;
int result;
+ struct snd_ctl_elem_value *control __free(kfree) =
+ memdup_user(_control, sizeof(*control));
- control = memdup_user(_control, sizeof(*control));
if (IS_ERR(control))
return PTR_ERR(control);
@@ -1320,11 +1320,11 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
static int snd_ctl_elem_write_user(struct snd_ctl_file *file,
struct snd_ctl_elem_value __user *_control)
{
- struct snd_ctl_elem_value *control __free(kfree) = NULL;
struct snd_card *card;
int result;
+ struct snd_ctl_elem_value *control __free(kfree) =
+ memdup_user(_control, sizeof(*control));
- control = memdup_user(_control, sizeof(*control));
if (IS_ERR(control))
return PTR_ERR(control);
diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c
index 6459809ed364..b8988a4bcd9b 100644
--- a/sound/core/control_compat.c
+++ b/sound/core/control_compat.c
@@ -80,10 +80,10 @@ static int snd_ctl_elem_info_compat(struct snd_ctl_file *ctl,
struct snd_ctl_elem_info32 __user *data32)
{
struct snd_card *card = ctl->card;
- struct snd_ctl_elem_info *data __free(kfree) = NULL;
int err;
+ struct snd_ctl_elem_info *data __free(kfree) =
+ kzalloc(sizeof(*data), GFP_KERNEL);
- data = kzalloc(sizeof(*data), GFP_KERNEL);
if (! data)
return -ENOMEM;
@@ -169,14 +169,15 @@ static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id,
int *countp)
{
struct snd_kcontrol *kctl;
- struct snd_ctl_elem_info *info __free(kfree) = NULL;
int err;
guard(rwsem_read)(&card->controls_rwsem);
kctl = snd_ctl_find_id(card, id);
if (!kctl)
return -ENOENT;
- info = kzalloc(sizeof(*info), GFP_KERNEL);
+
+ struct snd_ctl_elem_info *info __free(kfree) =
+ kzalloc(sizeof(*info), GFP_KERNEL);
if (info == NULL)
return -ENOMEM;
info->id = *id;
@@ -280,10 +281,10 @@ static int copy_ctl_value_to_user(void __user *userdata,
static int __ctl_elem_read_user(struct snd_card *card,
void __user *userdata, void __user *valuep)
{
- struct snd_ctl_elem_value *data __free(kfree) = NULL;
int err, type, count;
+ struct snd_ctl_elem_value *data __free(kfree) =
+ kzalloc(sizeof(*data), GFP_KERNEL);
- data = kzalloc(sizeof(*data), GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
@@ -314,11 +315,11 @@ static int ctl_elem_read_user(struct snd_card *card,
static int __ctl_elem_write_user(struct snd_ctl_file *file,
void __user *userdata, void __user *valuep)
{
- struct snd_ctl_elem_value *data __free(kfree) = NULL;
struct snd_card *card = file->card;
int err, type, count;
+ struct snd_ctl_elem_value *data __free(kfree) =
+ kzalloc(sizeof(*data), GFP_KERNEL);
- data = kzalloc(sizeof(*data), GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
@@ -378,9 +379,9 @@ static int snd_ctl_elem_add_compat(struct snd_ctl_file *file,
struct snd_ctl_elem_info32 __user *data32,
int replace)
{
- struct snd_ctl_elem_info *data __free(kfree) = NULL;
+ struct snd_ctl_elem_info *data __free(kfree) =
+ kzalloc(sizeof(*data), GFP_KERNEL);
- data = kzalloc(sizeof(*data), GFP_KERNEL);
if (! data)
return -ENOMEM;
diff --git a/sound/core/control_led.c b/sound/core/control_led.c
index e33dfcf863cf..c7641d5084e7 100644
--- a/sound/core/control_led.c
+++ b/sound/core/control_led.c
@@ -245,12 +245,12 @@ DEFINE_FREE(snd_card_unref, struct snd_card *, if (_T) snd_card_unref(_T))
static int snd_ctl_led_set_id(int card_number, struct snd_ctl_elem_id *id,
unsigned int group, bool set)
{
- struct snd_card *card __free(snd_card_unref) = NULL;
struct snd_kcontrol *kctl;
struct snd_kcontrol_volatile *vd;
unsigned int ioff, access, new_access;
+ struct snd_card *card __free(snd_card_unref) =
+ snd_card_ref(card_number);
- card = snd_card_ref(card_number);
if (!card)
return -ENXIO;
guard(rwsem_write)(&card->controls_rwsem);
@@ -302,13 +302,13 @@ static void snd_ctl_led_clean(struct snd_card *card)
static int snd_ctl_led_reset(int card_number, unsigned int group)
{
- struct snd_card *card __free(snd_card_unref) = NULL;
struct snd_ctl_led_ctl *lctl, *_lctl;
struct snd_ctl_led *led;
struct snd_kcontrol_volatile *vd;
bool change = false;
+ struct snd_card *card __free(snd_card_unref) =
+ snd_card_ref(card_number);
- card = snd_card_ref(card_number);
if (!card)
return -ENXIO;
@@ -598,11 +598,11 @@ static ssize_t list_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct snd_ctl_led_card *led_card = container_of(dev, struct snd_ctl_led_card, dev);
- struct snd_card *card __free(snd_card_unref) = NULL;
struct snd_ctl_led_ctl *lctl;
size_t l = 0;
+ struct snd_card *card __free(snd_card_unref) =
+ snd_card_ref(led_card->number);
- card = snd_card_ref(led_card->number);
if (!card)
return -ENXIO;
guard(rwsem_read)(&card->controls_rwsem);
diff --git a/sound/core/ctljack.c b/sound/core/ctljack.c
index 709b1a9c2caa..6b6ab34fbde8 100644
--- a/sound/core/ctljack.c
+++ b/sound/core/ctljack.c
@@ -7,6 +7,7 @@
#include <linux/kernel.h>
#include <linux/export.h>
+#include <linux/string.h>
#include <sound/core.h>
#include <sound/control.h>
@@ -46,17 +47,20 @@ static int get_available_index(struct snd_card *card, const char *name)
return sid.index;
}
-static void jack_kctl_name_gen(char *name, const char *src_name, int size)
+static void jack_kctl_name_gen(char *name, const char *src_name, size_t size)
{
size_t count = strlen(src_name);
- bool need_cat = true;
+ const char *suf = " Jack";
+ size_t suf_len = strlen(suf);
+ bool append_suf = true;
- /* remove redundant " Jack" from src_name */
- if (count >= 5)
- need_cat = strncmp(&src_name[count - 5], " Jack", 5) ? true : false;
-
- snprintf(name, size, need_cat ? "%s Jack" : "%s", src_name);
+ if (count >= suf_len)
+ append_suf = strncmp(&src_name[count - suf_len], suf, suf_len) != 0;
+ if (append_suf)
+ snprintf(name, size, "%s%s", src_name, suf);
+ else
+ strscpy(name, src_name, size);
}
struct snd_kcontrol *
diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c
index e839a4bb93f8..f4ad0bfb4dac 100644
--- a/sound/core/oss/mixer_oss.c
+++ b/sound/core/oss/mixer_oss.c
@@ -517,19 +517,22 @@ static void snd_mixer_oss_get_volume1_vol(struct snd_mixer_oss_file *fmixer,
unsigned int numid,
int *left, int *right)
{
- struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;
- struct snd_ctl_elem_value *uctl __free(kfree) = NULL;
struct snd_kcontrol *kctl;
struct snd_card *card = fmixer->card;
if (numid == ID_UNKNOWN)
return;
guard(rwsem_read)(&card->controls_rwsem);
+ if (card->shutdown)
+ return;
kctl = snd_ctl_find_numid(card, numid);
if (!kctl)
return;
- uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
- uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
+
+ struct snd_ctl_elem_info *uinfo __free(kfree) =
+ kzalloc(sizeof(*uinfo), GFP_KERNEL);
+ struct snd_ctl_elem_value *uctl __free(kfree) =
+ kzalloc(sizeof(*uctl), GFP_KERNEL);
if (uinfo == NULL || uctl == NULL)
return;
if (kctl->info(kctl, uinfo))
@@ -550,19 +553,22 @@ static void snd_mixer_oss_get_volume1_sw(struct snd_mixer_oss_file *fmixer,
int *left, int *right,
int route)
{
- struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;
- struct snd_ctl_elem_value *uctl __free(kfree) = NULL;
struct snd_kcontrol *kctl;
struct snd_card *card = fmixer->card;
if (numid == ID_UNKNOWN)
return;
guard(rwsem_read)(&card->controls_rwsem);
+ if (card->shutdown)
+ return;
kctl = snd_ctl_find_numid(card, numid);
if (!kctl)
return;
- uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
- uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
+
+ struct snd_ctl_elem_info *uinfo __free(kfree) =
+ kzalloc(sizeof(*uinfo), GFP_KERNEL);
+ struct snd_ctl_elem_value *uctl __free(kfree) =
+ kzalloc(sizeof(*uctl), GFP_KERNEL);
if (uinfo == NULL || uctl == NULL)
return;
if (kctl->info(kctl, uinfo))
@@ -609,8 +615,6 @@ static void snd_mixer_oss_put_volume1_vol(struct snd_mixer_oss_file *fmixer,
unsigned int numid,
int left, int right)
{
- struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;
- struct snd_ctl_elem_value *uctl __free(kfree) = NULL;
struct snd_kcontrol *kctl;
struct snd_card *card = fmixer->card;
int res;
@@ -618,11 +622,16 @@ static void snd_mixer_oss_put_volume1_vol(struct snd_mixer_oss_file *fmixer,
if (numid == ID_UNKNOWN)
return;
guard(rwsem_read)(&card->controls_rwsem);
+ if (card->shutdown)
+ return;
kctl = snd_ctl_find_numid(card, numid);
if (!kctl)
return;
- uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
- uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
+
+ struct snd_ctl_elem_info *uinfo __free(kfree) =
+ kzalloc(sizeof(*uinfo), GFP_KERNEL);
+ struct snd_ctl_elem_value *uctl __free(kfree) =
+ kzalloc(sizeof(*uctl), GFP_KERNEL);
if (uinfo == NULL || uctl == NULL)
return;
if (kctl->info(kctl, uinfo))
@@ -646,8 +655,6 @@ static void snd_mixer_oss_put_volume1_sw(struct snd_mixer_oss_file *fmixer,
int left, int right,
int route)
{
- struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;
- struct snd_ctl_elem_value *uctl __free(kfree) = NULL;
struct snd_kcontrol *kctl;
struct snd_card *card = fmixer->card;
int res;
@@ -655,11 +662,16 @@ static void snd_mixer_oss_put_volume1_sw(struct snd_mixer_oss_file *fmixer,
if (numid == ID_UNKNOWN)
return;
guard(rwsem_read)(&card->controls_rwsem);
+ if (card->shutdown)
+ return;
kctl = snd_ctl_find_numid(card, numid);
if (!kctl)
return;
- uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
- uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
+
+ struct snd_ctl_elem_info *uinfo __free(kfree) =
+ kzalloc(sizeof(*uinfo), GFP_KERNEL);
+ struct snd_ctl_elem_value *uctl __free(kfree) =
+ kzalloc(sizeof(*uctl), GFP_KERNEL);
if (uinfo == NULL || uctl == NULL)
return;
if (kctl->info(kctl, uinfo))
@@ -783,15 +795,17 @@ static int snd_mixer_oss_get_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned
struct snd_kcontrol *kctl;
struct snd_mixer_oss_slot *pslot;
struct slot *slot;
- struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;
- struct snd_ctl_elem_value *uctl __free(kfree) = NULL;
int err, idx;
- uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
- uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
+ struct snd_ctl_elem_info *uinfo __free(kfree) =
+ kzalloc(sizeof(*uinfo), GFP_KERNEL);
+ struct snd_ctl_elem_value *uctl __free(kfree) =
+ kzalloc(sizeof(*uctl), GFP_KERNEL);
if (uinfo == NULL || uctl == NULL)
return -ENOMEM;
guard(rwsem_read)(&card->controls_rwsem);
+ if (card->shutdown)
+ return -ENODEV;
kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);
if (!kctl)
return -ENOENT;
@@ -825,16 +839,18 @@ static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned
struct snd_kcontrol *kctl;
struct snd_mixer_oss_slot *pslot;
struct slot *slot = NULL;
- struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;
- struct snd_ctl_elem_value *uctl __free(kfree) = NULL;
int err;
unsigned int idx;
- uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
- uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
+ struct snd_ctl_elem_info *uinfo __free(kfree) =
+ kzalloc(sizeof(*uinfo), GFP_KERNEL);
+ struct snd_ctl_elem_value *uctl __free(kfree) =
+ kzalloc(sizeof(*uctl), GFP_KERNEL);
if (uinfo == NULL || uctl == NULL)
return -ENOMEM;
guard(rwsem_read)(&card->controls_rwsem);
+ if (card->shutdown)
+ return -ENODEV;
kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);
if (!kctl)
return -ENOENT;
@@ -872,18 +888,20 @@ struct snd_mixer_oss_assign_table {
static int snd_mixer_oss_build_test(struct snd_mixer_oss *mixer, struct slot *slot, const char *name, int index, int item)
{
- struct snd_ctl_elem_info *info __free(kfree) = NULL;
struct snd_kcontrol *kcontrol;
struct snd_card *card = mixer->card;
int err;
+ struct snd_ctl_elem_info *info __free(kfree) =
+ kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
scoped_guard(rwsem_read, &card->controls_rwsem) {
+ if (card->shutdown)
+ return -ENODEV;
kcontrol = snd_mixer_oss_test_id(mixer, name, index);
if (kcontrol == NULL)
return 0;
- info = kmalloc(sizeof(*info), GFP_KERNEL);
- if (!info)
- return -ENOMEM;
err = kcontrol->info(kcontrol, info);
if (err < 0)
return err;
@@ -1002,13 +1020,15 @@ static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer,
if (snd_mixer_oss_build_test_all(mixer, ptr, &slot))
return 0;
guard(rwsem_read)(&mixer->card->controls_rwsem);
+ if (mixer->card->shutdown)
+ return -ENODEV;
kctl = NULL;
if (!ptr->index)
kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);
if (kctl) {
- struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;
+ struct snd_ctl_elem_info *uinfo __free(kfree) =
+ kzalloc(sizeof(*uinfo), GFP_KERNEL);
- uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
if (!uinfo)
return -ENOMEM;
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c
index b12df5b5ddfc..3bc94d34b35e 100644
--- a/sound/core/oss/pcm_oss.c
+++ b/sound/core/oss/pcm_oss.c
@@ -377,7 +377,6 @@ static int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm,
snd_pcm_hw_param_t var, unsigned int best,
int *dir)
{
- struct snd_pcm_hw_params *save __free(kfree) = NULL;
int v;
unsigned int saved_min;
int last = 0;
@@ -397,19 +396,22 @@ static int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm,
maxdir = 1;
max--;
}
- save = kmalloc(sizeof(*save), GFP_KERNEL);
+
+ struct snd_pcm_hw_params *save __free(kfree) =
+ kmalloc(sizeof(*save), GFP_KERNEL);
if (save == NULL)
return -ENOMEM;
*save = *params;
saved_min = min;
min = snd_pcm_hw_param_min(pcm, params, var, min, &mindir);
if (min >= 0) {
- struct snd_pcm_hw_params *params1 __free(kfree) = NULL;
if (max < 0)
goto _end;
if ((unsigned int)min == saved_min && mindir == valdir)
goto _end;
- params1 = kmalloc(sizeof(*params1), GFP_KERNEL);
+
+ struct snd_pcm_hw_params *params1 __free(kfree) =
+ kmalloc(sizeof(*params1), GFP_KERNEL);
if (params1 == NULL)
return -ENOMEM;
*params1 = *save;
@@ -781,10 +783,10 @@ static int choose_rate(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, unsigned int best_rate)
{
const struct snd_interval *it;
- struct snd_pcm_hw_params *save __free(kfree) = NULL;
unsigned int rate, prev;
- save = kmalloc(sizeof(*save), GFP_KERNEL);
+ struct snd_pcm_hw_params *save __free(kfree) =
+ kmalloc(sizeof(*save), GFP_KERNEL);
if (save == NULL)
return -ENOMEM;
*save = *params;
@@ -1836,7 +1838,6 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file)
struct snd_pcm_substream *substream;
int err;
int direct;
- struct snd_pcm_hw_params *params __free(kfree) = NULL;
unsigned int formats = 0;
const struct snd_mask *format_mask;
int fmt;
@@ -1856,7 +1857,9 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file)
AFMT_S32_LE | AFMT_S32_BE |
AFMT_S24_LE | AFMT_S24_BE |
AFMT_S24_PACKED;
- params = kmalloc(sizeof(*params), GFP_KERNEL);
+
+ struct snd_pcm_hw_params *params __free(kfree) =
+ kmalloc(sizeof(*params), GFP_KERNEL);
if (!params)
return -ENOMEM;
_snd_pcm_hw_params_any(params);
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 283aac441fa0..0b512085eb63 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -328,13 +328,13 @@ static const char *snd_pcm_oss_format_name(int format)
static void snd_pcm_proc_info_read(struct snd_pcm_substream *substream,
struct snd_info_buffer *buffer)
{
- struct snd_pcm_info *info __free(kfree) = NULL;
int err;
if (! substream)
return;
- info = kmalloc(sizeof(*info), GFP_KERNEL);
+ struct snd_pcm_info *info __free(kfree) =
+ kmalloc(sizeof(*info), GFP_KERNEL);
if (!info)
return;
diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c
index 54eb9bd8eb21..e86f68f1f23c 100644
--- a/sound/core/pcm_compat.c
+++ b/sound/core/pcm_compat.c
@@ -235,7 +235,6 @@ static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream,
int refine,
struct snd_pcm_hw_params32 __user *data32)
{
- struct snd_pcm_hw_params *data __free(kfree) = NULL;
struct snd_pcm_runtime *runtime;
int err;
@@ -243,7 +242,8 @@ static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream,
if (!runtime)
return -ENOTTY;
- data = kmalloc(sizeof(*data), GFP_KERNEL);
+ struct snd_pcm_hw_params *data __free(kfree) =
+ kmalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -332,7 +332,6 @@ static int snd_pcm_ioctl_xfern_compat(struct snd_pcm_substream *substream,
compat_caddr_t buf;
compat_caddr_t __user *bufptr;
u32 frames;
- void __user **bufs __free(kfree) = NULL;
int err, ch, i;
if (! substream->runtime)
@@ -349,7 +348,9 @@ static int snd_pcm_ioctl_xfern_compat(struct snd_pcm_substream *substream,
get_user(frames, &data32->frames))
return -EFAULT;
bufptr = compat_ptr(buf);
- bufs = kmalloc_array(ch, sizeof(void __user *), GFP_KERNEL);
+
+ void __user **bufs __free(kfree) =
+ kmalloc_array(ch, sizeof(void __user *), GFP_KERNEL);
if (bufs == NULL)
return -ENOMEM;
for (i = 0; i < ch; i++) {
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 932a9bf98cbc..0a358d94b17c 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -242,10 +242,10 @@ int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info)
int snd_pcm_info_user(struct snd_pcm_substream *substream,
struct snd_pcm_info __user * _info)
{
- struct snd_pcm_info *info __free(kfree) = NULL;
int err;
+ struct snd_pcm_info *info __free(kfree) =
+ kmalloc(sizeof(*info), GFP_KERNEL);
- info = kmalloc(sizeof(*info), GFP_KERNEL);
if (! info)
return -ENOMEM;
err = snd_pcm_info(substream, info);
@@ -364,7 +364,6 @@ static int constrain_params_by_rules(struct snd_pcm_substream *substream,
struct snd_pcm_hw_constraints *constrs =
&substream->runtime->hw_constraints;
unsigned int k;
- unsigned int *rstamps __free(kfree) = NULL;
unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1];
unsigned int stamp;
struct snd_pcm_hw_rule *r;
@@ -380,7 +379,8 @@ static int constrain_params_by_rules(struct snd_pcm_substream *substream,
* Each member of 'rstamps' array represents the sequence number of
* recent application of corresponding rule.
*/
- rstamps = kcalloc(constrs->rules_num, sizeof(unsigned int), GFP_KERNEL);
+ unsigned int *rstamps __free(kfree) =
+ kcalloc(constrs->rules_num, sizeof(unsigned int), GFP_KERNEL);
if (!rstamps)
return -ENOMEM;
@@ -583,10 +583,10 @@ EXPORT_SYMBOL(snd_pcm_hw_refine);
static int snd_pcm_hw_refine_user(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params __user * _params)
{
- struct snd_pcm_hw_params *params __free(kfree) = NULL;
int err;
+ struct snd_pcm_hw_params *params __free(kfree) =
+ memdup_user(_params, sizeof(*params));
- params = memdup_user(_params, sizeof(*params));
if (IS_ERR(params))
return PTR_ERR(params);
@@ -889,10 +889,10 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
static int snd_pcm_hw_params_user(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params __user * _params)
{
- struct snd_pcm_hw_params *params __free(kfree) = NULL;
int err;
+ struct snd_pcm_hw_params *params __free(kfree) =
+ memdup_user(_params, sizeof(*params));
- params = memdup_user(_params, sizeof(*params));
if (IS_ERR(params))
return PTR_ERR(params);
@@ -2267,7 +2267,6 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
{
struct snd_pcm_file *pcm_file;
struct snd_pcm_substream *substream1;
- struct snd_pcm_group *group __free(kfree) = NULL;
struct snd_pcm_group *target_group;
bool nonatomic = substream->pcm->nonatomic;
CLASS(fd, f)(fd);
@@ -2283,7 +2282,8 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
if (substream == substream1)
return -EINVAL;
- group = kzalloc(sizeof(*group), GFP_KERNEL);
+ struct snd_pcm_group *group __free(kfree) =
+ kzalloc(sizeof(*group), GFP_KERNEL);
if (!group)
return -ENOMEM;
snd_pcm_group_init(group);
@@ -3577,7 +3577,6 @@ static ssize_t snd_pcm_readv(struct kiocb *iocb, struct iov_iter *to)
struct snd_pcm_runtime *runtime;
snd_pcm_sframes_t result;
unsigned long i;
- void __user **bufs __free(kfree) = NULL;
snd_pcm_uframes_t frames;
const struct iovec *iov = iter_iov(to);
@@ -3596,7 +3595,9 @@ static ssize_t snd_pcm_readv(struct kiocb *iocb, struct iov_iter *to)
if (!frame_aligned(runtime, iov->iov_len))
return -EINVAL;
frames = bytes_to_samples(runtime, iov->iov_len);
- bufs = kmalloc_array(to->nr_segs, sizeof(void *), GFP_KERNEL);
+
+ void __user **bufs __free(kfree) =
+ kmalloc_array(to->nr_segs, sizeof(void *), GFP_KERNEL);
if (bufs == NULL)
return -ENOMEM;
for (i = 0; i < to->nr_segs; ++i) {
@@ -3616,7 +3617,6 @@ static ssize_t snd_pcm_writev(struct kiocb *iocb, struct iov_iter *from)
struct snd_pcm_runtime *runtime;
snd_pcm_sframes_t result;
unsigned long i;
- void __user **bufs __free(kfree) = NULL;
snd_pcm_uframes_t frames;
const struct iovec *iov = iter_iov(from);
@@ -3634,7 +3634,9 @@ static ssize_t snd_pcm_writev(struct kiocb *iocb, struct iov_iter *from)
!frame_aligned(runtime, iov->iov_len))
return -EINVAL;
frames = bytes_to_samples(runtime, iov->iov_len);
- bufs = kmalloc_array(from->nr_segs, sizeof(void *), GFP_KERNEL);
+
+ void __user **bufs __free(kfree) =
+ kmalloc_array(from->nr_segs, sizeof(void *), GFP_KERNEL);
if (bufs == NULL)
return -ENOMEM;
for (i = 0; i < from->nr_segs; ++i) {
@@ -4106,15 +4108,15 @@ static void snd_pcm_hw_convert_to_old_params(struct snd_pcm_hw_params_old *opara
static int snd_pcm_hw_refine_old_user(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params_old __user * _oparams)
{
- struct snd_pcm_hw_params *params __free(kfree) = NULL;
- struct snd_pcm_hw_params_old *oparams __free(kfree) = NULL;
int err;
- params = kmalloc(sizeof(*params), GFP_KERNEL);
+ struct snd_pcm_hw_params *params __free(kfree) =
+ kmalloc(sizeof(*params), GFP_KERNEL);
if (!params)
return -ENOMEM;
- oparams = memdup_user(_oparams, sizeof(*oparams));
+ struct snd_pcm_hw_params_old *oparams __free(kfree) =
+ memdup_user(_oparams, sizeof(*oparams));
if (IS_ERR(oparams))
return PTR_ERR(oparams);
snd_pcm_hw_convert_from_old_params(params, oparams);
@@ -4135,15 +4137,15 @@ static int snd_pcm_hw_refine_old_user(struct snd_pcm_substream *substream,
static int snd_pcm_hw_params_old_user(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params_old __user * _oparams)
{
- struct snd_pcm_hw_params *params __free(kfree) = NULL;
- struct snd_pcm_hw_params_old *oparams __free(kfree) = NULL;
int err;
- params = kmalloc(sizeof(*params), GFP_KERNEL);
+ struct snd_pcm_hw_params *params __free(kfree) =
+ kmalloc(sizeof(*params), GFP_KERNEL);
if (!params)
return -ENOMEM;
- oparams = memdup_user(_oparams, sizeof(*oparams));
+ struct snd_pcm_hw_params_old *oparams __free(kfree) =
+ memdup_user(_oparams, sizeof(*oparams));
if (IS_ERR(oparams))
return PTR_ERR(oparams);
diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c
index 02d30d8b6c3a..021cd70f90db 100644
--- a/sound/core/seq/oss/seq_oss.c
+++ b/sound/core/seq/oss/seq_oss.c
@@ -54,10 +54,10 @@ static __poll_t odev_poll(struct file *file, poll_table * wait);
*/
static struct snd_seq_driver seq_oss_synth_driver = {
+ .probe = snd_seq_oss_synth_probe,
+ .remove = snd_seq_oss_synth_remove,
.driver = {
.name = KBUILD_MODNAME,
- .probe = snd_seq_oss_synth_probe,
- .remove = snd_seq_oss_synth_remove,
},
.id = SNDRV_SEQ_DEV_ID_OSS,
.argsize = sizeof(struct snd_seq_oss_reg),
diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c
index 973f057eb731..e0c368bd09cb 100644
--- a/sound/core/seq/oss/seq_oss_init.c
+++ b/sound/core/seq/oss/seq_oss_init.c
@@ -63,10 +63,10 @@ int __init
snd_seq_oss_create_client(void)
{
int rc;
- struct snd_seq_port_info *port __free(kfree) = NULL;
struct snd_seq_port_callback port_callback;
+ struct snd_seq_port_info *port __free(kfree) =
+ kzalloc(sizeof(*port), GFP_KERNEL);
- port = kzalloc(sizeof(*port), GFP_KERNEL);
if (!port)
return -ENOMEM;
diff --git a/sound/core/seq/oss/seq_oss_midi.c b/sound/core/seq/oss/seq_oss_midi.c
index 023e5d0a4351..2d48c25ff4df 100644
--- a/sound/core/seq/oss/seq_oss_midi.c
+++ b/sound/core/seq/oss/seq_oss_midi.c
@@ -65,11 +65,11 @@ static int send_midi_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev,
int
snd_seq_oss_midi_lookup_ports(int client)
{
- struct snd_seq_client_info *clinfo __free(kfree) = NULL;
- struct snd_seq_port_info *pinfo __free(kfree) = NULL;
+ struct snd_seq_client_info *clinfo __free(kfree) =
+ kzalloc(sizeof(*clinfo), GFP_KERNEL);
+ struct snd_seq_port_info *pinfo __free(kfree) =
+ kzalloc(sizeof(*pinfo), GFP_KERNEL);
- clinfo = kzalloc(sizeof(*clinfo), GFP_KERNEL);
- pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL);
if (!clinfo || !pinfo)
return -ENOMEM;
clinfo->client = -1;
@@ -305,10 +305,10 @@ int
snd_seq_oss_midi_open(struct seq_oss_devinfo *dp, int dev, int fmode)
{
int perm;
- struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL;
struct snd_seq_port_subscribe subs;
+ struct seq_oss_midi *mdev __free(seq_oss_midi) =
+ get_mididev(dp, dev);
- mdev = get_mididev(dp, dev);
if (!mdev)
return -ENODEV;
@@ -364,10 +364,10 @@ snd_seq_oss_midi_open(struct seq_oss_devinfo *dp, int dev, int fmode)
int
snd_seq_oss_midi_close(struct seq_oss_devinfo *dp, int dev)
{
- struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL;
struct snd_seq_port_subscribe subs;
+ struct seq_oss_midi *mdev __free(seq_oss_midi) =
+ get_mididev(dp, dev);
- mdev = get_mididev(dp, dev);
if (!mdev)
return -ENODEV;
guard(mutex)(&mdev->open_mutex);
@@ -399,10 +399,10 @@ snd_seq_oss_midi_close(struct seq_oss_devinfo *dp, int dev)
int
snd_seq_oss_midi_filemode(struct seq_oss_devinfo *dp, int dev)
{
- struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL;
int mode;
+ struct seq_oss_midi *mdev __free(seq_oss_midi) =
+ get_mididev(dp, dev);
- mdev = get_mididev(dp, dev);
if (!mdev)
return 0;
@@ -422,9 +422,9 @@ snd_seq_oss_midi_filemode(struct seq_oss_devinfo *dp, int dev)
void
snd_seq_oss_midi_reset(struct seq_oss_devinfo *dp, int dev)
{
- struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL;
+ struct seq_oss_midi *mdev __free(seq_oss_midi) =
+ get_mididev(dp, dev);
- mdev = get_mididev(dp, dev);
if (!mdev)
return;
if (!mdev->opened)
@@ -468,9 +468,9 @@ snd_seq_oss_midi_reset(struct seq_oss_devinfo *dp, int dev)
void
snd_seq_oss_midi_get_addr(struct seq_oss_devinfo *dp, int dev, struct snd_seq_addr *addr)
{
- struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL;
+ struct seq_oss_midi *mdev __free(seq_oss_midi) =
+ get_mididev(dp, dev);
- mdev = get_mididev(dp, dev);
if (!mdev)
return;
addr->client = mdev->client;
@@ -485,11 +485,11 @@ int
snd_seq_oss_midi_input(struct snd_seq_event *ev, int direct, void *private_data)
{
struct seq_oss_devinfo *dp = (struct seq_oss_devinfo *)private_data;
- struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL;
if (dp->readq == NULL)
return 0;
- mdev = find_slot(ev->source.client, ev->source.port);
+ struct seq_oss_midi *mdev __free(seq_oss_midi) =
+ find_slot(ev->source.client, ev->source.port);
if (!mdev)
return 0;
if (!(mdev->opened & PERM_READ))
@@ -595,9 +595,9 @@ send_midi_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, struct seq
int
snd_seq_oss_midi_putc(struct seq_oss_devinfo *dp, int dev, unsigned char c, struct snd_seq_event *ev)
{
- struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL;
+ struct seq_oss_midi *mdev __free(seq_oss_midi) =
+ get_mididev(dp, dev);
- mdev = get_mididev(dp, dev);
if (!mdev)
return -ENODEV;
if (snd_midi_event_encode_byte(mdev->coder, c, ev)) {
@@ -613,9 +613,9 @@ snd_seq_oss_midi_putc(struct seq_oss_devinfo *dp, int dev, unsigned char c, stru
int
snd_seq_oss_midi_make_info(struct seq_oss_devinfo *dp, int dev, struct midi_info *inf)
{
- struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL;
+ struct seq_oss_midi *mdev __free(seq_oss_midi) =
+ get_mididev(dp, dev);
- mdev = get_mididev(dp, dev);
if (!mdev)
return -ENXIO;
inf->device = dev;
@@ -651,10 +651,9 @@ snd_seq_oss_midi_info_read(struct snd_info_buffer *buf)
snd_iprintf(buf, "\nNumber of MIDI devices: %d\n", max_midi_devs);
for (i = 0; i < max_midi_devs; i++) {
- struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL;
-
snd_iprintf(buf, "\nmidi %d: ", i);
- mdev = get_mdev(i);
+ struct seq_oss_midi *mdev __free(seq_oss_midi) =
+ get_mdev(i);
if (mdev == NULL) {
snd_iprintf(buf, "*empty*\n");
continue;
diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c
index 8c4e5913c7e6..c7f81f2053a7 100644
--- a/sound/core/seq/oss/seq_oss_synth.c
+++ b/sound/core/seq/oss/seq_oss_synth.c
@@ -80,9 +80,8 @@ snd_seq_oss_synth_init(void)
* registration of the synth device
*/
int
-snd_seq_oss_synth_probe(struct device *_dev)
+snd_seq_oss_synth_probe(struct snd_seq_device *dev)
{
- struct snd_seq_device *dev = to_seq_dev(_dev);
int i;
struct seq_oss_synth *rec;
struct snd_seq_oss_reg *reg = SNDRV_SEQ_DEVICE_ARGPTR(dev);
@@ -128,10 +127,9 @@ snd_seq_oss_synth_probe(struct device *_dev)
}
-int
-snd_seq_oss_synth_remove(struct device *_dev)
+void
+snd_seq_oss_synth_remove(struct snd_seq_device *dev)
{
- struct snd_seq_device *dev = to_seq_dev(_dev);
int index;
struct seq_oss_synth *rec = dev->driver_data;
@@ -142,7 +140,7 @@ snd_seq_oss_synth_remove(struct device *_dev)
}
if (index >= max_synth_devs) {
pr_err("ALSA: seq_oss: can't unregister synth\n");
- return -EINVAL;
+ return;
}
synth_devs[index] = NULL;
if (index == max_synth_devs - 1) {
@@ -160,8 +158,6 @@ snd_seq_oss_synth_remove(struct device *_dev)
snd_use_lock_sync(&rec->use_lock);
kfree(rec);
-
- return 0;
}
@@ -368,7 +364,6 @@ reset_channels(struct seq_oss_synthinfo *info)
void
snd_seq_oss_synth_reset(struct seq_oss_devinfo *dp, int dev)
{
- struct seq_oss_synth *rec __free(seq_oss_synth) = NULL;
struct seq_oss_synthinfo *info;
info = get_synthinfo_nospec(dp, dev);
@@ -391,7 +386,8 @@ snd_seq_oss_synth_reset(struct seq_oss_devinfo *dp, int dev)
return;
}
- rec = get_sdev(dev);
+ struct seq_oss_synth *rec __free(seq_oss_synth) =
+ get_sdev(dev);
if (rec == NULL)
return;
if (rec->oper.reset) {
@@ -415,7 +411,6 @@ int
snd_seq_oss_synth_load_patch(struct seq_oss_devinfo *dp, int dev, int fmt,
const char __user *buf, int p, int c)
{
- struct seq_oss_synth *rec __free(seq_oss_synth) = NULL;
struct seq_oss_synthinfo *info;
info = get_synthinfo_nospec(dp, dev);
@@ -424,7 +419,9 @@ snd_seq_oss_synth_load_patch(struct seq_oss_devinfo *dp, int dev, int fmt,
if (info->is_midi)
return 0;
- rec = get_synthdev(dp, dev);
+
+ struct seq_oss_synth *rec __free(seq_oss_synth) =
+ get_synthdev(dp, dev);
if (!rec)
return -ENXIO;
@@ -440,9 +437,9 @@ snd_seq_oss_synth_load_patch(struct seq_oss_devinfo *dp, int dev, int fmt,
struct seq_oss_synthinfo *
snd_seq_oss_synth_info(struct seq_oss_devinfo *dp, int dev)
{
- struct seq_oss_synth *rec __free(seq_oss_synth) = NULL;
+ struct seq_oss_synth *rec __free(seq_oss_synth) =
+ get_synthdev(dp, dev);
- rec = get_synthdev(dp, dev);
if (rec)
return get_synthinfo_nospec(dp, dev);
return NULL;
@@ -495,13 +492,14 @@ snd_seq_oss_synth_addr(struct seq_oss_devinfo *dp, int dev, struct snd_seq_event
int
snd_seq_oss_synth_ioctl(struct seq_oss_devinfo *dp, int dev, unsigned int cmd, unsigned long addr)
{
- struct seq_oss_synth *rec __free(seq_oss_synth) = NULL;
struct seq_oss_synthinfo *info;
info = get_synthinfo_nospec(dp, dev);
if (!info || info->is_midi)
return -ENXIO;
- rec = get_synthdev(dp, dev);
+
+ struct seq_oss_synth *rec __free(seq_oss_synth) =
+ get_synthdev(dp, dev);
if (!rec)
return -ENXIO;
if (rec->oper.ioctl == NULL)
@@ -575,10 +573,9 @@ snd_seq_oss_synth_info_read(struct snd_info_buffer *buf)
snd_iprintf(buf, "\nNumber of synth devices: %d\n", max_synth_devs);
for (i = 0; i < max_synth_devs; i++) {
- struct seq_oss_synth *rec __free(seq_oss_synth) = NULL;
-
snd_iprintf(buf, "\nsynth %d: ", i);
- rec = get_sdev(i);
+ struct seq_oss_synth *rec __free(seq_oss_synth) =
+ get_sdev(i);
if (rec == NULL) {
snd_iprintf(buf, "*empty*\n");
continue;
diff --git a/sound/core/seq/oss/seq_oss_synth.h b/sound/core/seq/oss/seq_oss_synth.h
index ffc40d8a7ef1..f52283904cba 100644
--- a/sound/core/seq/oss/seq_oss_synth.h
+++ b/sound/core/seq/oss/seq_oss_synth.h
@@ -15,8 +15,8 @@
#include <sound/seq_device.h>
void snd_seq_oss_synth_init(void);
-int snd_seq_oss_synth_probe(struct device *dev);
-int snd_seq_oss_synth_remove(struct device *dev);
+int snd_seq_oss_synth_probe(struct snd_seq_device *dev);
+void snd_seq_oss_synth_remove(struct snd_seq_device *dev);
void snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp);
void snd_seq_oss_synth_setup_midi(struct seq_oss_devinfo *dp);
void snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp);
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index f9a6e497f997..75a7a2af9d8c 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -494,9 +494,9 @@ static int check_port_perm(struct snd_seq_client_port *port, unsigned int flags)
*/
static struct snd_seq_client *get_event_dest_client(struct snd_seq_event *event)
{
- struct snd_seq_client *dest __free(snd_seq_client) = NULL;
+ struct snd_seq_client *dest __free(snd_seq_client) =
+ snd_seq_client_use_ptr(event->dest.client);
- dest = snd_seq_client_use_ptr(event->dest.client);
if (dest == NULL)
return NULL;
if (! dest->accept_input)
@@ -565,9 +565,9 @@ static int bounce_error_event(struct snd_seq_client *client,
static int update_timestamp_of_queue(struct snd_seq_event *event,
int queue, int real_time)
{
- struct snd_seq_queue *q __free(snd_seq_queue) = NULL;
+ struct snd_seq_queue *q __free(snd_seq_queue) =
+ queueptr(queue);
- q = queueptr(queue);
if (! q)
return 0;
event->queue = queue;
@@ -609,13 +609,13 @@ static int _snd_seq_deliver_single_event(struct snd_seq_client *client,
struct snd_seq_event *event,
int atomic, int hop)
{
- struct snd_seq_client *dest __free(snd_seq_client) = NULL;
- struct snd_seq_client_port *dest_port __free(snd_seq_port) = NULL;
-
- dest = get_event_dest_client(event);
+ struct snd_seq_client *dest __free(snd_seq_client) =
+ get_event_dest_client(event);
if (dest == NULL)
return -ENOENT;
- dest_port = snd_seq_port_use_ptr(dest, event->dest.port);
+
+ struct snd_seq_client_port *dest_port __free(snd_seq_port) =
+ snd_seq_port_use_ptr(dest, event->dest.port);
if (dest_port == NULL)
return -ENOENT;
@@ -672,7 +672,6 @@ static int __deliver_to_subscribers(struct snd_seq_client *client,
struct snd_seq_event *event,
int port, int atomic, int hop)
{
- struct snd_seq_client_port *src_port __free(snd_seq_port) = NULL;
struct snd_seq_subscribers *subs;
int err, result = 0, num_ev = 0;
union __snd_seq_event event_saved;
@@ -681,7 +680,9 @@ static int __deliver_to_subscribers(struct snd_seq_client *client,
if (port < 0)
return 0;
- src_port = snd_seq_port_use_ptr(client, port);
+
+ struct snd_seq_client_port *src_port __free(snd_seq_port) =
+ snd_seq_port_use_ptr(client, port);
if (!src_port)
return 0;
@@ -801,13 +802,13 @@ static int snd_seq_deliver_event(struct snd_seq_client *client, struct snd_seq_e
*/
int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop)
{
- struct snd_seq_client *client __free(snd_seq_client) = NULL;
int result;
if (snd_BUG_ON(!cell))
return -EINVAL;
- client = snd_seq_client_use_ptr(cell->event.source.client);
+ struct snd_seq_client *client __free(snd_seq_client) =
+ snd_seq_client_use_ptr(cell->event.source.client);
if (client == NULL) {
snd_seq_cell_free(cell); /* release this cell */
return -EINVAL;
@@ -1154,10 +1155,10 @@ static int snd_seq_ioctl_system_info(struct snd_seq_client *client, void *arg)
static int snd_seq_ioctl_running_mode(struct snd_seq_client *client, void *arg)
{
struct snd_seq_running_info *info = arg;
- struct snd_seq_client *cptr __free(snd_seq_client) = NULL;
-
/* requested client number */
- cptr = client_load_and_use_ptr(info->client);
+ struct snd_seq_client *cptr __free(snd_seq_client) =
+ client_load_and_use_ptr(info->client);
+
if (cptr == NULL)
return -ENOENT; /* don't change !!! */
@@ -1207,10 +1208,10 @@ static int snd_seq_ioctl_get_client_info(struct snd_seq_client *client,
void *arg)
{
struct snd_seq_client_info *client_info = arg;
- struct snd_seq_client *cptr __free(snd_seq_client) = NULL;
-
/* requested client number */
- cptr = client_load_and_use_ptr(client_info->client);
+ struct snd_seq_client *cptr __free(snd_seq_client) =
+ client_load_and_use_ptr(client_info->client);
+
if (cptr == NULL)
return -ENOENT; /* don't change !!! */
@@ -1344,14 +1345,14 @@ static int snd_seq_ioctl_delete_port(struct snd_seq_client *client, void *arg)
static int snd_seq_ioctl_get_port_info(struct snd_seq_client *client, void *arg)
{
struct snd_seq_port_info *info = arg;
- struct snd_seq_client *cptr __free(snd_seq_client) = NULL;
- struct snd_seq_client_port *port __free(snd_seq_port) = NULL;
- cptr = client_load_and_use_ptr(info->addr.client);
+ struct snd_seq_client *cptr __free(snd_seq_client) =
+ client_load_and_use_ptr(info->addr.client);
if (cptr == NULL)
return -ENXIO;
- port = snd_seq_port_use_ptr(cptr, info->addr.port);
+ struct snd_seq_client_port *port __free(snd_seq_port) =
+ snd_seq_port_use_ptr(cptr, info->addr.port);
if (port == NULL)
return -ENOENT; /* don't change */
@@ -1367,11 +1368,12 @@ static int snd_seq_ioctl_get_port_info(struct snd_seq_client *client, void *arg)
static int snd_seq_ioctl_set_port_info(struct snd_seq_client *client, void *arg)
{
struct snd_seq_port_info *info = arg;
- struct snd_seq_client_port *port __free(snd_seq_port) = NULL;
if (info->addr.client != client->number) /* only set our own ports ! */
return -EPERM;
- port = snd_seq_port_use_ptr(client, info->addr.port);
+
+ struct snd_seq_client_port *port __free(snd_seq_port) =
+ snd_seq_port_use_ptr(client, info->addr.port);
if (port) {
snd_seq_set_port_info(port, info);
/* notify the change */
@@ -1444,22 +1446,22 @@ static int snd_seq_ioctl_subscribe_port(struct snd_seq_client *client,
void *arg)
{
struct snd_seq_port_subscribe *subs = arg;
- struct snd_seq_client *receiver __free(snd_seq_client) = NULL;
- struct snd_seq_client *sender __free(snd_seq_client) = NULL;
- struct snd_seq_client_port *sport __free(snd_seq_port) = NULL;
- struct snd_seq_client_port *dport __free(snd_seq_port) = NULL;
int result;
- receiver = client_load_and_use_ptr(subs->dest.client);
+ struct snd_seq_client *receiver __free(snd_seq_client) =
+ client_load_and_use_ptr(subs->dest.client);
if (!receiver)
return -EINVAL;
- sender = client_load_and_use_ptr(subs->sender.client);
+ struct snd_seq_client *sender __free(snd_seq_client) =
+ client_load_and_use_ptr(subs->sender.client);
if (!sender)
return -EINVAL;
- sport = snd_seq_port_use_ptr(sender, subs->sender.port);
+ struct snd_seq_client_port *sport __free(snd_seq_port) =
+ snd_seq_port_use_ptr(sender, subs->sender.port);
if (!sport)
return -EINVAL;
- dport = snd_seq_port_use_ptr(receiver, subs->dest.port);
+ struct snd_seq_client_port *dport __free(snd_seq_port) =
+ snd_seq_port_use_ptr(receiver, subs->dest.port);
if (!dport)
return -EINVAL;
@@ -1483,22 +1485,22 @@ static int snd_seq_ioctl_unsubscribe_port(struct snd_seq_client *client,
void *arg)
{
struct snd_seq_port_subscribe *subs = arg;
- struct snd_seq_client *receiver __free(snd_seq_client) = NULL;
- struct snd_seq_client *sender __free(snd_seq_client) = NULL;
- struct snd_seq_client_port *sport __free(snd_seq_port) = NULL;
- struct snd_seq_client_port *dport __free(snd_seq_port) = NULL;
int result;
- receiver = snd_seq_client_use_ptr(subs->dest.client);
+ struct snd_seq_client *receiver __free(snd_seq_client) =
+ snd_seq_client_use_ptr(subs->dest.client);
if (!receiver)
return -ENXIO;
- sender = snd_seq_client_use_ptr(subs->sender.client);
+ struct snd_seq_client *sender __free(snd_seq_client) =
+ snd_seq_client_use_ptr(subs->sender.client);
if (!sender)
return -ENXIO;
- sport = snd_seq_port_use_ptr(sender, subs->sender.port);
+ struct snd_seq_client_port *sport __free(snd_seq_port) =
+ snd_seq_port_use_ptr(sender, subs->sender.port);
if (!sport)
return -ENXIO;
- dport = snd_seq_port_use_ptr(receiver, subs->dest.port);
+ struct snd_seq_client_port *dport __free(snd_seq_port) =
+ snd_seq_port_use_ptr(receiver, subs->dest.port);
if (!dport)
return -ENXIO;
@@ -1518,9 +1520,9 @@ static int snd_seq_ioctl_unsubscribe_port(struct snd_seq_client *client,
static int snd_seq_ioctl_create_queue(struct snd_seq_client *client, void *arg)
{
struct snd_seq_queue_info *info = arg;
- struct snd_seq_queue *q __free(snd_seq_queue) = NULL;
+ struct snd_seq_queue *q __free(snd_seq_queue) =
+ snd_seq_queue_alloc(client->number, info->locked, info->flags);
- q = snd_seq_queue_alloc(client->number, info->locked, info->flags);
if (IS_ERR(q))
return PTR_ERR(q);
@@ -1549,9 +1551,9 @@ static int snd_seq_ioctl_get_queue_info(struct snd_seq_client *client,
void *arg)
{
struct snd_seq_queue_info *info = arg;
- struct snd_seq_queue *q __free(snd_seq_queue) = NULL;
+ struct snd_seq_queue *q __free(snd_seq_queue) =
+ queueptr(info->queue);
- q = queueptr(info->queue);
if (q == NULL)
return -EINVAL;
@@ -1569,7 +1571,6 @@ static int snd_seq_ioctl_set_queue_info(struct snd_seq_client *client,
void *arg)
{
struct snd_seq_queue_info *info = arg;
- struct snd_seq_queue *q __free(snd_seq_queue) = NULL;
if (info->owner != client->number)
return -EINVAL;
@@ -1584,7 +1585,8 @@ static int snd_seq_ioctl_set_queue_info(struct snd_seq_client *client,
return -EPERM;
}
- q = queueptr(info->queue);
+ struct snd_seq_queue *q __free(snd_seq_queue) =
+ queueptr(info->queue);
if (! q)
return -EINVAL;
if (q->owner != client->number)
@@ -1599,9 +1601,9 @@ static int snd_seq_ioctl_get_named_queue(struct snd_seq_client *client,
void *arg)
{
struct snd_seq_queue_info *info = arg;
- struct snd_seq_queue *q __free(snd_seq_queue) = NULL;
+ struct snd_seq_queue *q __free(snd_seq_queue) =
+ snd_seq_queue_find_name(info->name);
- q = snd_seq_queue_find_name(info->name);
if (q == NULL)
return -EINVAL;
info->queue = q->queue;
@@ -1616,10 +1618,10 @@ static int snd_seq_ioctl_get_queue_status(struct snd_seq_client *client,
void *arg)
{
struct snd_seq_queue_status *status = arg;
- struct snd_seq_queue *queue __free(snd_seq_queue) = NULL;
struct snd_seq_timer *tmr;
+ struct snd_seq_queue *queue __free(snd_seq_queue) =
+ queueptr(status->queue);
- queue = queueptr(status->queue);
if (queue == NULL)
return -EINVAL;
memset(status, 0, sizeof(*status));
@@ -1644,10 +1646,10 @@ static int snd_seq_ioctl_get_queue_tempo(struct snd_seq_client *client,
void *arg)
{
struct snd_seq_queue_tempo *tempo = arg;
- struct snd_seq_queue *queue __free(snd_seq_queue) = NULL;
struct snd_seq_timer *tmr;
+ struct snd_seq_queue *queue __free(snd_seq_queue) =
+ queueptr(tempo->queue);
- queue = queueptr(tempo->queue);
if (queue == NULL)
return -EINVAL;
memset(tempo, 0, sizeof(*tempo));
@@ -1693,10 +1695,10 @@ static int snd_seq_ioctl_get_queue_timer(struct snd_seq_client *client,
void *arg)
{
struct snd_seq_queue_timer *timer = arg;
- struct snd_seq_queue *queue __free(snd_seq_queue) = NULL;
struct snd_seq_timer *tmr;
+ struct snd_seq_queue *queue __free(snd_seq_queue) =
+ queueptr(timer->queue);
- queue = queueptr(timer->queue);
if (queue == NULL)
return -EINVAL;
@@ -1726,10 +1728,10 @@ static int snd_seq_ioctl_set_queue_timer(struct snd_seq_client *client,
return -EINVAL;
if (snd_seq_queue_check_access(timer->queue, client->number)) {
- struct snd_seq_queue *q __free(snd_seq_queue) = NULL;
struct snd_seq_timer *tmr;
+ struct snd_seq_queue *q __free(snd_seq_queue) =
+ queueptr(timer->queue);
- q = queueptr(timer->queue);
if (q == NULL)
return -ENXIO;
guard(mutex)(&q->timer_mutex);
@@ -1788,9 +1790,9 @@ static int snd_seq_ioctl_get_client_pool(struct snd_seq_client *client,
void *arg)
{
struct snd_seq_client_pool *info = arg;
- struct snd_seq_client *cptr __free(snd_seq_client) = NULL;
+ struct snd_seq_client *cptr __free(snd_seq_client) =
+ client_load_and_use_ptr(info->client);
- cptr = client_load_and_use_ptr(info->client);
if (cptr == NULL)
return -ENOENT;
memset(info, 0, sizeof(*info));
@@ -1888,13 +1890,13 @@ static int snd_seq_ioctl_get_subscription(struct snd_seq_client *client,
void *arg)
{
struct snd_seq_port_subscribe *subs = arg;
- struct snd_seq_client *sender __free(snd_seq_client) = NULL;
- struct snd_seq_client_port *sport __free(snd_seq_port) = NULL;
- sender = client_load_and_use_ptr(subs->sender.client);
+ struct snd_seq_client *sender __free(snd_seq_client) =
+ client_load_and_use_ptr(subs->sender.client);
if (!sender)
return -EINVAL;
- sport = snd_seq_port_use_ptr(sender, subs->sender.port);
+ struct snd_seq_client_port *sport __free(snd_seq_port) =
+ snd_seq_port_use_ptr(sender, subs->sender.port);
if (!sport)
return -EINVAL;
return snd_seq_port_get_subscription(&sport->c_src, &subs->dest, subs);
@@ -1907,16 +1909,16 @@ static int snd_seq_ioctl_get_subscription(struct snd_seq_client *client,
static int snd_seq_ioctl_query_subs(struct snd_seq_client *client, void *arg)
{
struct snd_seq_query_subs *subs = arg;
- struct snd_seq_client *cptr __free(snd_seq_client) = NULL;
- struct snd_seq_client_port *port __free(snd_seq_port) = NULL;
struct snd_seq_port_subs_info *group;
struct list_head *p;
int i;
- cptr = client_load_and_use_ptr(subs->root.client);
+ struct snd_seq_client *cptr __free(snd_seq_client) =
+ client_load_and_use_ptr(subs->root.client);
if (!cptr)
return -ENXIO;
- port = snd_seq_port_use_ptr(cptr, subs->root.port);
+ struct snd_seq_client_port *port __free(snd_seq_port) =
+ snd_seq_port_use_ptr(cptr, subs->root.port);
if (!port)
return -ENXIO;
@@ -1963,7 +1965,6 @@ static int snd_seq_ioctl_query_next_client(struct snd_seq_client *client,
void *arg)
{
struct snd_seq_client_info *info = arg;
- struct snd_seq_client *cptr __free(snd_seq_client) = NULL;
/* search for next client */
if (info->client < INT_MAX)
@@ -1971,7 +1972,8 @@ static int snd_seq_ioctl_query_next_client(struct snd_seq_client *client,
if (info->client < 0)
info->client = 0;
for (; info->client < SNDRV_SEQ_MAX_CLIENTS; info->client++) {
- cptr = client_load_and_use_ptr(info->client);
+ struct snd_seq_client *cptr __free(snd_seq_client) =
+ client_load_and_use_ptr(info->client);
if (cptr) {
get_client_info(cptr, info);
return 0; /* found */
@@ -1987,16 +1989,16 @@ static int snd_seq_ioctl_query_next_port(struct snd_seq_client *client,
void *arg)
{
struct snd_seq_port_info *info = arg;
- struct snd_seq_client *cptr __free(snd_seq_client) = NULL;
- struct snd_seq_client_port *port __free(snd_seq_port) = NULL;
- cptr = client_load_and_use_ptr(info->addr.client);
+ struct snd_seq_client *cptr __free(snd_seq_client) =
+ client_load_and_use_ptr(info->addr.client);
if (cptr == NULL)
return -ENXIO;
/* search for next port */
info->addr.port++;
- port = snd_seq_port_query_nearest(cptr, info);
+ struct snd_seq_client_port *port __free(snd_seq_port) =
+ snd_seq_port_query_nearest(cptr, info);
if (port == NULL)
return -ENOENT;
@@ -2067,7 +2069,6 @@ static int snd_seq_ioctl_client_ump_info(struct snd_seq_client *caller,
{
struct snd_seq_client_ump_info __user *argp =
(struct snd_seq_client_ump_info __user *)arg;
- struct snd_seq_client *cptr __free(snd_seq_client) = NULL;
int client, type, err = 0;
size_t size;
void *p;
@@ -2083,7 +2084,9 @@ static int snd_seq_ioctl_client_ump_info(struct snd_seq_client *caller,
size = sizeof(struct snd_ump_endpoint_info);
else
size = sizeof(struct snd_ump_block_info);
- cptr = client_load_and_use_ptr(client);
+
+ struct snd_seq_client *cptr __free(snd_seq_client) =
+ client_load_and_use_ptr(client);
if (!cptr)
return -ENOENT;
@@ -2342,8 +2345,6 @@ EXPORT_SYMBOL(snd_seq_delete_kernel_client);
int snd_seq_kernel_client_enqueue(int client, struct snd_seq_event *ev,
struct file *file, bool blocking)
{
- struct snd_seq_client *cptr __free(snd_seq_client) = NULL;
-
if (snd_BUG_ON(!ev))
return -EINVAL;
@@ -2360,7 +2361,8 @@ int snd_seq_kernel_client_enqueue(int client, struct snd_seq_event *ev,
if (check_event_type_and_length(ev))
return -EINVAL;
- cptr = client_load_and_use_ptr(client);
+ struct snd_seq_client *cptr __free(snd_seq_client) =
+ client_load_and_use_ptr(client);
if (cptr == NULL)
return -EINVAL;
@@ -2385,8 +2387,6 @@ EXPORT_SYMBOL(snd_seq_kernel_client_enqueue);
int snd_seq_kernel_client_dispatch(int client, struct snd_seq_event * ev,
int atomic, int hop)
{
- struct snd_seq_client *cptr __free(snd_seq_client) = NULL;
-
if (snd_BUG_ON(!ev))
return -EINVAL;
@@ -2397,7 +2397,8 @@ int snd_seq_kernel_client_dispatch(int client, struct snd_seq_event * ev,
if (check_event_type_and_length(ev))
return -EINVAL;
- cptr = snd_seq_client_use_ptr(client);
+ struct snd_seq_client *cptr __free(snd_seq_client) =
+ snd_seq_client_use_ptr(client);
if (cptr == NULL)
return -EINVAL;
@@ -2450,9 +2451,9 @@ EXPORT_SYMBOL(snd_seq_kernel_client_ctl);
/* a similar like above but taking locks; used only from OSS sequencer layer */
int snd_seq_kernel_client_ioctl(int clientid, unsigned int cmd, void *arg)
{
- struct snd_seq_client *client __free(snd_seq_client) = NULL;
+ struct snd_seq_client *client __free(snd_seq_client) =
+ client_load_and_use_ptr(clientid);
- client = client_load_and_use_ptr(clientid);
if (!client)
return -ENXIO;
guard(mutex)(&client->ioctl_mutex);
@@ -2597,9 +2598,9 @@ void snd_seq_info_clients_read(struct snd_info_entry *entry,
/* list the client table */
for (c = 0; c < SNDRV_SEQ_MAX_CLIENTS; c++) {
- struct snd_seq_client *client __free(snd_seq_client) = NULL;
+ struct snd_seq_client *client __free(snd_seq_client) =
+ client_load_and_use_ptr(c);
- client = client_load_and_use_ptr(c);
if (client == NULL)
continue;
if (client->type == NO_CLIENT)
diff --git a/sound/core/seq/seq_compat.c b/sound/core/seq/seq_compat.c
index 643af4c1e838..260428747e33 100644
--- a/sound/core/seq/seq_compat.c
+++ b/sound/core/seq/seq_compat.c
@@ -31,10 +31,10 @@ struct snd_seq_port_info32 {
static int snd_seq_call_port_info_ioctl(struct snd_seq_client *client, unsigned int cmd,
struct snd_seq_port_info32 __user *data32)
{
- struct snd_seq_port_info *data __free(kfree) = NULL;
int err;
+ struct snd_seq_port_info *data __free(kfree) =
+ kmalloc(sizeof(*data), GFP_KERNEL);
- data = kmalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c
index 581e138a3115..88e930980f16 100644
--- a/sound/core/seq/seq_midi.c
+++ b/sound/core/seq/seq_midi.c
@@ -265,13 +265,10 @@ static void snd_seq_midisynth_delete(struct seq_midisynth *msynth)
/* register new midi synth port */
static int
-snd_seq_midisynth_probe(struct device *_dev)
+snd_seq_midisynth_probe(struct snd_seq_device *dev)
{
- struct snd_seq_device *dev = to_seq_dev(_dev);
struct seq_midisynth_client *client;
struct seq_midisynth *msynth, *ms;
- struct snd_seq_port_info *port __free(kfree) = NULL;
- struct snd_rawmidi_info *info __free(kfree) = NULL;
struct snd_rawmidi *rmidi = dev->private_data;
int newclient = 0;
unsigned int p, ports;
@@ -282,7 +279,9 @@ snd_seq_midisynth_probe(struct device *_dev)
if (snd_BUG_ON(!card || device < 0 || device >= SNDRV_RAWMIDI_DEVICES))
return -EINVAL;
- info = kmalloc(sizeof(*info), GFP_KERNEL);
+
+ struct snd_rawmidi_info *info __free(kfree) =
+ kmalloc(sizeof(*info), GFP_KERNEL);
if (! info)
return -ENOMEM;
info->device = device;
@@ -320,7 +319,9 @@ snd_seq_midisynth_probe(struct device *_dev)
}
msynth = kcalloc(ports, sizeof(struct seq_midisynth), GFP_KERNEL);
- port = kmalloc(sizeof(*port), GFP_KERNEL);
+
+ struct snd_seq_port_info *port __free(kfree) =
+ kmalloc(sizeof(*port), GFP_KERNEL);
if (msynth == NULL || port == NULL)
goto __nomem;
@@ -411,10 +412,9 @@ snd_seq_midisynth_probe(struct device *_dev)
}
/* release midi synth port */
-static int
-snd_seq_midisynth_remove(struct device *_dev)
+static void
+snd_seq_midisynth_remove(struct snd_seq_device *dev)
{
- struct snd_seq_device *dev = to_seq_dev(_dev);
struct seq_midisynth_client *client;
struct seq_midisynth *msynth;
struct snd_card *card = dev->card;
@@ -423,7 +423,7 @@ snd_seq_midisynth_remove(struct device *_dev)
guard(mutex)(&register_mutex);
client = synths[card->number];
if (client == NULL || client->ports[device] == NULL)
- return -ENODEV;
+ return;
ports = client->ports_per_device[device];
client->ports_per_device[device] = 0;
msynth = client->ports[device];
@@ -437,14 +437,13 @@ snd_seq_midisynth_remove(struct device *_dev)
synths[card->number] = NULL;
kfree(client);
}
- return 0;
}
static struct snd_seq_driver seq_midisynth_driver = {
+ .probe = snd_seq_midisynth_probe,
+ .remove = snd_seq_midisynth_remove,
.driver = {
.name = KBUILD_MODNAME,
- .probe = snd_seq_midisynth_probe,
- .remove = snd_seq_midisynth_remove,
},
.id = SNDRV_SEQ_DEV_ID_MIDISYNTH,
.argsize = 0,
diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c
index 40fa379847e5..bbec34bba4f9 100644
--- a/sound/core/seq/seq_ports.c
+++ b/sound/core/seq/seq_ports.c
@@ -211,14 +211,13 @@ static void clear_subscriber_list(struct snd_seq_client *client,
list_for_each_safe(p, n, &grp->list_head) {
struct snd_seq_subscribers *subs;
- struct snd_seq_client *c __free(snd_seq_client) = NULL;
- struct snd_seq_client_port *aport __free(snd_seq_port) = NULL;
subs = get_subscriber(p, is_src);
- if (is_src)
- aport = get_client_port(&subs->info.dest, &c);
- else
- aport = get_client_port(&subs->info.sender, &c);
+ struct snd_seq_client *c __free(snd_seq_client) = NULL;
+ struct snd_seq_client_port *aport __free(snd_seq_port) =
+ is_src ?
+ get_client_port(&subs->info.dest, &c) :
+ get_client_port(&subs->info.sender, &c);
delete_and_unsubscribe_port(client, port, subs, is_src, false);
if (!aport) {
diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c
index f5c0e401c8ae..c0c5e1424c5a 100644
--- a/sound/core/seq/seq_queue.c
+++ b/sound/core/seq/seq_queue.c
@@ -211,8 +211,9 @@ struct snd_seq_queue *snd_seq_queue_find_name(char *name)
int i;
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
- struct snd_seq_queue *q __free(snd_seq_queue) = NULL;
- q = queueptr(i);
+ struct snd_seq_queue *q __free(snd_seq_queue) =
+ queueptr(i);
+
if (q) {
if (strncmp(q->name, name, sizeof(q->name)) == 0)
return no_free_ptr(q);
@@ -285,12 +286,13 @@ void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop)
int snd_seq_enqueue_event(struct snd_seq_event_cell *cell, int atomic, int hop)
{
int dest, err;
- struct snd_seq_queue *q __free(snd_seq_queue) = NULL;
if (snd_BUG_ON(!cell))
return -EINVAL;
dest = cell->event.queue; /* destination queue */
- q = queueptr(dest);
+
+ struct snd_seq_queue *q __free(snd_seq_queue) =
+ queueptr(dest);
if (q == NULL)
return -EINVAL;
/* handle relative time stamps, convert them into absolute */
@@ -403,10 +405,10 @@ int snd_seq_queue_set_owner(int queueid, int client, int locked)
int snd_seq_queue_timer_open(int queueid)
{
int result = 0;
- struct snd_seq_queue *queue __free(snd_seq_queue) = NULL;
struct snd_seq_timer *tmr;
+ struct snd_seq_queue *queue __free(snd_seq_queue) =
+ queueptr(queueid);
- queue = queueptr(queueid);
if (queue == NULL)
return -EINVAL;
tmr = queue->timer;
@@ -423,10 +425,10 @@ int snd_seq_queue_timer_open(int queueid)
*/
int snd_seq_queue_timer_close(int queueid)
{
- struct snd_seq_queue *queue __free(snd_seq_queue) = NULL;
int result = 0;
+ struct snd_seq_queue *queue __free(snd_seq_queue) =
+ queueptr(queueid);
- queue = queueptr(queueid);
if (queue == NULL)
return -EINVAL;
snd_seq_timer_close(queue);
@@ -479,9 +481,9 @@ static void queue_use(struct snd_seq_queue *queue, int client, int use)
*/
int snd_seq_queue_use(int queueid, int client, int use)
{
- struct snd_seq_queue *queue __free(snd_seq_queue) = NULL;
+ struct snd_seq_queue *queue __free(snd_seq_queue) =
+ queueptr(queueid);
- queue = queueptr(queueid);
if (queue == NULL)
return -EINVAL;
guard(mutex)(&queue->timer_mutex);
@@ -496,9 +498,9 @@ int snd_seq_queue_use(int queueid, int client, int use)
*/
int snd_seq_queue_is_used(int queueid, int client)
{
- struct snd_seq_queue *q __free(snd_seq_queue) = NULL;
+ struct snd_seq_queue *q __free(snd_seq_queue) =
+ queueptr(queueid);
- q = queueptr(queueid);
if (q == NULL)
return -EINVAL; /* invalid queue */
return test_bit(client, q->clients_bitmap) ? 1 : 0;
@@ -642,11 +644,11 @@ static void snd_seq_queue_process_event(struct snd_seq_queue *q,
*/
int snd_seq_control_queue(struct snd_seq_event *ev, int atomic, int hop)
{
- struct snd_seq_queue *q __free(snd_seq_queue) = NULL;
-
if (snd_BUG_ON(!ev))
return -EINVAL;
- q = queueptr(ev->data.queue.queue);
+
+ struct snd_seq_queue *q __free(snd_seq_queue) =
+ queueptr(ev->data.queue.queue);
if (q == NULL)
return -EINVAL;
diff --git a/sound/core/seq/seq_ump_client.c b/sound/core/seq/seq_ump_client.c
index 27247babb16d..7bc18415a540 100644
--- a/sound/core/seq/seq_ump_client.c
+++ b/sound/core/seq/seq_ump_client.c
@@ -214,13 +214,13 @@ static bool skip_group(struct seq_ump_client *client, struct snd_ump_group *grou
static int seq_ump_group_init(struct seq_ump_client *client, int group_index)
{
struct snd_ump_group *group = &client->ump->groups[group_index];
- struct snd_seq_port_info *port __free(kfree) = NULL;
struct snd_seq_port_callback pcallbacks;
if (skip_group(client, group))
return 0;
- port = kzalloc(sizeof(*port), GFP_KERNEL);
+ struct snd_seq_port_info *port __free(kfree) =
+ kzalloc(sizeof(*port), GFP_KERNEL);
if (!port)
return -ENOMEM;
@@ -243,12 +243,12 @@ static int seq_ump_group_init(struct seq_ump_client *client, int group_index)
/* update the sequencer ports; called from notify_fb_change callback */
static void update_port_infos(struct seq_ump_client *client)
{
- struct snd_seq_port_info *old __free(kfree) = NULL;
- struct snd_seq_port_info *new __free(kfree) = NULL;
int i, err;
- old = kzalloc(sizeof(*old), GFP_KERNEL);
- new = kzalloc(sizeof(*new), GFP_KERNEL);
+ struct snd_seq_port_info *old __free(kfree) =
+ kzalloc(sizeof(*old), GFP_KERNEL);
+ struct snd_seq_port_info *new __free(kfree) =
+ kzalloc(sizeof(*new), GFP_KERNEL);
if (!old || !new)
return;
@@ -278,12 +278,12 @@ static void update_port_infos(struct seq_ump_client *client)
/* create a UMP Endpoint port */
static int create_ump_endpoint_port(struct seq_ump_client *client)
{
- struct snd_seq_port_info *port __free(kfree) = NULL;
struct snd_seq_port_callback pcallbacks;
unsigned int rawmidi_info = client->ump->core.info_flags;
int err;
- port = kzalloc(sizeof(*port), GFP_KERNEL);
+ struct snd_seq_port_info *port __free(kfree) =
+ kzalloc(sizeof(*port), GFP_KERNEL);
if (!port)
return -ENOMEM;
@@ -452,9 +452,8 @@ static const struct snd_seq_ump_ops seq_ump_ops = {
};
/* create a sequencer client and ports for the given UMP endpoint */
-static int snd_seq_ump_probe(struct device *_dev)
+static int snd_seq_ump_probe(struct snd_seq_device *dev)
{
- struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_ump_endpoint *ump = dev->private_data;
struct snd_card *card = dev->card;
struct seq_ump_client *client;
@@ -513,21 +512,19 @@ static int snd_seq_ump_probe(struct device *_dev)
}
/* remove a sequencer client */
-static int snd_seq_ump_remove(struct device *_dev)
+static void snd_seq_ump_remove(struct snd_seq_device *dev)
{
- struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_ump_endpoint *ump = dev->private_data;
if (ump->seq_client)
seq_ump_client_free(ump->seq_client);
- return 0;
}
static struct snd_seq_driver seq_ump_driver = {
+ .probe = snd_seq_ump_probe,
+ .remove = snd_seq_ump_remove,
.driver = {
.name = KBUILD_MODNAME,
- .probe = snd_seq_ump_probe,
- .remove = snd_seq_ump_remove,
},
.id = SNDRV_SEQ_DEV_ID_UMP,
.argsize = 0,
diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c
index 9e7fd4993a10..574493fbd50d 100644
--- a/sound/core/seq/seq_virmidi.c
+++ b/sound/core/seq/seq_virmidi.c
@@ -361,13 +361,13 @@ static int snd_virmidi_dev_attach_seq(struct snd_virmidi_dev *rdev)
{
int client;
struct snd_seq_port_callback pcallbacks;
- struct snd_seq_port_info *pinfo __free(kfree) = NULL;
int err;
if (rdev->client >= 0)
return 0;
- pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL);
+ struct snd_seq_port_info *pinfo __free(kfree) =
+ kzalloc(sizeof(*pinfo), GFP_KERNEL);
if (!pinfo)
return -ENOMEM;
diff --git a/sound/core/seq_device.c b/sound/core/seq_device.c
index bac9f8603734..1b062d6b17ea 100644
--- a/sound/core/seq_device.c
+++ b/sound/core/seq_device.c
@@ -49,9 +49,31 @@ static int snd_seq_bus_match(struct device *dev, const struct device_driver *drv
sdrv->argsize == sdev->argsize;
}
+static int snd_seq_bus_probe(struct device *dev)
+{
+ struct snd_seq_device *sdev = to_seq_dev(dev);
+ const struct snd_seq_driver *sdrv = to_seq_drv(dev->driver);
+
+ if (sdrv->probe)
+ return sdrv->probe(sdev);
+ else
+ return 0;
+}
+
+static void snd_seq_bus_remove(struct device *dev)
+{
+ struct snd_seq_device *sdev = to_seq_dev(dev);
+ const struct snd_seq_driver *sdrv = to_seq_drv(dev->driver);
+
+ if (sdrv->remove)
+ sdrv->remove(sdev);
+}
+
static const struct bus_type snd_seq_bus_type = {
.name = "snd_seq",
.match = snd_seq_bus_match,
+ .probe = snd_seq_bus_probe,
+ .remove = snd_seq_bus_remove,
};
/*
@@ -247,10 +269,12 @@ EXPORT_SYMBOL(snd_seq_device_new);
*/
int __snd_seq_driver_register(struct snd_seq_driver *drv, struct module *mod)
{
- if (WARN_ON(!drv->driver.name || !drv->id))
+ if (WARN_ON(!drv->driver.name || !drv->id || drv->driver.probe || drv->driver.remove))
return -EINVAL;
+
drv->driver.bus = &snd_seq_bus_type;
drv->driver.owner = mod;
+
return driver_register(&drv->driver);
}
EXPORT_SYMBOL_GPL(__snd_seq_driver_register);
diff --git a/sound/core/timer.c b/sound/core/timer.c
index d9fff5c87613..9a4a1748ff80 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -1614,12 +1614,12 @@ static int snd_timer_user_next_device(struct snd_timer_id __user *_tid)
static int snd_timer_user_ginfo(struct file *file,
struct snd_timer_ginfo __user *_ginfo)
{
- struct snd_timer_ginfo *ginfo __free(kfree) = NULL;
struct snd_timer_id tid;
struct snd_timer *t;
struct list_head *p;
+ struct snd_timer_ginfo *ginfo __free(kfree) =
+ memdup_user(_ginfo, sizeof(*ginfo));
- ginfo = memdup_user(_ginfo, sizeof(*ginfo));
if (IS_ERR(ginfo))
return PTR_ERR(ginfo);
@@ -1756,7 +1756,6 @@ static int snd_timer_user_info(struct file *file,
struct snd_timer_info __user *_info)
{
struct snd_timer_user *tu;
- struct snd_timer_info *info __free(kfree) = NULL;
struct snd_timer *t;
tu = file->private_data;
@@ -1766,7 +1765,8 @@ static int snd_timer_user_info(struct file *file,
if (!t)
return -EBADFD;
- info = kzalloc(sizeof(*info), GFP_KERNEL);
+ struct snd_timer_info *info __free(kfree) =
+ kzalloc(sizeof(*info), GFP_KERNEL);
if (! info)
return -ENOMEM;
info->card = t->card ? t->card->number : -1;
@@ -2192,10 +2192,10 @@ static int snd_utimer_ioctl_create(struct file *file,
struct snd_timer_uinfo __user *_utimer_info)
{
struct snd_utimer *utimer;
- struct snd_timer_uinfo *utimer_info __free(kfree) = NULL;
int err, timer_fd;
+ struct snd_timer_uinfo *utimer_info __free(kfree) =
+ memdup_user(_utimer_info, sizeof(*utimer_info));
- utimer_info = memdup_user(_utimer_info, sizeof(*utimer_info));
if (IS_ERR(utimer_info))
return PTR_ERR(utimer_info);
diff --git a/sound/core/vmaster.c b/sound/core/vmaster.c
index c657659b236c..76cc64245f5d 100644
--- a/sound/core/vmaster.c
+++ b/sound/core/vmaster.c
@@ -56,10 +56,10 @@ struct link_follower {
static int follower_update(struct link_follower *follower)
{
- struct snd_ctl_elem_value *uctl __free(kfree) = NULL;
int err, ch;
+ struct snd_ctl_elem_value *uctl __free(kfree) =
+ kzalloc(sizeof(*uctl), GFP_KERNEL);
- uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
if (!uctl)
return -ENOMEM;
uctl->id = follower->follower.id;
@@ -74,7 +74,6 @@ static int follower_update(struct link_follower *follower)
/* get the follower ctl info and save the initial values */
static int follower_init(struct link_follower *follower)
{
- struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;
int err;
if (follower->info.count) {
@@ -84,7 +83,8 @@ static int follower_init(struct link_follower *follower)
return 0;
}
- uinfo = kmalloc(sizeof(*uinfo), GFP_KERNEL);
+ struct snd_ctl_elem_info *uinfo __free(kfree) =
+ kmalloc(sizeof(*uinfo), GFP_KERNEL);
if (!uinfo)
return -ENOMEM;
uinfo->id = follower->follower.id;
@@ -341,9 +341,9 @@ static int master_get(struct snd_kcontrol *kcontrol,
static int sync_followers(struct link_master *master, int old_val, int new_val)
{
struct link_follower *follower;
- struct snd_ctl_elem_value *uval __free(kfree) = NULL;
+ struct snd_ctl_elem_value *uval __free(kfree) =
+ kmalloc(sizeof(*uval), GFP_KERNEL);
- uval = kmalloc(sizeof(*uval), GFP_KERNEL);
if (!uval)
return -ENOMEM;
list_for_each_entry(follower, &master->followers, list) {
diff --git a/sound/drivers/opl3/opl3_seq.c b/sound/drivers/opl3/opl3_seq.c
index d3278428d360..b77d8e8f12fb 100644
--- a/sound/drivers/opl3/opl3_seq.c
+++ b/sound/drivers/opl3/opl3_seq.c
@@ -201,9 +201,8 @@ static int snd_opl3_synth_create_port(struct snd_opl3 * opl3)
/* ------------------------------ */
-static int snd_opl3_seq_probe(struct device *_dev)
+static int snd_opl3_seq_probe(struct snd_seq_device *dev)
{
- struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_opl3 *opl3;
int client, err;
char name[32];
@@ -244,14 +243,13 @@ static int snd_opl3_seq_probe(struct device *_dev)
return 0;
}
-static int snd_opl3_seq_remove(struct device *_dev)
+static void snd_opl3_seq_remove(struct snd_seq_device *dev)
{
- struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_opl3 *opl3;
opl3 = *(struct snd_opl3 **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
if (opl3 == NULL)
- return -EINVAL;
+ return;
#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS)
snd_opl3_free_seq_oss(opl3);
@@ -260,14 +258,13 @@ static int snd_opl3_seq_remove(struct device *_dev)
snd_seq_delete_kernel_client(opl3->seq_client);
opl3->seq_client = -1;
}
- return 0;
}
static struct snd_seq_driver opl3_seq_driver = {
+ .probe = snd_opl3_seq_probe,
+ .remove = snd_opl3_seq_remove,
.driver = {
.name = KBUILD_MODNAME,
- .probe = snd_opl3_seq_probe,
- .remove = snd_opl3_seq_remove,
},
.id = SNDRV_SEQ_DEV_ID_OPL3,
.argsize = sizeof(struct snd_opl3 *),
diff --git a/sound/drivers/opl4/opl4_seq.c b/sound/drivers/opl4/opl4_seq.c
index 7bb22089a093..fd6f15be6109 100644
--- a/sound/drivers/opl4/opl4_seq.c
+++ b/sound/drivers/opl4/opl4_seq.c
@@ -118,9 +118,8 @@ static void snd_opl4_seq_free_port(void *private_data)
snd_midi_channel_free_set(opl4->chset);
}
-static int snd_opl4_seq_probe(struct device *_dev)
+static int snd_opl4_seq_probe(struct snd_seq_device *dev)
{
- struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_opl4 *opl4;
int client;
struct snd_seq_port_callback pcallbacks;
@@ -175,27 +174,25 @@ static int snd_opl4_seq_probe(struct device *_dev)
return 0;
}
-static int snd_opl4_seq_remove(struct device *_dev)
+static void snd_opl4_seq_remove(struct snd_seq_device *dev)
{
- struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_opl4 *opl4;
opl4 = *(struct snd_opl4 **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
if (!opl4)
- return -EINVAL;
+ return;
if (opl4->seq_client >= 0) {
snd_seq_delete_kernel_client(opl4->seq_client);
opl4->seq_client = -1;
}
- return 0;
}
static struct snd_seq_driver opl4_seq_driver = {
+ .probe = snd_opl4_seq_probe,
+ .remove = snd_opl4_seq_remove,
.driver = {
.name = KBUILD_MODNAME,
- .probe = snd_opl4_seq_probe,
- .remove = snd_opl4_seq_remove,
},
.id = SNDRV_SEQ_DEV_ID_OPL4,
.argsize = sizeof(struct snd_opl4 *),
diff --git a/sound/hda/codecs/cirrus/Kconfig b/sound/hda/codecs/cirrus/Kconfig
index ec6cbcaf64f0..d7a1b619d243 100644
--- a/sound/hda/codecs/cirrus/Kconfig
+++ b/sound/hda/codecs/cirrus/Kconfig
@@ -34,6 +34,7 @@ comment "Set to Y if you want auto-loading the codec driver"
config SND_HDA_CODEC_CS8409
tristate "Build Cirrus Logic HDA bridge support"
select SND_HDA_GENERIC
+ select SND_HDA_SCODEC_COMPONENT
help
Say Y or M here to include Cirrus Logic HDA bridge support
such as CS8409.
diff --git a/sound/hda/codecs/cirrus/cs8409-tables.c b/sound/hda/codecs/cirrus/cs8409-tables.c
index 8c703b714a71..b9ec8fb8eab7 100644
--- a/sound/hda/codecs/cirrus/cs8409-tables.c
+++ b/sound/hda/codecs/cirrus/cs8409-tables.c
@@ -469,6 +469,70 @@ struct sub_codec dolphin_cs42l42_1 = {
};
/******************************************************************************
+ * CDB35L56-FOUR-HD Specific Arrays
+ ******************************************************************************/
+const struct hda_verb cs8409_cdb35l56_four_init_verbs[] = {
+ { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_STATE, 0x0001 }, /* Enable VPW processing */
+ {} /* terminator */
+};
+
+static const struct hda_pintbl cs8409_cdb35l56_four_pincfgs[] = {
+ /* 0xPPLLLLLLDDDDTTTTCCCCMMMMAAAASSSS
+ * P = PCON: AC_JACK_PORT_*
+ * L = LOC: AC_JACK_LOC_*
+ * D = DD: device type AC_JACK_*
+ * T = CTYP: AC_JACK_CONN_*
+ * C = COL: AC_JACK_COLOR_*
+ * M = MISC: ?
+ * A = DA: AC_DEFCFG_DEF_ASSOC
+ * S = SEQ: Sequence number in DA group
+ */
+ { CS8409_PIN_ASP2_TRANSMITTER_A, 0x901000f0 }, /* ASP-2-TX */
+ /* "Mic" */
+ { CS8409_PIN_ASP2_RECEIVER_A, 0x04a12050 }, /* ASP-2-RX */
+ {} /* terminator */
+};
+
+const struct cs8409_cir_param cs8409_cdb35l56_four_hw_cfg[] = {
+ /* +PLL1/2_EN, +I2C_EN */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG1, 0xb008 },
+ /* ASP1/2_EN=0, ASP1_STP=1 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG2, 0x0002 },
+ /* ASP1/2_BUS_IDLE=10, +GPIO_I2C */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG3, 0x0a80 },
+ /* ASP2.A: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=0 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP2_A_TX_CTRL1, 0x0800 },
+ /* ASP2.A: TX.RAP=1, TX.RSZ=24 bits, TX.RCS=0 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP2_A_TX_CTRL2, 0x2800 },
+ /* ASP2.A: RX.LAP=0, RX.LSZ=24 bits, RX.LCS=0 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP2_A_RX_CTRL1, 0x0800 },
+ /* ASP2.A: RX.RAP=1, RX.RSZ=24 bits, RX.RCS=0 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP2_A_RX_CTRL2, 0x2800 },
+ /* ASP1: LCHI = 00h */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL1, 0x8000 },
+ /* ASP1: MC/SC_SRCSEL=PLL1, LCPR=FFh */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL2, 0x28ff },
+ /* ASP1: MCEN=0, FSD=011, SCPOL_IN/OUT=0, SCDIV=1:4 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL3, 0x0062 },
+ /* ASP2: LCHI=1Fh */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP2_CLK_CTRL1, 0x801f },
+ /* ASP2: MC/SC_SRCSEL=PLL1, LCPR=3Fh */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP2_CLK_CTRL2, 0x283f },
+ /* ASP2: 5050=1, MCEN=0, FSD=010, SCPOL_IN/OUT=1, SCDIV=1:16 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP2_CLK_CTRL3, 0x805c },
+ /* ASP1/2_BEEP=0 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_BEEP_CFG, 0x0000 },
+ /* ASP1/2_EN=1, ASP1_STP=1 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG2, 0x0062 },
+ /* -PLL2_EN */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG1, 0x9008 }, /* TX2.A: pre-scale att.=0 dB */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PRE_SCALE_ATTN2, 0x0000 },
+ /* ASP1/2_xxx_EN=1, ASP1/2_MCLK_EN=0, DMIC1_SCL_EN=1 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PAD_CFG_SLW_RATE_CTRL, 0xfc03 },
+ {} /* Terminator */
+};
+
+/******************************************************************************
* CS8409 Patch Driver Structs
* Arrays Used for all projects using CS8409
******************************************************************************/
@@ -557,7 +621,6 @@ const struct hda_quirk cs8409_fixup_tbl[] = {
{} /* terminator */
};
-/* Dell Inspiron models with cs8409/cs42l42 */
const struct hda_model_fixup cs8409_models[] = {
{ .id = CS8409_BULLSEYE, .name = "bullseye" },
{ .id = CS8409_WARLOCK, .name = "warlock" },
@@ -566,6 +629,7 @@ const struct hda_model_fixup cs8409_models[] = {
{ .id = CS8409_CYBORG, .name = "cyborg" },
{ .id = CS8409_DOLPHIN, .name = "dolphin" },
{ .id = CS8409_ODIN, .name = "odin" },
+ { .id = CS8409_CDB35L56_FOUR_HD, .name = "CDB35L56-FOUR-HD" },
{}
};
@@ -620,4 +684,14 @@ const struct hda_fixup cs8409_fixups[] = {
.chained = true,
.chain_id = CS8409_FIXUPS,
},
+ [CS8409_CDB35L56_FOUR_HD] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cs8409_cdb35l56_four_pincfgs,
+ .chained = true,
+ .chain_id = CS8409_CDB35L56_FOUR_HD_FIXUP,
+ },
+ [CS8409_CDB35L56_FOUR_HD_FIXUP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs8409_cdb35l56_four_autodet_fixup,
+ },
};
diff --git a/sound/hda/codecs/cirrus/cs8409.c b/sound/hda/codecs/cirrus/cs8409.c
index 2c02d3be89ee..61b6a15d6291 100644
--- a/sound/hda/codecs/cirrus/cs8409.c
+++ b/sound/hda/codecs/cirrus/cs8409.c
@@ -6,14 +6,19 @@
* Cirrus Logic International Semiconductor Ltd.
*/
+#include <linux/acpi.h>
+#include <linux/cleanup.h>
+#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/spi/spi.h>
#include <sound/core.h>
#include <linux/mutex.h>
#include <linux/iopoll.h>
#include "cs8409.h"
+#include "../side-codecs/hda_component.h"
/******************************************************************************
* CS8409 Specific Functions
@@ -1216,6 +1221,172 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
}
}
+static int cs8409_comp_bind(struct device *dev)
+{
+ struct hda_codec *codec = dev_to_hda_codec(dev);
+ struct cs8409_spec *spec = codec->spec;
+
+ return hda_component_manager_bind(codec, &spec->comps);
+}
+
+static void cs8409_comp_unbind(struct device *dev)
+{
+ struct hda_codec *codec = dev_to_hda_codec(dev);
+ struct cs8409_spec *spec = codec->spec;
+
+ hda_component_manager_unbind(codec, &spec->comps);
+}
+
+static const struct component_master_ops cs8409_comp_master_ops = {
+ .bind = cs8409_comp_bind,
+ .unbind = cs8409_comp_unbind,
+};
+
+static void cs8409_comp_playback_hook(struct hda_pcm_stream *hinfo, struct hda_codec *codec,
+ struct snd_pcm_substream *sub, int action)
+{
+ struct cs8409_spec *spec = codec->spec;
+
+ hda_component_manager_playback_hook(&spec->comps, action);
+}
+
+static void cs8409_cdb35l56_four_hw_init(struct hda_codec *codec)
+{
+ const struct cs8409_cir_param *seq = cs8409_cdb35l56_four_hw_cfg;
+
+ for (; seq->nid; seq++)
+ cs8409_vendor_coef_set(codec, seq->cir, seq->coeff);
+}
+
+static int cs8409_spk_sw_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct cs8409_spec *spec = codec->spec;
+
+ ucontrol->value.integer.value[0] = !spec->speaker_muted;
+
+ return 0;
+}
+
+static int cs8409_spk_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct cs8409_spec *spec = codec->spec;
+ bool muted = !ucontrol->value.integer.value[0];
+
+ if (muted == spec->speaker_muted)
+ return 0;
+
+ spec->speaker_muted = muted;
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new cs8409_spk_sw_component_ctrl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_ctl_boolean_mono_info,
+ .get = cs8409_spk_sw_get,
+ .put = cs8409_spk_sw_put,
+};
+
+void cs8409_cdb35l56_four_autodet_fixup(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct device *dev = hda_codec_dev(codec);
+ struct cs8409_spec *spec = codec->spec;
+ struct acpi_device *adev;
+ const char *bus = NULL;
+ static const struct {
+ const char *hid;
+ const char *name;
+ } acpi_ids[] = {{ "CSC3554", "cs35l54-hda" },
+ { "CSC3556", "cs35l56-hda" },
+ { "CSC3557", "cs35l57-hda" }};
+ char *match;
+ int i, count = 0, count_devindex = 0;
+ int ret;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE: {
+ for (i = 0; i < ARRAY_SIZE(acpi_ids); ++i) {
+ adev = acpi_dev_get_first_match_dev(acpi_ids[i].hid, NULL, -1);
+ if (adev)
+ break;
+ }
+ if (!adev) {
+ dev_err(dev, "Failed to find ACPI entry for a Cirrus Amp\n");
+ return;
+ }
+
+ count = i2c_acpi_client_count(adev);
+ if (count > 0) {
+ bus = "i2c";
+ } else {
+ count = acpi_spi_count_resources(adev);
+ if (count > 0)
+ bus = "spi";
+ }
+
+ struct fwnode_handle *fwnode __free(fwnode_handle) =
+ fwnode_handle_get(acpi_fwnode_handle(adev));
+ acpi_dev_put(adev);
+
+ if (!bus) {
+ dev_err(dev, "Did not find any buses for %s\n", acpi_ids[i].hid);
+ return;
+ }
+
+ if (!fwnode) {
+ dev_err(dev, "Could not get fwnode for %s\n", acpi_ids[i].hid);
+ return;
+ }
+
+ /*
+ * When available the cirrus,dev-index property is an accurate
+ * count of the amps in a system and is used in preference to
+ * the count of bus devices that can contain additional address
+ * alias entries.
+ */
+ count_devindex = fwnode_property_count_u32(fwnode, "cirrus,dev-index");
+ if (count_devindex > 0)
+ count = count_devindex;
+
+ match = devm_kasprintf(dev, GFP_KERNEL, "-%%s:00-%s.%%d", acpi_ids[i].name);
+ if (!match)
+ return;
+ dev_info(dev, "Found %d %s on %s (%s)\n", count, acpi_ids[i].hid, bus, match);
+
+ ret = hda_component_manager_init(codec, &spec->comps, count, bus,
+ acpi_ids[i].hid, match,
+ &cs8409_comp_master_ops);
+ if (ret)
+ return;
+
+ spec->gen.pcm_playback_hook = cs8409_comp_playback_hook;
+
+ snd_hda_add_verbs(codec, cs8409_cdb35l56_four_init_verbs);
+ snd_hda_sequence_write(codec, cs8409_cdb35l56_four_init_verbs);
+ break;
+ }
+ case HDA_FIXUP_ACT_PROBE:
+ spec->speaker_muted = 0; /* speakers begin enabled */
+ snd_hda_gen_add_kctl(&spec->gen, "Speaker Playback Switch",
+ &cs8409_spk_sw_component_ctrl);
+ spec->gen.stream_analog_playback = &cs42l42_48k_pcm_analog_playback;
+ snd_hda_codec_set_name(codec, "CS8409/CS35L56");
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ cs8409_cdb35l56_four_hw_init(codec);
+ break;
+ case HDA_FIXUP_ACT_FREE:
+ hda_component_manager_free(&spec->comps, &cs8409_comp_master_ops);
+ break;
+ }
+}
+
/******************************************************************************
* Dolphin Specific Functions
* CS8409/ 2 X CS42L42
@@ -1473,3 +1644,4 @@ module_hda_codec_driver(cs8409_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Cirrus Logic HDA bridge");
+MODULE_IMPORT_NS("SND_HDA_SCODEC_COMPONENT");
diff --git a/sound/hda/codecs/cirrus/cs8409.h b/sound/hda/codecs/cirrus/cs8409.h
index 7fe56f4a73bc..be1714a84fff 100644
--- a/sound/hda/codecs/cirrus/cs8409.h
+++ b/sound/hda/codecs/cirrus/cs8409.h
@@ -18,6 +18,7 @@
#include "hda_auto_parser.h"
#include "hda_jack.h"
#include "../generic.h"
+#include "../side-codecs/hda_component.h"
/* CS8409 Specific Definitions */
@@ -271,6 +272,8 @@ enum {
CS8409_DOLPHIN,
CS8409_DOLPHIN_FIXUPS,
CS8409_ODIN,
+ CS8409_CDB35L56_FOUR_HD,
+ CS8409_CDB35L56_FOUR_HD_FIXUP,
};
enum {
@@ -341,12 +344,17 @@ struct cs8409_spec {
unsigned int capture_started:1;
unsigned int init_done:1;
unsigned int build_ctrl_done:1;
+ unsigned int speaker_muted:1;
/* verb exec op override */
int (*exec_verb)(struct hdac_device *dev, unsigned int cmd, unsigned int flags,
unsigned int *res);
/* unsol_event op override */
void (*unsol_event)(struct hda_codec *codec, unsigned int res);
+
+ /* component binding */
+ struct component_match *match;
+ struct hda_component_parent comps;
};
extern const struct snd_kcontrol_new cs42l42_dac_volume_mixer;
@@ -374,4 +382,9 @@ extern struct sub_codec dolphin_cs42l42_1;
void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action);
void dolphin_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action);
+extern const struct cs8409_cir_param cs8409_cdb35l56_four_hw_cfg[];
+extern const struct hda_verb cs8409_cdb35l56_four_init_verbs[];
+void cs8409_cdb35l56_four_autodet_fixup(struct hda_codec *codec, const struct hda_fixup *fix,
+ int action);
+
#endif
diff --git a/sound/hda/codecs/cm9825.c b/sound/hda/codecs/cm9825.c
index 5c474ce44348..52ee431f2e2c 100644
--- a/sound/hda/codecs/cm9825.c
+++ b/sound/hda/codecs/cm9825.c
@@ -13,6 +13,11 @@
#include "hda_jack.h"
#include "generic.h"
+enum {
+ QUIRK_CM_STD = 0x0,
+ QUIRK_GENE_TWL7_SSID = 0x160dc000
+};
+
/* CM9825 Offset Definitions */
#define CM9825_VERB_SET_HPF_1 0x781
@@ -25,6 +30,7 @@
#define CM9825_VERB_SET_VNEG 0x7a8
#define CM9825_VERB_SET_D2S 0x7a9
#define CM9825_VERB_SET_DACTRL 0x7aa
+#define CM9825_VERB_SET_P3BCP 0x7ab
#define CM9825_VERB_SET_PDNEG 0x7ac
#define CM9825_VERB_SET_VDO 0x7ad
#define CM9825_VERB_SET_CDALR 0x7b0
@@ -42,7 +48,12 @@ struct cmi_spec {
const struct hda_verb *chip_hp_present_verbs;
const struct hda_verb *chip_hp_remove_verbs;
struct hda_codec *codec;
+ struct delayed_work unsol_inputs_work;
+ struct delayed_work unsol_lineout_work;
struct delayed_work unsol_hp_work;
+ hda_nid_t jd_cap_hp;
+ hda_nid_t jd_cap_lineout;
+ hda_nid_t jd_cap_inputs[AUTO_CFG_MAX_INS];
int quirk;
};
@@ -111,6 +122,121 @@ static const struct hda_verb cm9825_hp_remove_verbs[] = {
{}
};
+/*
+ * To save power, AD/CLK is turned off.
+ */
+static const struct hda_verb cm9825_gene_twl7_d3_verbs[] = {
+ {0x43, CM9825_VERB_SET_D2S, 0x62},
+ {0x43, CM9825_VERB_SET_PLL, 0x01},
+ {0x43, CM9825_VERB_SET_NEG, 0xc2},
+ {0x43, CM9825_VERB_SET_ADCL, 0x00},
+ {0x43, CM9825_VERB_SET_DACL, 0x02},
+ {0x43, CM9825_VERB_SET_MBIAS, 0x00},
+ {0x43, CM9825_VERB_SET_VNEG, 0x50},
+ {0x43, CM9825_VERB_SET_PDNEG, 0x04},
+ {0x43, CM9825_VERB_SET_CDALR, 0xf6},
+ {0x43, CM9825_VERB_SET_OTP, 0xcd},
+ {}
+};
+
+/*
+ * These settings are required to properly enable the PLL, clock, ADC and
+ * DAC paths, and to select the correct analog input routing. Without
+ * these explicit configurations, the ADC does not start correctly and
+ * recording does not work reliably on this hardware.
+ *
+ * D0 configuration: enable PLL/CLK/ADC/DAC and optimize performance
+ */
+static const struct hda_verb cm9825_gene_twl7_d0_verbs[] = {
+ {0x34, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
+ {0x43, CM9825_VERB_SET_SNR, 0x38},
+ {0x43, CM9825_VERB_SET_PLL, 0x00},
+ {0x43, CM9825_VERB_SET_ADCL, 0xcf},
+ {0x43, CM9825_VERB_SET_DACL, 0xaa},
+ {0x43, CM9825_VERB_SET_MBIAS, 0x1c},
+ {0x43, CM9825_VERB_SET_VNEG, 0x56},
+ {0x43, CM9825_VERB_SET_D2S, 0x62},
+ {0x43, CM9825_VERB_SET_DACTRL, 0x00},
+ {0x43, CM9825_VERB_SET_PDNEG, 0x0c},
+ {0x43, CM9825_VERB_SET_CDALR, 0xf4},
+ {0x43, CM9825_VERB_SET_OTP, 0xcd},
+ {0x43, CM9825_VERB_SET_MTCBA, 0x61},
+ {0x43, CM9825_VERB_SET_OCP, 0x33},
+ {0x43, CM9825_VERB_SET_GAD, 0x07},
+ {0x43, CM9825_VERB_SET_TMOD, 0x26},
+ {0x43, CM9825_VERB_SET_HPF_1, 0x40},
+ {0x43, CM9825_VERB_SET_HPF_2, 0x40},
+ {0x40, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x3d, AC_VERB_SET_CONNECT_SEL, 0x01},
+ {0x46, CM9825_VERB_SET_P3BCP, 0x20},
+ {}
+};
+
+/*
+ * Enable DAC to start playback.
+ */
+static const struct hda_verb cm9825_gene_twl7_playback_start_verbs[] = {
+ {0x43, CM9825_VERB_SET_D2S, 0xf2},
+ {0x43, CM9825_VERB_SET_VDO, 0xd4},
+ {0x43, CM9825_VERB_SET_SNR, 0x30},
+ {}
+};
+
+/*
+ * Disable DAC and enable de-pop noise mechanism.
+ */
+static const struct hda_verb cm9825_gene_twl7_playback_stop_verbs[] = {
+ {0x43, CM9825_VERB_SET_VDO, 0xc0},
+ {0x43, CM9825_VERB_SET_D2S, 0x62},
+ {0x43, CM9825_VERB_SET_VDO, 0xd0},
+ {0x43, CM9825_VERB_SET_SNR, 0x38},
+ {}
+};
+
+static void cm9825_update_jk_plug_status(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct cmi_spec *spec = codec->spec;
+ bool jack_plugin;
+ struct hda_jack_tbl *jack;
+
+ jack_plugin = snd_hda_jack_detect(spec->codec, nid);
+ jack = snd_hda_jack_tbl_get(spec->codec, nid);
+ if (jack) {
+ jack->block_report = 0;
+ snd_hda_jack_report_sync(spec->codec);
+ }
+
+ codec_dbg(spec->codec,
+ "%s, jack_plugin %d, nid 0x%X, line%d\n",
+ __func__, (int)jack_plugin, nid, __LINE__);
+}
+
+static void cm9825_unsol_inputs_delayed(struct work_struct *work)
+{
+ struct cmi_spec *spec =
+ container_of(to_delayed_work(work), struct cmi_spec,
+ unsol_inputs_work);
+ int i;
+
+ for (i = 0; i < spec->gen.autocfg.num_inputs; i++) {
+ if (!spec->jd_cap_inputs[i])
+ continue;
+
+ cm9825_update_jk_plug_status(spec->codec,
+ spec->gen.autocfg.inputs[i].pin);
+ }
+}
+
+static void cm9825_unsol_lineout_delayed(struct work_struct *work)
+{
+ struct cmi_spec *spec =
+ container_of(to_delayed_work(work), struct cmi_spec,
+ unsol_lineout_work);
+
+ cm9825_update_jk_plug_status(spec->codec,
+ spec->gen.autocfg.line_out_pins[0]);
+}
+
static void cm9825_unsol_hp_delayed(struct work_struct *work)
{
struct cmi_spec *spec =
@@ -159,16 +285,93 @@ static void hp_callback(struct hda_codec *codec, struct hda_jack_callback *cb)
tbl = snd_hda_jack_tbl_get(codec, cb->nid);
if (tbl)
tbl->block_report = 1;
- schedule_delayed_work(&spec->unsol_hp_work, msecs_to_jiffies(200));
+
+ if (cb->nid == spec->jd_cap_hp)
+ schedule_delayed_work(&spec->unsol_hp_work,
+ msecs_to_jiffies(200));
+ else if (cb->nid == spec->jd_cap_lineout)
+ schedule_delayed_work(&spec->unsol_lineout_work,
+ msecs_to_jiffies(200));
+
+ for (int i = 0; i < spec->gen.autocfg.num_inputs; i++) {
+ if (cb->nid == spec->jd_cap_inputs[i])
+ schedule_delayed_work(&spec->unsol_inputs_work,
+ msecs_to_jiffies(200));
+ }
}
static void cm9825_setup_unsol(struct hda_codec *codec)
{
struct cmi_spec *spec = codec->spec;
+ int i;
hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
- snd_hda_jack_detect_enable_callback(codec, hp_pin, hp_callback);
+ hda_nid_t lineout_pin = spec->gen.autocfg.line_out_pins[0];
+
+ if (hp_pin != 0) {
+ if (is_jack_detectable(codec, hp_pin)) {
+ spec->jd_cap_hp = hp_pin;
+ snd_hda_jack_detect_enable_callback(codec, hp_pin,
+ hp_callback);
+ } else
+ spec->jd_cap_hp = 0;
+ } else
+ spec->jd_cap_hp = 0;
+
+ if (lineout_pin != 0) {
+ if (is_jack_detectable(codec, lineout_pin)) {
+ spec->jd_cap_lineout = lineout_pin;
+ snd_hda_jack_detect_enable_callback(codec, lineout_pin,
+ hp_callback);
+ } else
+ spec->jd_cap_lineout = 0;
+ } else
+ spec->jd_cap_lineout = 0;
+
+ codec_dbg(codec,
+ "%s, jd_cap_hp 0x%02X, jd_cap_lineout 0x%02X, line%d\n",
+ __func__, spec->jd_cap_hp, spec->jd_cap_lineout, __LINE__);
+
+ for (i = 0; i < spec->gen.autocfg.num_inputs; i++) {
+ if (spec->gen.autocfg.inputs[i].pin != 0) {
+ if (is_jack_detectable
+ (codec, spec->gen.autocfg.inputs[i].pin)) {
+ spec->jd_cap_inputs[i] =
+ spec->gen.autocfg.inputs[i].pin;
+ snd_hda_jack_detect_enable_callback(codec,
+ spec->gen.autocfg.inputs[i].pin,
+ hp_callback);
+ } else
+ spec->jd_cap_inputs[i] = 0;
+ } else
+ spec->jd_cap_inputs[i] = 0;
+
+ codec_dbg(codec,
+ "%s, input jd_cap_inputs[%d] 0x%02X, line%d\n",
+ __func__, i, spec->jd_cap_inputs[i], __LINE__);
+ }
+}
+
+static void cm9825_playback_pcm_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
+{
+ struct cmi_spec *spec = codec->spec;
+
+ switch (action) {
+ case HDA_GEN_PCM_ACT_PREPARE:
+ snd_hda_sequence_write(spec->codec,
+ cm9825_gene_twl7_playback_start_verbs);
+ break;
+ case HDA_GEN_PCM_ACT_CLEANUP:
+ snd_hda_sequence_write(spec->codec,
+ cm9825_gene_twl7_playback_stop_verbs);
+ break;
+ default:
+ return;
+ }
}
static int cm9825_init(struct hda_codec *codec)
@@ -182,23 +385,48 @@ static int cm9825_init(struct hda_codec *codec)
static void cm9825_remove(struct hda_codec *codec)
{
struct cmi_spec *spec = codec->spec;
+ int i;
+
+ if (spec->jd_cap_hp)
+ cancel_delayed_work_sync(&spec->unsol_hp_work);
+
+ if (spec->jd_cap_lineout)
+ cancel_delayed_work_sync(&spec->unsol_lineout_work);
+
+ for (i = 0; i < spec->gen.autocfg.num_inputs; i++) {
+ if (spec->jd_cap_inputs[i]) {
+ cancel_delayed_work_sync(&spec->unsol_inputs_work);
+ break;
+ }
+ }
- cancel_delayed_work_sync(&spec->unsol_hp_work);
snd_hda_gen_remove(codec);
}
static int cm9825_suspend(struct hda_codec *codec)
{
struct cmi_spec *spec = codec->spec;
+ int i;
+
+ if (spec->jd_cap_hp)
+ cancel_delayed_work_sync(&spec->unsol_hp_work);
- cancel_delayed_work_sync(&spec->unsol_hp_work);
+ if (spec->jd_cap_lineout)
+ cancel_delayed_work_sync(&spec->unsol_lineout_work);
+
+ for (i = 0; i < spec->gen.autocfg.num_inputs; i++) {
+ if (spec->jd_cap_inputs[i]) {
+ cancel_delayed_work_sync(&spec->unsol_inputs_work);
+ break;
+ }
+ }
snd_hda_sequence_write(codec, spec->chip_d3_verbs);
return 0;
}
-static int cm9825_resume(struct hda_codec *codec)
+static int cm9825_cm_std_resume(struct hda_codec *codec)
{
struct cmi_spec *spec = codec->spec;
hda_nid_t hp_pin = 0;
@@ -215,6 +443,8 @@ static int cm9825_resume(struct hda_codec *codec)
snd_hda_codec_init(codec);
+ snd_hda_sequence_write(codec, spec->chip_d0_verbs);
+
hp_pin = spec->gen.autocfg.hp_pins[0];
hp_jack_plugin = snd_hda_jack_detect(spec->codec, hp_pin);
@@ -232,6 +462,20 @@ static int cm9825_resume(struct hda_codec *codec)
snd_hda_sequence_write(codec, cm9825_hp_remove_verbs);
}
+ return 0;
+}
+
+static int cm9825_resume(struct hda_codec *codec)
+{
+ struct cmi_spec *spec = codec->spec;
+
+ if (codec->core.subsystem_id == QUIRK_CM_STD)
+ cm9825_cm_std_resume(codec);
+ else if (codec->core.subsystem_id == QUIRK_GENE_TWL7_SSID) {
+ snd_hda_codec_init(codec);
+ snd_hda_sequence_write(codec, spec->chip_d0_verbs);
+ }
+
snd_hda_regmap_sync(codec);
hda_call_check_power_status(codec, 0x01);
@@ -242,13 +486,15 @@ static int cm9825_probe(struct hda_codec *codec, const struct hda_device_id *id)
{
struct cmi_spec *spec;
struct auto_pin_cfg *cfg;
- int err;
+ int err = 0;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
return -ENOMEM;
- INIT_DELAYED_WORK(&spec->unsol_hp_work, cm9825_unsol_hp_delayed);
+ codec_dbg(codec, "chip_name: %s, ssid: 0x%X\n",
+ codec->core.chip_name, codec->core.subsystem_id);
+
codec->spec = spec;
spec->codec = codec;
cfg = &spec->gen.autocfg;
@@ -258,6 +504,36 @@ static int cm9825_probe(struct hda_codec *codec, const struct hda_device_id *id)
spec->chip_hp_present_verbs = cm9825_hp_present_verbs;
spec->chip_hp_remove_verbs = cm9825_hp_remove_verbs;
+ INIT_DELAYED_WORK(&spec->unsol_hp_work, cm9825_unsol_hp_delayed);
+ INIT_DELAYED_WORK(&spec->unsol_inputs_work,
+ cm9825_unsol_inputs_delayed);
+ INIT_DELAYED_WORK(&spec->unsol_lineout_work,
+ cm9825_unsol_lineout_delayed);
+
+ switch (codec->core.subsystem_id) {
+ case QUIRK_CM_STD:
+ snd_hda_codec_set_name(codec, "CM9825 STD");
+ spec->chip_d0_verbs = cm9825_std_d0_verbs;
+ spec->chip_d3_verbs = cm9825_std_d3_verbs;
+ spec->chip_hp_present_verbs = cm9825_hp_present_verbs;
+ spec->chip_hp_remove_verbs = cm9825_hp_remove_verbs;
+ break;
+ case QUIRK_GENE_TWL7_SSID:
+ snd_hda_codec_set_name(codec, "CM9825 GENE_TWL7");
+ spec->chip_d0_verbs = cm9825_gene_twl7_d0_verbs;
+ spec->chip_d3_verbs = cm9825_gene_twl7_d3_verbs;
+ spec->gen.pcm_playback_hook = cm9825_playback_pcm_hook;
+ /* Internal fixed device, Rear, Mic-in, 3.5mm */
+ snd_hda_codec_set_pincfg(codec, 0x37, 0x24A70100);
+ break;
+ default:
+ err = -ENXIO;
+ break;
+ }
+
+ if (err < 0)
+ goto error;
+
snd_hda_sequence_write(codec, spec->chip_d0_verbs);
err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0);
diff --git a/sound/hda/codecs/conexant.c b/sound/hda/codecs/conexant.c
index 5fcbc1312c69..5623d8c0a0f7 100644
--- a/sound/hda/codecs/conexant.c
+++ b/sound/hda/codecs/conexant.c
@@ -1081,6 +1081,7 @@ static const struct hda_quirk cxt5066_fixups[] = {
SND_PCI_QUIRK(0x103c, 0x8174, "HP Spectre x360", CXT_FIXUP_HP_SPECTRE),
SND_PCI_QUIRK(0x103c, 0x822e, "HP ProBook 440 G4", CXT_FIXUP_MUTE_LED_GPIO),
SND_PCI_QUIRK(0x103c, 0x8231, "HP ProBook 450 G4", CXT_FIXUP_MUTE_LED_GPIO),
+ SND_PCI_QUIRK(0x103c, 0x826b, "HP ZBook Studio G4", CXT_FIXUP_MUTE_LED_GPIO),
SND_PCI_QUIRK(0x103c, 0x828c, "HP EliteBook 840 G4", CXT_FIXUP_HP_DOCK),
SND_PCI_QUIRK(0x103c, 0x8299, "HP 800 G3 SFF", CXT_FIXUP_HP_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x829a, "HP 800 G3 DM", CXT_FIXUP_HP_MIC_NO_PRESENCE),
@@ -1123,6 +1124,7 @@ static const struct hda_quirk cxt5066_fixups[] = {
SND_PCI_QUIRK_VENDOR(0x17aa, "Thinkpad/Ideapad", CXT_FIXUP_LENOVO_XPAD_ACPI),
SND_PCI_QUIRK(0x1c06, 0x2011, "Lemote A1004", CXT_PINCFG_LEMOTE_A1004),
SND_PCI_QUIRK(0x1c06, 0x2012, "Lemote A1205", CXT_PINCFG_LEMOTE_A1205),
+ SND_PCI_QUIRK(0x1d05, 0x3012, "MECHREVO Wujie 15X Pro", CXT_FIXUP_HEADSET_MIC),
HDA_CODEC_QUIRK(0x2782, 0x12c3, "Sirius Gen1", CXT_PINCFG_TOP_SPEAKER),
HDA_CODEC_QUIRK(0x2782, 0x12c5, "Sirius Gen2", CXT_PINCFG_TOP_SPEAKER),
{}
diff --git a/sound/hda/codecs/generic.c b/sound/hda/codecs/generic.c
index 7bcf9aef8275..b75a5e9470df 100644
--- a/sound/hda/codecs/generic.c
+++ b/sound/hda/codecs/generic.c
@@ -1517,7 +1517,7 @@ static int count_multiio_pins(struct hda_codec *codec, hda_nid_t reference_pin)
/*
* multi-io helper
*
- * When hardwired is set, try to fill ony hardwired pins, and returns
+ * When hardwired is set, try to fill only hardwired pins, and returns
* zero if any pins are filled, non-zero if nothing found.
* When hardwired is off, try to fill possible input pins, and returns
* the badness value.
@@ -1984,15 +1984,15 @@ static int parse_output_paths(struct hda_codec *codec)
{
struct hda_gen_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
- struct auto_pin_cfg *best_cfg __free(kfree) = NULL;
unsigned int val;
int best_badness = INT_MAX;
int badness;
bool fill_hardwired = true, fill_mio_first = true;
bool best_wired = true, best_mio = true;
bool hp_spk_swapped = false;
+ struct auto_pin_cfg *best_cfg __free(kfree) =
+ kmalloc(sizeof(*best_cfg), GFP_KERNEL);
- best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL);
if (!best_cfg)
return -ENOMEM;
*best_cfg = *cfg;
@@ -4889,7 +4889,7 @@ static int check_auto_mic_availability(struct hda_codec *codec)
* snd_hda_gen_path_power_filter - power_filter hook to make inactive widgets
* into power down
* @codec: the HDA codec
- * @nid: NID to evalute
+ * @nid: NID to evaluate
* @power_state: target power state
*/
unsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec,
diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c
index b66965a52107..80f0be13b69f 100644
--- a/sound/hda/codecs/realtek/alc269.c
+++ b/sound/hda/codecs/realtek/alc269.c
@@ -1551,6 +1551,22 @@ static void alc245_fixup_hp_mute_led_v1_coefbit(struct hda_codec *codec,
}
}
+static void alc245_fixup_hp_mute_led_v2_coefbit(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mute_led_polarity = 0;
+ spec->mute_led_coef.idx = 0x0b;
+ spec->mute_led_coef.mask = 1 << 3;
+ spec->mute_led_coef.on = 1 << 3;
+ spec->mute_led_coef.off = 0;
+ snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set);
+ }
+}
+
/* turn on/off mic-mute LED per capture hook by coef bit */
static int coef_micmute_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
@@ -1616,6 +1632,20 @@ static void alc295_fixup_hp_mute_led_coefbit11(struct hda_codec *codec,
}
}
+static void alc233_fixup_lenovo_coef_micmute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mic_led_coef.idx = 0x10;
+ spec->mic_led_coef.mask = 1 << 13;
+ spec->mic_led_coef.on = 0;
+ spec->mic_led_coef.off = 1 << 13;
+ snd_hda_gen_add_micmute_led_cdev(codec, coef_micmute_led_set);
+ }
+}
+
static void alc285_fixup_hp_mute_led(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
@@ -1630,6 +1660,13 @@ static void alc285_fixup_hp_spectre_x360_mute_led(struct hda_codec *codec,
alc285_fixup_hp_gpio_micmute_led(codec, fix, action);
}
+static void alc245_fixup_hp_envy_x360_mute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc245_fixup_hp_mute_led_v1_coefbit(codec, fix, action);
+ alc245_fixup_hp_gpio_led(codec, fix, action);
+}
+
static void alc236_fixup_hp_mute_led(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
@@ -1824,6 +1861,163 @@ static void alc298_samsung_v2_init_amps(struct hda_codec *codec,
spec->gen.pcm_playback_hook = alc298_samsung_v2_playback_hook;
}
+/* LG Gram Style 14: program vendor coef sequence used by HDA-verb workaround */
+struct alc298_lg_gram_style_seq {
+ unsigned short verb;
+ unsigned short idx;
+ unsigned short val;
+};
+
+static void alc298_lg_gram_style_coef_write(struct hda_codec *codec,
+ unsigned int verb,
+ unsigned int idx,
+ unsigned int val)
+{
+ snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0x23);
+ snd_hda_codec_write(codec, 0x20, 0, verb, idx);
+ snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, 0x00);
+ snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, val);
+ snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, 0xb011);
+}
+
+static void alc298_lg_gram_style_run_seq(struct hda_codec *codec,
+ const struct alc298_lg_gram_style_seq *seq,
+ int seq_size)
+{
+ int i;
+
+ for (i = 0; i < seq_size; i++)
+ alc298_lg_gram_style_coef_write(codec, seq[i].verb,
+ seq[i].idx, seq[i].val);
+}
+
+/* Coef sequences derived from the HDA-verb workaround for this model. */
+static const struct alc298_lg_gram_style_seq alc298_lg_gram_style_preinit_seq[] = {
+ { 0x420, 0x00, 0x01 },
+};
+
+static const struct alc298_lg_gram_style_seq alc298_lg_gram_style_disable_seq[] = {
+ { 0x423, 0xff, 0x00 },
+ { 0x420, 0x3a, 0x80 },
+};
+
+static const struct alc298_lg_gram_style_seq alc298_lg_gram_style_enable_seq[] = {
+ { 0x420, 0x3a, 0x81 },
+ { 0x423, 0xff, 0x01 },
+};
+
+static const struct alc298_lg_gram_style_seq alc298_lg_gram_style_init_seq_38[] = {
+ { 0x423, 0xe1, 0x00 }, { 0x420, 0x12, 0x6f }, { 0x420, 0x14, 0x00 },
+ { 0x420, 0x1b, 0x01 }, { 0x420, 0x1d, 0x01 }, { 0x420, 0x1f, 0xfe },
+ { 0x420, 0x21, 0x00 }, { 0x420, 0x22, 0x10 }, { 0x420, 0x3d, 0x05 },
+ { 0x420, 0x3f, 0x03 }, { 0x420, 0x50, 0x2c }, { 0x420, 0x76, 0x0e },
+ { 0x420, 0x7c, 0x4a }, { 0x420, 0x81, 0x03 }, { 0x423, 0x99, 0x03 },
+ { 0x423, 0xa4, 0xb5 }, { 0x423, 0xa5, 0x01 }, { 0x423, 0xba, 0x94 },
+};
+
+static const struct alc298_lg_gram_style_seq alc298_lg_gram_style_init_seq_39[] = {
+ { 0x423, 0xe1, 0x00 }, { 0x420, 0x12, 0x6f }, { 0x420, 0x14, 0x00 },
+ { 0x420, 0x1b, 0x02 }, { 0x420, 0x1d, 0x02 }, { 0x420, 0x1f, 0xfd },
+ { 0x420, 0x21, 0x01 }, { 0x420, 0x22, 0x10 }, { 0x420, 0x3d, 0x05 },
+ { 0x420, 0x3f, 0x03 }, { 0x420, 0x50, 0x2c }, { 0x420, 0x76, 0x0e },
+ { 0x420, 0x7c, 0x4a }, { 0x420, 0x81, 0x03 }, { 0x423, 0x99, 0x03 },
+ { 0x423, 0xa4, 0xb5 }, { 0x423, 0xa5, 0x01 }, { 0x423, 0xba, 0x94 },
+};
+
+static const struct alc298_lg_gram_style_seq alc298_lg_gram_style_init_seq_3c[] = {
+ { 0x423, 0xe1, 0x00 }, { 0x420, 0x12, 0x6f }, { 0x420, 0x14, 0x00 },
+ { 0x420, 0x1b, 0x01 }, { 0x420, 0x1d, 0x01 }, { 0x420, 0x1f, 0xfe },
+ { 0x420, 0x21, 0x00 }, { 0x420, 0x22, 0x10 }, { 0x420, 0x3d, 0x05 },
+ { 0x420, 0x3f, 0x03 }, { 0x420, 0x50, 0x2c }, { 0x420, 0x76, 0x0e },
+ { 0x420, 0x7c, 0x4a }, { 0x420, 0x81, 0x03 }, { 0x423, 0xba, 0x8d },
+};
+
+static const struct alc298_lg_gram_style_seq alc298_lg_gram_style_init_seq_3d[] = {
+ { 0x423, 0xe1, 0x00 }, { 0x420, 0x12, 0x6f }, { 0x420, 0x14, 0x00 },
+ { 0x420, 0x1b, 0x02 }, { 0x420, 0x1d, 0x02 }, { 0x420, 0x1f, 0xfd },
+ { 0x420, 0x21, 0x01 }, { 0x420, 0x22, 0x10 }, { 0x420, 0x3d, 0x05 },
+ { 0x420, 0x3f, 0x03 }, { 0x420, 0x50, 0x2c }, { 0x420, 0x76, 0x0e },
+ { 0x420, 0x7c, 0x4a }, { 0x420, 0x81, 0x03 }, { 0x423, 0xba, 0x8d },
+};
+
+struct alc298_lg_gram_style_amp_desc {
+ unsigned char nid;
+ const struct alc298_lg_gram_style_seq *init_seq;
+ int init_seq_size;
+};
+
+static const struct alc298_lg_gram_style_amp_desc alc298_lg_gram_style_amps[] = {
+ { 0x38, alc298_lg_gram_style_init_seq_38,
+ ARRAY_SIZE(alc298_lg_gram_style_init_seq_38) },
+ { 0x39, alc298_lg_gram_style_init_seq_39,
+ ARRAY_SIZE(alc298_lg_gram_style_init_seq_39) },
+ { 0x3c, alc298_lg_gram_style_init_seq_3c,
+ ARRAY_SIZE(alc298_lg_gram_style_init_seq_3c) },
+ { 0x3d, alc298_lg_gram_style_init_seq_3d,
+ ARRAY_SIZE(alc298_lg_gram_style_init_seq_3d) },
+};
+
+static void alc298_lg_gram_style_enable_amps(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < spec->num_speaker_amps; i++) {
+ alc_write_coef_idx(codec, 0x22, alc298_lg_gram_style_amps[i].nid);
+ alc298_lg_gram_style_run_seq(codec,
+ alc298_lg_gram_style_enable_seq,
+ ARRAY_SIZE(alc298_lg_gram_style_enable_seq));
+ }
+}
+
+static void alc298_lg_gram_style_disable_amps(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < spec->num_speaker_amps; i++) {
+ alc_write_coef_idx(codec, 0x22, alc298_lg_gram_style_amps[i].nid);
+ alc298_lg_gram_style_run_seq(codec,
+ alc298_lg_gram_style_disable_seq,
+ ARRAY_SIZE(alc298_lg_gram_style_disable_seq));
+ }
+}
+
+static void alc298_lg_gram_style_playback_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
+{
+ if (action == HDA_GEN_PCM_ACT_OPEN)
+ alc298_lg_gram_style_enable_amps(codec);
+ if (action == HDA_GEN_PCM_ACT_CLOSE)
+ alc298_lg_gram_style_disable_amps(codec);
+}
+
+static void alc298_lg_gram_style_init_amps(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int i;
+
+ spec->num_speaker_amps = ARRAY_SIZE(alc298_lg_gram_style_amps);
+
+ for (i = 0; i < spec->num_speaker_amps; i++) {
+ alc_write_coef_idx(codec, 0x22, alc298_lg_gram_style_amps[i].nid);
+ alc298_lg_gram_style_run_seq(codec,
+ alc298_lg_gram_style_preinit_seq,
+ ARRAY_SIZE(alc298_lg_gram_style_preinit_seq));
+ alc298_lg_gram_style_run_seq(codec,
+ alc298_lg_gram_style_disable_seq,
+ ARRAY_SIZE(alc298_lg_gram_style_disable_seq));
+ alc298_lg_gram_style_run_seq(codec,
+ alc298_lg_gram_style_amps[i].init_seq,
+ alc298_lg_gram_style_amps[i].init_seq_size);
+ alc_write_coef_idx(codec, 0x89, 0x0);
+ }
+
+ spec->gen.pcm_playback_hook = alc298_lg_gram_style_playback_hook;
+}
+
static void alc298_fixup_samsung_amp_v2_2_amps(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
@@ -1838,6 +2032,13 @@ static void alc298_fixup_samsung_amp_v2_4_amps(struct hda_codec *codec,
alc298_samsung_v2_init_amps(codec, 4);
}
+static void alc298_fixup_lg_gram_style_14(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PROBE)
+ alc298_lg_gram_style_init_amps(codec);
+}
+
static void gpio2_mic_hotkey_event(struct hda_codec *codec,
struct hda_jack_callback *event)
{
@@ -1918,6 +2119,39 @@ static void alc280_fixup_hp_gpio2_mic_hotkey(struct hda_codec *codec,
}
}
+/* GPIO2 = mic mute hotkey
+ * GPIO3 = mic mute LED
+ */
+static void alc233_fixup_lenovo_gpio2_mic_hotkey(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ alc233_fixup_lenovo_coef_micmute_led(codec, fix, action);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ alc_update_coef_idx(codec, 0x10, 1<<2, 1<<2);
+ if (alc_register_micmute_input_device(codec) != 0)
+ return;
+
+ spec->gpio_mask |= 0x04;
+ spec->gpio_dir |= 0x0;
+ snd_hda_codec_write_cache(codec, codec->core.afg, 0,
+ AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x04);
+ snd_hda_jack_detect_enable_callback(codec, codec->core.afg,
+ gpio2_mic_hotkey_event);
+ return;
+ }
+
+ if (!spec->kb_dev)
+ return;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_FREE:
+ input_unregister_device(spec->kb_dev);
+ spec->kb_dev = NULL;
+ }
+}
+
/* Line2 = mic mute hotkey
* GPIO2 = mic mute LED
*/
@@ -2916,7 +3150,6 @@ static void find_cirrus_companion_amps(struct hda_codec *cdc)
{
struct device *dev = hda_codec_dev(cdc);
struct acpi_device *adev;
- struct fwnode_handle *fwnode __free(fwnode_handle) = NULL;
const char *bus = NULL;
static const struct {
const char *hid;
@@ -2946,7 +3179,8 @@ static void find_cirrus_companion_amps(struct hda_codec *cdc)
bus = "spi";
}
- fwnode = fwnode_handle_get(acpi_fwnode_handle(adev));
+ struct fwnode_handle *fwnode __free(fwnode_handle) =
+ fwnode_handle_get(acpi_fwnode_handle(adev));
acpi_dev_put(adev);
if (!bus) {
@@ -3692,6 +3926,7 @@ enum {
ALC285_FIXUP_HP_GPIO_LED,
ALC285_FIXUP_HP_MUTE_LED,
ALC285_FIXUP_HP_SPECTRE_X360_MUTE_LED,
+ ALC245_FIXUP_HP_ENVY_X360_MUTE_LED,
ALC285_FIXUP_HP_BEEP_MICMUTE_LED,
ALC236_FIXUP_HP_MUTE_LED_COEFBIT2,
ALC236_FIXUP_HP_GPIO_LED,
@@ -3701,6 +3936,7 @@ enum {
ALC298_FIXUP_SAMSUNG_AMP,
ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS,
ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS,
+ ALC298_FIXUP_LG_GRAM_STYLE_14,
ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET,
ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET,
ALC295_FIXUP_ASUS_MIC_NO_PRESENCE,
@@ -3781,6 +4017,7 @@ enum {
ALC287_FIXUP_YOGA7_14ARB7_I2C,
ALC245_FIXUP_HP_MUTE_LED_COEFBIT,
ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT,
+ ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT,
ALC245_FIXUP_HP_X360_MUTE_LEDS,
ALC287_FIXUP_THINKPAD_I2S_SPK,
ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD,
@@ -3816,6 +4053,8 @@ enum {
ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED,
ALC288_FIXUP_SURFACE_SWAP_DACS,
ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO,
+ ALC233_FIXUP_LENOVO_GPIO2_MIC_HOTKEY,
+ ALC245_FIXUP_BASS_HP_DAC,
};
/* A special fixup for Lenovo C940 and Yoga Duet 7;
@@ -5344,6 +5583,10 @@ static const struct hda_fixup alc269_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc285_fixup_hp_spectre_x360_mute_led,
},
+ [ALC245_FIXUP_HP_ENVY_X360_MUTE_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc245_fixup_hp_envy_x360_mute_led,
+ },
[ALC285_FIXUP_HP_BEEP_MICMUTE_LED] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc285_fixup_hp_beep,
@@ -5394,6 +5637,10 @@ static const struct hda_fixup alc269_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc298_fixup_samsung_amp_v2_4_amps
},
+ [ALC298_FIXUP_LG_GRAM_STYLE_14] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc298_fixup_lg_gram_style_14
+ },
[ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET] = {
.type = HDA_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
@@ -6117,6 +6364,10 @@ static const struct hda_fixup alc269_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc245_fixup_hp_mute_led_v1_coefbit,
},
+ [ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc245_fixup_hp_mute_led_v2_coefbit,
+ },
[ALC245_FIXUP_HP_X360_MUTE_LEDS] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc245_fixup_hp_mute_led_coefbit,
@@ -6306,6 +6557,15 @@ static const struct hda_fixup alc269_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc288_fixup_surface_swap_dacs,
},
+ [ALC233_FIXUP_LENOVO_GPIO2_MIC_HOTKEY] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc233_fixup_lenovo_gpio2_mic_hotkey,
+ },
+ [ALC245_FIXUP_BASS_HP_DAC] = {
+ .type = HDA_FIXUP_FUNC,
+ /* Borrow the DAC routing selected for those Thinkpads */
+ .v.func = alc285_fixup_thinkpad_x1_gen7,
+ },
};
static const struct hda_quirk alc269_fixup_tbl[] = {
@@ -6600,8 +6860,10 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8895, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED),
SND_PCI_QUIRK(0x103c, 0x8896, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x8898, "HP EliteBook 845 G8 Notebook PC", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x103c, 0x88b3, "HP ENVY x360 Convertible 15-es0xxx", ALC245_FIXUP_HP_ENVY_X360_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x88d0, "HP Pavilion 15-eh1xxx (mainboard 88D0)", ALC287_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x88dd, "HP Pavilion 15z-ec200", ALC285_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x88eb, "HP Victus 16-e0xxx", ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT),
SND_PCI_QUIRK(0x103c, 0x8902, "HP OMEN 16", ALC285_FIXUP_HP_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x890e, "HP 255 G8 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
SND_PCI_QUIRK(0x103c, 0x8919, "HP Pavilion Aero Laptop 13-be0xxx", ALC287_FIXUP_HP_GPIO_LED),
@@ -7211,7 +7473,12 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x3176, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x17aa, 0x3178, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x17aa, 0x31af, "ThinkCentre Station", ALC623_FIXUP_LENOVO_THINKSTATION_P340),
+ SND_PCI_QUIRK(0x17aa, 0x3341, "Lenovo ThinkCentre M90 Gen4", ALC233_FIXUP_LENOVO_GPIO2_MIC_HOTKEY),
+ SND_PCI_QUIRK(0x17aa, 0x3342, "Lenovo ThinkCentre M90 Gen4", ALC233_FIXUP_LENOVO_GPIO2_MIC_HOTKEY),
+ SND_PCI_QUIRK(0x17aa, 0x3343, "Lenovo ThinkCentre M70 Gen4", ALC233_FIXUP_LENOVO_GPIO2_MIC_HOTKEY),
+ SND_PCI_QUIRK(0x17aa, 0x3344, "Lenovo ThinkCentre M70 Gen4", ALC233_FIXUP_LENOVO_GPIO2_MIC_HOTKEY),
SND_PCI_QUIRK(0x17aa, 0x334b, "Lenovo ThinkCentre M70 Gen5", ALC283_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x17aa, 0x334f, "Lenovo ThinkCentre M90a Gen5", ALC233_FIXUP_LENOVO_GPIO2_MIC_HOTKEY),
SND_PCI_QUIRK(0x17aa, 0x3384, "ThinkCentre M90a PRO", ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED),
SND_PCI_QUIRK(0x17aa, 0x3386, "ThinkCentre M90a Gen6", ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED),
SND_PCI_QUIRK(0x17aa, 0x3387, "ThinkCentre M70a Gen6", ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED),
@@ -7325,6 +7592,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1854, 0x0488, "LG gram 16 (16Z90R)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS),
SND_PCI_QUIRK(0x1854, 0x0489, "LG gram 16 (16Z90R-A)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS),
SND_PCI_QUIRK(0x1854, 0x048a, "LG gram 17 (17ZD90R)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS),
+ SND_PCI_QUIRK(0x1854, 0x0490, "LG Gram Style 14 (14Z90RS)", ALC298_FIXUP_LG_GRAM_STYLE_14),
SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MACH-WX9", ALC256_FIXUP_HUAWEI_MACH_WX9_PINS),
SND_PCI_QUIRK(0x19e5, 0x320f, "Huawei WRT-WX9 ", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x19e5, 0x3212, "Huawei KLV-WX9 ", ALC256_FIXUP_ACER_HEADSET_MIC),
@@ -7358,6 +7626,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1d72, 0x1947, "RedmiBook Air", ALC255_FIXUP_XIAOMI_HEADSET_MIC),
SND_PCI_QUIRK(0x1e39, 0xca14, "MEDION NM14LNL", ALC233_FIXUP_MEDION_MTL_SPK),
SND_PCI_QUIRK(0x1ee7, 0x2078, "HONOR BRB-X M1010", ALC2XX_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1f4c, 0xe001, "Minisforum V3 (SE)", ALC245_FIXUP_BASS_HP_DAC),
SND_PCI_QUIRK(0x1f66, 0x0105, "Ayaneo Portable Game Player", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x2014, 0x800a, "Positivo ARN50", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x2039, 0x0001, "Inspur S14-G1", ALC295_FIXUP_CHROME_BOOK),
@@ -7573,6 +7842,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
{.id = ALC285_FIXUP_HP_GPIO_AMP_INIT, .name = "alc285-hp-amp-init"},
{.id = ALC236_FIXUP_LENOVO_INV_DMIC, .name = "alc236-fixup-lenovo-inv-mic"},
{.id = ALC2XX_FIXUP_HEADSET_MIC, .name = "alc2xx-fixup-headset-mic"},
+ {.id = ALC245_FIXUP_BASS_HP_DAC, .name = "alc245-fixup-bass-hp-dac"},
{}
};
#define ALC225_STANDARD_PINS \
diff --git a/sound/hda/codecs/realtek/realtek.c b/sound/hda/codecs/realtek/realtek.c
index ca377a5adadb..efe20b450529 100644
--- a/sound/hda/codecs/realtek/realtek.c
+++ b/sound/hda/codecs/realtek/realtek.c
@@ -215,12 +215,13 @@ void alc_update_knob_master(struct hda_codec *codec,
{
unsigned int val;
struct snd_kcontrol *kctl;
- struct snd_ctl_elem_value *uctl __free(kfree) = NULL;
kctl = snd_hda_find_mixer_ctl(codec, "Master Playback Volume");
if (!kctl)
return;
- uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
+
+ struct snd_ctl_elem_value *uctl __free(kfree) =
+ kzalloc(sizeof(*uctl), GFP_KERNEL);
if (!uctl)
return;
val = snd_hda_codec_read(codec, jack->nid, 0,
diff --git a/sound/hda/codecs/side-codecs/cirrus_scodec_test.c b/sound/hda/codecs/side-codecs/cirrus_scodec_test.c
index dc35932b6b22..50527c5fa374 100644
--- a/sound/hda/codecs/side-codecs/cirrus_scodec_test.c
+++ b/sound/hda/codecs/side-codecs/cirrus_scodec_test.c
@@ -5,14 +5,13 @@
// Copyright (C) 2023 Cirrus Logic, Inc. and
// Cirrus Logic International Semiconductor Ltd.
-#include <kunit/platform_device.h>
#include <kunit/resource.h>
#include <kunit/test.h>
#include <linux/device.h>
+#include <linux/device/devres.h>
#include <linux/device/faux.h>
#include <linux/gpio/driver.h>
#include <linux/module.h>
-#include <linux/platform_device.h>
#include "cirrus_scodec.h"
@@ -29,7 +28,7 @@ struct cirrus_scodec_test_gpio {
struct cirrus_scodec_test_priv {
struct faux_device *amp_dev;
- struct platform_device *gpio_pdev;
+ struct faux_device *gpio_dev;
struct cirrus_scodec_test_gpio *gpio_priv;
};
@@ -92,59 +91,57 @@ static const struct gpio_chip cirrus_scodec_test_gpio_chip = {
.ngpio = 32,
};
-static int cirrus_scodec_test_gpio_probe(struct platform_device *pdev)
+/* software_node referencing the gpio driver */
+static const struct software_node cirrus_scodec_test_gpio_swnode = {
+ .name = "cirrus_scodec_test_gpio",
+};
+
+static int cirrus_scodec_test_gpio_probe(struct faux_device *fdev)
{
struct cirrus_scodec_test_gpio *gpio_priv;
int ret;
- gpio_priv = devm_kzalloc(&pdev->dev, sizeof(*gpio_priv), GFP_KERNEL);
+ gpio_priv = devm_kzalloc(&fdev->dev, sizeof(*gpio_priv), GFP_KERNEL);
if (!gpio_priv)
return -ENOMEM;
+ ret = device_add_software_node(&fdev->dev, &cirrus_scodec_test_gpio_swnode);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&fdev->dev, device_remove_software_node_wrapper,
+ &fdev->dev);
+ if (ret)
+ return ret;
+
/* GPIO core modifies our struct gpio_chip so use a copy */
gpio_priv->chip = cirrus_scodec_test_gpio_chip;
- gpio_priv->chip.parent = &pdev->dev;
- ret = devm_gpiochip_add_data(&pdev->dev, &gpio_priv->chip, gpio_priv);
+ gpio_priv->chip.parent = &fdev->dev;
+ ret = devm_gpiochip_add_data(&fdev->dev, &gpio_priv->chip, gpio_priv);
if (ret)
- return dev_err_probe(&pdev->dev, ret, "Failed to add gpiochip\n");
+ return dev_err_probe(&fdev->dev, ret, "Failed to add gpiochip\n");
- dev_set_drvdata(&pdev->dev, gpio_priv);
+ dev_set_drvdata(&fdev->dev, gpio_priv);
return 0;
}
-static struct platform_driver cirrus_scodec_test_gpio_driver = {
- .driver.name = "cirrus_scodec_test_gpio_drv",
- .driver.owner = THIS_MODULE,
+static const struct faux_device_ops cirrus_scodec_test_gpio_driver_ops = {
.probe = cirrus_scodec_test_gpio_probe,
};
-/* software_node referencing the gpio driver */
-static const struct software_node cirrus_scodec_test_gpio_swnode = {
- .name = "cirrus_scodec_test_gpio",
-};
-
static void cirrus_scodec_test_create_gpio(struct kunit *test)
{
struct cirrus_scodec_test_priv *priv = test->priv;
- KUNIT_ASSERT_EQ(test, 0,
- kunit_platform_driver_register(test, &cirrus_scodec_test_gpio_driver));
-
- priv->gpio_pdev = kunit_platform_device_alloc(test,
- cirrus_scodec_test_gpio_driver.driver.name,
- PLATFORM_DEVID_NONE);
- KUNIT_ASSERT_NOT_NULL(test, priv->gpio_pdev);
-
- KUNIT_ASSERT_EQ(test, 0, device_add_software_node(&priv->gpio_pdev->dev,
- &cirrus_scodec_test_gpio_swnode));
+ priv->gpio_dev = faux_device_create("cirrus_scodec_test_gpio_drv", NULL,
+ &cirrus_scodec_test_gpio_driver_ops);
+ KUNIT_ASSERT_NOT_NULL(test, priv->gpio_dev);
KUNIT_ASSERT_EQ(test, 0, kunit_add_action_or_reset(test,
- device_remove_software_node_wrapper,
- &priv->gpio_pdev->dev));
-
- KUNIT_ASSERT_EQ(test, 0, kunit_platform_device_add(test, priv->gpio_pdev));
+ faux_device_destroy_wrapper,
+ priv->gpio_dev));
- priv->gpio_priv = dev_get_drvdata(&priv->gpio_pdev->dev);
+ priv->gpio_priv = dev_get_drvdata(&priv->gpio_dev->dev);
KUNIT_ASSERT_NOT_NULL(test, priv->gpio_priv);
}
diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda.c b/sound/hda/codecs/side-codecs/cs35l41_hda.c
index 21e00055c0c4..b64890006bb7 100644
--- a/sound/hda/codecs/side-codecs/cs35l41_hda.c
+++ b/sound/hda/codecs/side-codecs/cs35l41_hda.c
@@ -1255,19 +1255,16 @@ static void cs35l41_fw_load_work(struct work_struct *work)
{
struct cs35l41_hda *cs35l41 = container_of(work, struct cs35l41_hda, fw_load_work);
- pm_runtime_get_sync(cs35l41->dev);
-
- scoped_guard(mutex, &cs35l41->fw_mutex) {
- /* Recheck if playback is ongoing, mutex will block playback during firmware loading */
- if (cs35l41->playback_started)
- dev_err(cs35l41->dev, "Cannot Load/Unload firmware during Playback. Retrying...\n");
- else
- cs35l41_load_firmware(cs35l41, cs35l41->request_fw_load);
+ guard(pm_runtime_active_auto)(cs35l41->dev);
- cs35l41->fw_request_ongoing = false;
- }
+ guard(mutex)(&cs35l41->fw_mutex);
+ /* Recheck if playback is ongoing, mutex will block playback during firmware loading */
+ if (cs35l41->playback_started)
+ dev_err(cs35l41->dev, "Cannot Load/Unload firmware during Playback. Retrying...\n");
+ else
+ cs35l41_load_firmware(cs35l41, cs35l41->request_fw_load);
- pm_runtime_put_autosuspend(cs35l41->dev);
+ cs35l41->fw_request_ongoing = false;
}
static int cs35l41_fw_load_ctl_put(struct snd_kcontrol *kcontrol,
@@ -1455,7 +1452,7 @@ static int cs35l41_hda_bind(struct device *dev, struct device *master, void *mas
if (comp->dev)
return -EBUSY;
- pm_runtime_get_sync(dev);
+ guard(pm_runtime_active_auto)(dev);
mutex_lock(&cs35l41->fw_mutex);
@@ -1499,8 +1496,6 @@ static int cs35l41_hda_bind(struct device *dev, struct device *master, void *mas
dev_warn(dev, "Unable to create device link\n");
unlock_system_sleep(sleep_flags);
- pm_runtime_put_autosuspend(dev);
-
dev_info(cs35l41->dev,
"CS35L41 Bound - SSID: %s, BST: %d, VSPK: %d, CH: %c, FW EN: %d, SPKID: %d\n",
cs35l41->acpi_subsystem_id, cs35l41->hw_cfg.bst_type,
diff --git a/sound/hda/codecs/side-codecs/cs35l56_hda.c b/sound/hda/codecs/side-codecs/cs35l56_hda.c
index f7ba92e11957..cfc8de2ae499 100644
--- a/sound/hda/codecs/side-codecs/cs35l56_hda.c
+++ b/sound/hda/codecs/side-codecs/cs35l56_hda.c
@@ -588,7 +588,8 @@ static void cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
cs35l56->base.fw_patched = false;
- ret = pm_runtime_resume_and_get(cs35l56->base.dev);
+ PM_RUNTIME_ACQUIRE_IF_ENABLED(cs35l56->base.dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
if (ret < 0) {
dev_err(cs35l56->base.dev, "Failed to resume and get %d\n", ret);
return;
@@ -601,7 +602,7 @@ static void cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
*/
ret = cs35l56_read_prot_status(&cs35l56->base, &firmware_missing, &preloaded_fw_ver);
if (ret)
- goto err_pm_put;
+ return;
if (firmware_missing)
preloaded_fw_ver = 0;
@@ -690,8 +691,6 @@ err:
err_fw_release:
cs35l56_hda_release_firmware_files(wmfw_firmware, wmfw_filename,
coeff_firmware, coeff_filename);
-err_pm_put:
- pm_runtime_put(cs35l56->base.dev);
}
static void cs35l56_hda_dsp_work(struct work_struct *work)
@@ -708,14 +707,12 @@ static ssize_t cs35l56_hda_debugfs_calibrate_write(struct file *file,
struct cs35l56_base *cs35l56_base = file->private_data;
ssize_t ret;
- ret = pm_runtime_resume_and_get(cs35l56_base->dev);
+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(cs35l56_base->dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
if (ret)
return ret;
- ret = cs35l56_calibrate_debugfs_write(cs35l56_base, from, count, ppos);
- pm_runtime_autosuspend(cs35l56_base->dev);
-
- return ret;
+ return cs35l56_calibrate_debugfs_write(cs35l56_base, from, count, ppos);
}
static ssize_t cs35l56_hda_debugfs_cal_temperature_write(struct file *file,
@@ -725,14 +722,12 @@ static ssize_t cs35l56_hda_debugfs_cal_temperature_write(struct file *file,
struct cs35l56_base *cs35l56_base = file->private_data;
ssize_t ret;
- ret = pm_runtime_resume_and_get(cs35l56_base->dev);
+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(cs35l56_base->dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
if (ret)
return ret;
- ret = cs35l56_cal_ambient_debugfs_write(cs35l56_base, from, count, ppos);
- pm_runtime_autosuspend(cs35l56_base->dev);
-
- return ret;
+ return cs35l56_cal_ambient_debugfs_write(cs35l56_base, from, count, ppos);
}
static ssize_t cs35l56_hda_debugfs_cal_data_read(struct file *file,
@@ -742,14 +737,12 @@ static ssize_t cs35l56_hda_debugfs_cal_data_read(struct file *file,
struct cs35l56_base *cs35l56_base = file->private_data;
ssize_t ret;
- ret = pm_runtime_resume_and_get(cs35l56_base->dev);
+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(cs35l56_base->dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
if (ret)
return ret;
- ret = cs35l56_cal_data_debugfs_read(cs35l56_base, to, count, ppos);
- pm_runtime_autosuspend(cs35l56_base->dev);
-
- return ret;
+ return cs35l56_cal_data_debugfs_read(cs35l56_base, to, count, ppos);
}
static ssize_t cs35l56_hda_debugfs_cal_data_write(struct file *file,
@@ -767,7 +760,8 @@ static ssize_t cs35l56_hda_debugfs_cal_data_write(struct file *file,
if (ret < 0)
return ret;
- ret = pm_runtime_resume_and_get(cs35l56_base->dev);
+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(cs35l56_base->dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
if (ret)
return ret;
@@ -777,8 +771,6 @@ static ssize_t cs35l56_hda_debugfs_cal_data_write(struct file *file,
else
count = -EIO;
- pm_runtime_autosuspend(cs35l56_base->dev);
-
return count;
}
diff --git a/sound/hda/codecs/side-codecs/tas2781_hda.c b/sound/hda/codecs/side-codecs/tas2781_hda.c
index 96e6d82dc69e..b22f93424c62 100644
--- a/sound/hda/codecs/side-codecs/tas2781_hda.c
+++ b/sound/hda/codecs/side-codecs/tas2781_hda.c
@@ -2,7 +2,7 @@
//
// TAS2781 HDA Shared Lib for I2C&SPI driver
//
-// Copyright 2025 Texas Instruments, Inc.
+// Copyright 2025 - 2026 Texas Instruments, Inc.
//
// Author: Shenghao Ding <shenghao-ding@ti.com>
@@ -159,7 +159,6 @@ static void tas2781_apply_calib(struct tasdevice_priv *p)
r->tlimit_reg = cali_reg[4];
}
- p->is_user_space_calidata = true;
cali_data->total_sz = p->ndev * (cali_data->cali_dat_sz_per_dev + 1);
}
@@ -216,6 +215,12 @@ int tas2781_save_calibration(struct tas2781_hda *hda)
status = -ENOMEM;
continue;
}
+ /*
+ * Set to an invalid value before the calibrated data
+ * is stored into it, for the default value is 0, which
+ * means the first device.
+ */
+ data[0] = 0xff;
/* Get variable contents into buffer */
status = efi.get_variable(efi_name[i], &efi_guid,
&attr, &cali_data->total_sz, data);
diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c
index 624a822341bb..74c3cf1e45e1 100644
--- a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c
+++ b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c
@@ -393,19 +393,6 @@ static int tas2563_save_calibration(struct tas2781_hda *h)
r->pow_reg = TAS2563_CAL_POWER;
r->tlimit_reg = TAS2563_CAL_TLIM;
- /*
- * TAS2781_FMWLIB supports two solutions of calibrated data. One is
- * from the driver itself: driver reads the calibrated files directly
- * during probe; The other from user space: during init of audio hal,
- * the audio hal will pass the calibrated data via kcontrol interface.
- * Driver will store this data in "struct calidata" for use. For hda
- * device, calibrated data are usunally saved into UEFI. So Hda side
- * codec driver use the mixture of these two solutions, driver reads
- * the data from UEFI, then store this data in "struct calidata" for
- * use.
- */
- p->is_user_space_calidata = true;
-
return 0;
}
@@ -504,8 +491,8 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context)
struct hda_codec *codec = tas_priv->codec;
int ret;
- pm_runtime_get_sync(tas_priv->dev);
- mutex_lock(&tas_priv->codec_lock);
+ guard(pm_runtime_active_auto)(tas_priv->dev);
+ guard(mutex)(&tas_priv->codec_lock);
ret = tasdevice_rca_parser(tas_priv, fmw);
if (ret)
@@ -541,9 +528,7 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context)
}
out:
- mutex_unlock(&tas_hda->priv->codec_lock);
release_firmware(fmw);
- pm_runtime_put_autosuspend(tas_hda->dev);
}
static int tas2781_hda_bind(struct device *dev, struct device *master,
@@ -587,7 +572,7 @@ static int tas2781_hda_bind(struct device *dev, struct device *master,
if (codec->core.subsystem_id == 0x10431384)
hda_priv->skip_calibration = true;
- pm_runtime_get_sync(dev);
+ guard(pm_runtime_active_auto)(dev);
comp->dev = dev;
@@ -597,8 +582,6 @@ static int tas2781_hda_bind(struct device *dev, struct device *master,
if (!ret)
comp->playback_hook = tas2781_hda_playback_hook;
- pm_runtime_put_autosuspend(dev);
-
return ret;
}
diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c
index b9a55672bf15..0c9b57b6ff55 100644
--- a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c
+++ b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c
@@ -2,7 +2,7 @@
//
// TAS2781 HDA SPI driver
//
-// Copyright 2024 - 2025 Texas Instruments, Inc.
+// Copyright 2024 - 2026 Texas Instruments, Inc.
//
// Author: Baojun Xu <baojun.xu@ti.com>
@@ -41,9 +41,6 @@
#define TASDEVICE_RANGE_MAX_SIZE (256 * 128)
#define TASDEVICE_WIN_LEN 128
#define TAS2781_SPI_MAX_FREQ (4 * HZ_PER_MHZ)
-/* Flag of calibration registers address. */
-#define TASDEVICE_CALIBRATION_REG_ADDRESS BIT(7)
-#define TASDEV_UEFI_CALI_REG_ADDR_FLG BIT(7)
/* System Reset Check Register */
#define TAS2781_REG_CLK_CONFIG TASDEVICE_REG(0x0, 0x0, 0x5c)
@@ -636,7 +633,7 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context)
struct hda_codec *codec = tas_priv->codec;
int ret, val;
- pm_runtime_get_sync(tas_priv->dev);
+ guard(pm_runtime_active_auto)(tas_priv->dev);
guard(mutex)(&tas_priv->codec_lock);
ret = tasdevice_rca_parser(tas_priv, fmw);
@@ -699,7 +696,6 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context)
tas2781_save_calibration(tas_hda);
out:
release_firmware(fmw);
- pm_runtime_put_autosuspend(tas_hda->priv->dev);
}
static int tas2781_hda_bind(struct device *dev, struct device *master,
@@ -720,7 +716,7 @@ static int tas2781_hda_bind(struct device *dev, struct device *master,
codec = parent->codec;
- pm_runtime_get_sync(dev);
+ guard(pm_runtime_active_auto)(dev);
comp->dev = dev;
@@ -731,7 +727,8 @@ static int tas2781_hda_bind(struct device *dev, struct device *master,
if (!ret)
comp->playback_hook = tas2781_hda_playback_hook;
- pm_runtime_put_autosuspend(dev);
+ /* Only HP Laptop support SPI-based TAS2781 */
+ tas_hda->catlog_id = HP;
return ret;
}
diff --git a/sound/hda/common/codec.c b/sound/hda/common/codec.c
index c6d44168c7f9..ffe7c69d5a32 100644
--- a/sound/hda/common/codec.c
+++ b/sound/hda/common/codec.c
@@ -1854,9 +1854,9 @@ static int check_follower_present(struct hda_codec *codec,
/* call kctl->put with the given value(s) */
static int put_kctl_with_value(struct snd_kcontrol *kctl, int val)
{
- struct snd_ctl_elem_value *ucontrol __free(kfree) = NULL;
+ struct snd_ctl_elem_value *ucontrol __free(kfree) =
+ kzalloc(sizeof(*ucontrol), GFP_KERNEL);
- ucontrol = kzalloc(sizeof(*ucontrol), GFP_KERNEL);
if (!ucontrol)
return -ENOMEM;
ucontrol->value.integer.value[0] = val;
diff --git a/sound/hda/common/hda_jack.h b/sound/hda/common/hda_jack.h
index ff7d289c034b..e9b9970c59ed 100644
--- a/sound/hda/common/hda_jack.h
+++ b/sound/hda/common/hda_jack.h
@@ -82,10 +82,10 @@ snd_hda_jack_detect_enable_callback_mst(struct hda_codec *codec, hda_nid_t nid,
int dev_id, hda_jack_callback_fn func);
/**
- * snd_hda_jack_detect_enable - enable the jack-detection
+ * snd_hda_jack_detect_enable_callback - enable the jack-detection
* @codec: the HDA codec
* @nid: pin NID to enable
- * @func: callback function to register
+ * @cb: callback function to register
*
* In the case of error, the return value will be a pointer embedded with
* errno. Check and handle the return value appropriately with standard
diff --git a/sound/hda/common/hda_local.h b/sound/hda/common/hda_local.h
index a7e53277a0fe..ab423f1cef54 100644
--- a/sound/hda/common/hda_local.h
+++ b/sound/hda/common/hda_local.h
@@ -424,7 +424,7 @@ int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin,
unsigned int val, bool cached);
/**
- * _snd_hda_set_pin_ctl - Set a pin-control value safely
+ * snd_hda_set_pin_ctl - Set a pin-control value safely
* @codec: the codec instance
* @pin: the pin NID to set the control
* @val: the pin-control value (AC_PINCTL_* bits)
diff --git a/sound/hda/common/sysfs.c b/sound/hda/common/sysfs.c
index f8c8483fd5e5..bedf10b30885 100644
--- a/sound/hda/common/sysfs.c
+++ b/sound/hda/common/sysfs.c
@@ -299,7 +299,6 @@ static void remove_trail_spaces(char *str)
static int parse_hints(struct hda_codec *codec, const char *buf)
{
- char *key __free(kfree) = NULL;
char *val;
struct hda_hint *hint;
@@ -308,7 +307,9 @@ static int parse_hints(struct hda_codec *codec, const char *buf)
return 0;
if (*buf == '=')
return -EINVAL;
- key = kstrndup_noeol(buf, 1024);
+
+ char *key __free(kfree) =
+ kstrndup_noeol(buf, 1024);
if (!key)
return -ENOMEM;
/* extract key and val */
diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c
index a44de2306a2b..6fddf400c4a3 100644
--- a/sound/hda/controllers/intel.c
+++ b/sound/hda/controllers/intel.c
@@ -2551,11 +2551,12 @@ static const struct pci_device_id azx_ids[] = {
/* Wildcat Lake */
{ PCI_DEVICE_DATA(INTEL, HDA_WCL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) },
/* Nova Lake */
+ { PCI_DEVICE_DATA(INTEL, HDA_NVL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) },
{ PCI_DEVICE_DATA(INTEL, HDA_NVL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) },
/* Apollolake (Broxton-P) */
{ PCI_DEVICE_DATA(INTEL, HDA_APL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON) },
/* Gemini-Lake */
- { PCI_DEVICE_DATA(INTEL, HDA_GML, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON) },
+ { PCI_DEVICE_DATA(INTEL, HDA_GLK, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON) },
/* Haswell */
{ PCI_DEVICE_DATA(INTEL, HDA_HSW_0, AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL) },
{ PCI_DEVICE_DATA(INTEL, HDA_HSW_2, AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL) },
diff --git a/sound/hda/controllers/tegra.c b/sound/hda/controllers/tegra.c
index 6ab338f37db5..31c14c4bbe68 100644
--- a/sound/hda/controllers/tegra.c
+++ b/sound/hda/controllers/tegra.c
@@ -592,30 +592,26 @@ static void hda_tegra_probe_work(struct work_struct *work)
struct platform_device *pdev = to_platform_device(hda->dev);
int err;
- pm_runtime_get_sync(hda->dev);
+ guard(pm_runtime_active)(hda->dev);
err = hda_tegra_first_init(chip, pdev);
if (err < 0)
- goto out_free;
+ return;
/* create codec instances */
err = azx_probe_codecs(chip, 8);
if (err < 0)
- goto out_free;
+ return;
err = azx_codec_configure(chip);
if (err < 0)
- goto out_free;
+ return;
err = snd_card_register(chip->card);
if (err < 0)
- goto out_free;
+ return;
chip->running = 1;
snd_hda_set_power_save(&chip->bus, power_save * 1000);
-
- out_free:
- pm_runtime_put(hda->dev);
- return; /* no error return from async probe */
}
static void hda_tegra_remove(struct platform_device *pdev)
diff --git a/sound/hda/core/intel-dsp-config.c b/sound/hda/core/intel-dsp-config.c
index 0c25e87408de..f0a44fd111f3 100644
--- a/sound/hda/core/intel-dsp-config.c
+++ b/sound/hda/core/intel-dsp-config.c
@@ -154,7 +154,7 @@ static const struct config_entry config_table[] = {
#if IS_ENABLED(CONFIG_SND_SOC_SOF_GEMINILAKE)
{
.flags = FLAG_SOF,
- .device = PCI_DEVICE_ID_INTEL_HDA_GML,
+ .device = PCI_DEVICE_ID_INTEL_HDA_GLK,
.dmi_table = (const struct dmi_system_id []) {
{
.ident = "Google Chromebooks",
@@ -167,7 +167,7 @@ static const struct config_entry config_table[] = {
},
{
.flags = FLAG_SOF,
- .device = PCI_DEVICE_ID_INTEL_HDA_GML,
+ .device = PCI_DEVICE_ID_INTEL_HDA_GLK,
.codec_hid = &essx_83x6,
},
#endif
@@ -582,6 +582,10 @@ static const struct config_entry config_table[] = {
#if IS_ENABLED(CONFIG_SND_SOC_SOF_NOVALAKE)
{
.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_NVL,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
.device = PCI_DEVICE_ID_INTEL_HDA_NVL_S,
},
#endif
diff --git a/sound/isa/sb/emu8000_synth.c b/sound/isa/sb/emu8000_synth.c
index 9bec85ec55b4..3414c6d0695f 100644
--- a/sound/isa/sb/emu8000_synth.c
+++ b/sound/isa/sb/emu8000_synth.c
@@ -21,9 +21,8 @@ MODULE_LICENSE("GPL");
/*
* create a new hardware dependent device for Emu8000
*/
-static int snd_emu8000_probe(struct device *_dev)
+static int snd_emu8000_probe(struct snd_seq_device *dev)
{
- struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_emu8000 *hw;
struct snd_emux *emu;
@@ -81,13 +80,12 @@ static int snd_emu8000_probe(struct device *_dev)
/*
* free all resources
*/
-static int snd_emu8000_remove(struct device *_dev)
+static void snd_emu8000_remove(struct snd_seq_device *dev)
{
- struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_emu8000 *hw;
if (dev->driver_data == NULL)
- return 0; /* no synth was allocated actually */
+ return; /* no synth was allocated actually */
hw = dev->driver_data;
if (hw->pcm)
@@ -96,7 +94,6 @@ static int snd_emu8000_remove(struct device *_dev)
snd_util_memhdr_free(hw->memhdr);
hw->emu = NULL;
hw->memhdr = NULL;
- return 0;
}
/*
@@ -104,10 +101,10 @@ static int snd_emu8000_remove(struct device *_dev)
*/
static struct snd_seq_driver emu8000_driver = {
+ .probe = snd_emu8000_probe,
+ .remove = snd_emu8000_remove,
.driver = {
.name = KBUILD_MODNAME,
- .probe = snd_emu8000_probe,
- .remove = snd_emu8000_remove,
},
.id = SNDRV_SEQ_DEV_ID_EMU8000,
.argsize = sizeof(struct snd_emu8000 *),
diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c
index 227d8c8490e1..a25a599fc5be 100644
--- a/sound/pci/ctxfi/ctatc.c
+++ b/sound/pci/ctxfi/ctatc.c
@@ -52,18 +52,19 @@ static const struct snd_pci_quirk subsys_20k1_list[] = {
static const struct snd_pci_quirk subsys_20k2_list[] = {
SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB0760,
"SB0760", CTSB0760),
- SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB1270,
- "SB1270", CTSB1270),
SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08801,
"SB0880", CTSB0880),
SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08802,
"SB0880", CTSB0880),
SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08803,
"SB0880", CTSB0880),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB1270,
+ "SB1270", CTSB1270),
+ SND_PCI_QUIRK(0x160b, 0x0101, "OK0010", CTOK0010),
+ SND_PCI_QUIRK(0x160b, 0x0102, "OK0010", CTOK0010),
SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_CREATIVE, 0xf000,
PCI_SUBDEVICE_ID_CREATIVE_HENDRIX, "HENDRIX",
CTHENDRIX),
- SND_PCI_QUIRK(0x160b, 0x0101, "OK0010", CTOK0010),
{ } /* terminator */
};
@@ -78,8 +79,8 @@ static const char *ct_subsys_name[NUM_CTCARDS] = {
[CTSB0760] = "SB076x",
[CTHENDRIX] = "Hendrix",
[CTSB0880] = "SB0880",
- [CTSB1270] = "SB1270",
- [CTOK0010] = "OK0010",
+ [CTSB1270] = "SB1270",
+ [CTOK0010] = "OK0010",
[CT20K2_UNKNOWN] = "Unknown",
};
diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c
index f2c8602a1ad7..8b7b6838106f 100644
--- a/sound/pci/echoaudio/echoaudio.c
+++ b/sound/pci/echoaudio/echoaudio.c
@@ -2159,7 +2159,7 @@ static int snd_echo_resume(struct device *dev)
*/
pipe_alloc_mask = chip->pipe_alloc_mask;
chip->pipe_alloc_mask = 0;
- err = restore_dsp_rettings(chip);
+ err = restore_dsp_settings(chip);
chip->pipe_alloc_mask = pipe_alloc_mask;
if (err < 0) {
kfree(commpage_bak);
diff --git a/sound/pci/echoaudio/echoaudio_dsp.c b/sound/pci/echoaudio/echoaudio_dsp.c
index 2a40091d472c..6e872103305b 100644
--- a/sound/pci/echoaudio/echoaudio_dsp.c
+++ b/sound/pci/echoaudio/echoaudio_dsp.c
@@ -32,7 +32,7 @@
#error PAGE_SIZE is < 4k
#endif
-static int restore_dsp_rettings(struct echoaudio *chip);
+static int restore_dsp_settings(struct echoaudio *chip);
/* Some vector commands involve the DSP reading or writing data to and from the
@@ -666,7 +666,7 @@ static void get_audio_meters(struct echoaudio *chip, long *meters)
-static int restore_dsp_rettings(struct echoaudio *chip)
+static int restore_dsp_settings(struct echoaudio *chip)
{
int i, o, err;
@@ -1014,7 +1014,7 @@ static int init_line_levels(struct echoaudio *chip)
chip->input_clock = ECHO_CLOCK_INTERNAL;
chip->output_clock = ECHO_CLOCK_WORD;
chip->sample_rate = 44100;
- return restore_dsp_rettings(chip);
+ return restore_dsp_settings(chip);
}
diff --git a/sound/pci/emu10k1/emu10k1_synth.c b/sound/pci/emu10k1/emu10k1_synth.c
index 662d20eb9689..26499c31eaa8 100644
--- a/sound/pci/emu10k1/emu10k1_synth.c
+++ b/sound/pci/emu10k1/emu10k1_synth.c
@@ -16,9 +16,8 @@ MODULE_LICENSE("GPL");
/*
* create a new hardware dependent device for Emu10k1
*/
-static int snd_emu10k1_synth_probe(struct device *_dev)
+static int snd_emu10k1_synth_probe(struct snd_seq_device *dev)
{
- struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_emux *emux;
struct snd_emu10k1 *hw;
struct snd_emu10k1_synth_arg *arg;
@@ -64,14 +63,13 @@ static int snd_emu10k1_synth_probe(struct device *_dev)
return 0;
}
-static int snd_emu10k1_synth_remove(struct device *_dev)
+static void snd_emu10k1_synth_remove(struct snd_seq_device *dev)
{
- struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_emux *emux;
struct snd_emu10k1 *hw;
if (dev->driver_data == NULL)
- return 0; /* not registered actually */
+ return; /* not registered actually */
emux = dev->driver_data;
@@ -82,7 +80,6 @@ static int snd_emu10k1_synth_remove(struct device *_dev)
}
snd_emux_free(emux);
- return 0;
}
/*
@@ -90,10 +87,10 @@ static int snd_emu10k1_synth_remove(struct device *_dev)
*/
static struct snd_seq_driver emu10k1_synth_driver = {
+ .probe = snd_emu10k1_synth_probe,
+ .remove = snd_emu10k1_synth_remove,
.driver = {
.name = KBUILD_MODNAME,
- .probe = snd_emu10k1_synth_probe,
- .remove = snd_emu10k1_synth_remove,
},
.id = SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH,
.argsize = sizeof(struct snd_emu10k1_synth_arg),
diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c
index e2dbbbfbca9f..65bf48647d08 100644
--- a/sound/pci/ice1712/ice1724.c
+++ b/sound/pci/ice1712/ice1724.c
@@ -973,7 +973,7 @@ static int set_rate_constraints(struct snd_ice1712 *ice,
ice->hw_rates);
}
-/* if the card has the internal rate locked (is_pro_locked), limit runtime
+/* if the card has the internal rate locked (is_pro_rate_locked), limit runtime
hw rates to the current internal rate only.
*/
static void constrain_rate_if_locked(struct snd_pcm_substream *substream)
diff --git a/sound/pci/mixart/mixart.h b/sound/pci/mixart/mixart.h
index cbed6d9a9f2e..3fcc5ca757c1 100644
--- a/sound/pci/mixart/mixart.h
+++ b/sound/pci/mixart/mixart.h
@@ -125,7 +125,7 @@ struct mixart_stream {
u64 abs_period_elapsed; /* last absolute stream position where period_elapsed was called (multiple of runtime->period_size) */
u32 buf_periods; /* periods counter in the buffer (< runtime->periods) */
- u32 buf_period_frag; /* defines with buf_period_pos the exact position in the buffer (< runtime->period_size) */
+ u32 buf_period_frag; /* defines with buf_periods the exact position in the buffer (< runtime->period_size) */
int channels;
};
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 36e0d443ba0e..edfdcbf734fe 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -127,6 +127,7 @@ source "sound/soc/renesas/Kconfig"
source "sound/soc/rockchip/Kconfig"
source "sound/soc/samsung/Kconfig"
source "sound/soc/sdca/Kconfig"
+source "sound/soc/sophgo/Kconfig"
source "sound/soc/spacemit/Kconfig"
source "sound/soc/spear/Kconfig"
source "sound/soc/sprd/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 8c0480e6484e..21d8406767fc 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -70,6 +70,7 @@ obj-$(CONFIG_SND_SOC) += rockchip/
obj-$(CONFIG_SND_SOC) += samsung/
obj-$(CONFIG_SND_SOC) += sdca/
obj-$(CONFIG_SND_SOC) += sof/
+obj-$(CONFIG_SND_SOC) += sophgo/
obj-$(CONFIG_SND_SOC) += spacemit/
obj-$(CONFIG_SND_SOC) += spear/
obj-$(CONFIG_SND_SOC) += sprd/
diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig
index fd35a03aadcb..f7c3010624df 100644
--- a/sound/soc/amd/Kconfig
+++ b/sound/soc/amd/Kconfig
@@ -8,7 +8,6 @@ config SND_SOC_AMD_ACP
config SND_SOC_AMD_CZ_DA7219MX98357_MACH
tristate "AMD CZ support for DA7219, RT5682 and MAX9835"
- select CLK_FIXED_FCH
select SND_SOC_DA7219
select SND_SOC_RT5682_I2C
select SND_SOC_MAX98357A
@@ -45,7 +44,6 @@ config SND_SOC_AMD_ACP3x
config SND_SOC_AMD_RV_RT5682_MACH
tristate "AMD RV support for RT5682"
- select CLK_FIXED_FCH
select SND_SOC_RT5682_I2C
select SND_SOC_MAX98357A
select SND_SOC_CROS_EC_CODEC
diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig
index c2a60bc80ee6..977e4f2a7a70 100644
--- a/sound/soc/amd/acp/Kconfig
+++ b/sound/soc/amd/acp/Kconfig
@@ -15,7 +15,13 @@ config SND_SOC_AMD_ACP_COMMON
config SND_SOC_ACPI_AMD_MATCH
tristate
- select SND_SOC_ACPI if ACPI
+ select SND_SOC_ACPI_AMD_SDCA_QUIRKS if SND_SOC_SDCA
+ select SND_SOC_ACPI if ACPI
+
+config SND_SOC_ACPI_AMD_SDCA_QUIRKS
+ tristate
+ depends on ACPI
+ depends on SND_SOC_SDCA
if SND_SOC_AMD_ACP_COMMON
@@ -104,7 +110,6 @@ config SND_AMD_ASOC_ACP70
config SND_SOC_AMD_MACH_COMMON
tristate
depends on X86 && PCI && I2C
- select CLK_FIXED_FCH
select SND_SOC_RT5682_I2C
select SND_SOC_DMIC
select SND_SOC_RT1019
diff --git a/sound/soc/amd/acp/Makefile b/sound/soc/amd/acp/Makefile
index 08220b9a3802..81d23aded348 100644
--- a/sound/soc/amd/acp/Makefile
+++ b/sound/soc/amd/acp/Makefile
@@ -27,6 +27,7 @@ snd-soc-acpi-amd-match-y := amd-acp63-acpi-match.o amd-acp70-acpi-match.o
snd-acp-sdw-mach-y := acp-sdw-mach-common.o
snd-acp-sdw-sof-mach-y += acp-sdw-sof-mach.o
snd-acp-sdw-legacy-mach-y += acp-sdw-legacy-mach.o
+snd-soc-acpi-amd-sdca-quirks-y += soc-acpi-amd-sdca-quirks.o
obj-$(CONFIG_SND_SOC_AMD_ACP_PCM) += snd-acp-pcm.o
obj-$(CONFIG_SND_SOC_AMD_ACP_I2S) += snd-acp-i2s.o
@@ -40,6 +41,7 @@ obj-$(CONFIG_SND_AMD_ASOC_REMBRANDT) += snd-acp-rembrandt.o
obj-$(CONFIG_SND_AMD_ASOC_ACP63) += snd-acp63.o
obj-$(CONFIG_SND_AMD_ASOC_ACP70) += snd-acp70.o
+obj-$(CONFIG_SND_SOC_ACPI_AMD_SDCA_QUIRKS) += snd-soc-acpi-amd-sdca-quirks.o
obj-$(CONFIG_SND_AMD_SOUNDWIRE_ACPI) += snd-amd-sdw-acpi.o
obj-$(CONFIG_SND_SOC_AMD_MACH_COMMON) += snd-acp-mach.o
obj-$(CONFIG_SND_SOC_AMD_LEGACY_MACH) += snd-acp-legacy-mach.o
diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c
index fae94b9edd5a..9cb55d592c3c 100644
--- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c
+++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c
@@ -358,8 +358,6 @@ static int soc_card_dai_links_create(struct snd_soc_card *card)
int sdw_be_num = 0, dmic_num = 0;
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
- struct asoc_sdw_endpoint *soc_ends __free(kfree) = NULL;
- struct asoc_sdw_dailink *soc_dais __free(kfree) = NULL;
struct snd_soc_aux_dev *soc_aux;
struct snd_soc_codec_conf *codec_conf;
struct snd_soc_dai_link *dai_links;
@@ -380,12 +378,14 @@ static int soc_card_dai_links_create(struct snd_soc_card *card)
num_confs = num_ends;
/* One per DAI link, worst case is a DAI link for every endpoint */
- soc_dais = kcalloc(num_ends, sizeof(*soc_dais), GFP_KERNEL);
+ struct asoc_sdw_dailink *soc_dais __free(kfree) =
+ kcalloc(num_ends, sizeof(*soc_dais), GFP_KERNEL);
if (!soc_dais)
return -ENOMEM;
/* One per endpoint, ie. each DAI on each codec/amp */
- soc_ends = kcalloc(num_ends, sizeof(*soc_ends), GFP_KERNEL);
+ struct asoc_sdw_endpoint *soc_ends __free(kfree) =
+ kcalloc(num_ends, sizeof(*soc_ends), GFP_KERNEL);
if (!soc_ends)
return -ENOMEM;
diff --git a/sound/soc/amd/acp/acp-sdw-sof-mach.c b/sound/soc/amd/acp/acp-sdw-sof-mach.c
index 5677ae63fca9..da815b3f6389 100644
--- a/sound/soc/amd/acp/acp-sdw-sof-mach.c
+++ b/sound/soc/amd/acp/acp-sdw-sof-mach.c
@@ -270,8 +270,6 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)
int sdw_be_num = 0, dmic_num = 0;
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
- struct asoc_sdw_endpoint *sof_ends __free(kfree) = NULL;
- struct asoc_sdw_dailink *sof_dais __free(kfree) = NULL;
struct snd_soc_aux_dev *sof_aux;
struct snd_soc_codec_conf *codec_conf;
struct snd_soc_dai_link *dai_links;
@@ -289,12 +287,14 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)
}
/* One per DAI link, worst case is a DAI link for every endpoint */
- sof_dais = kcalloc(num_ends, sizeof(*sof_dais), GFP_KERNEL);
+ struct asoc_sdw_dailink *sof_dais __free(kfree) =
+ kcalloc(num_ends, sizeof(*sof_dais), GFP_KERNEL);
if (!sof_dais)
return -ENOMEM;
/* One per endpoint, ie. each DAI on each codec/amp */
- sof_ends = kcalloc(num_ends, sizeof(*sof_ends), GFP_KERNEL);
+ struct asoc_sdw_endpoint *sof_ends __free(kfree) =
+ kcalloc(num_ends, sizeof(*sof_ends), GFP_KERNEL);
if (!sof_ends)
return -ENOMEM;
diff --git a/sound/soc/amd/acp/amd-acp70-acpi-match.c b/sound/soc/amd/acp/amd-acp70-acpi-match.c
index 871b4f054a84..dd2b010efdaa 100644
--- a/sound/soc/amd/acp/amd-acp70-acpi-match.c
+++ b/sound/soc/amd/acp/amd-acp70-acpi-match.c
@@ -7,6 +7,7 @@
*/
#include <sound/soc-acpi.h>
+#include "soc-acpi-amd-sdca-quirks.h"
#include "../mach-config.h"
static const struct snd_soc_acpi_endpoint single_endpoint = {
@@ -44,6 +45,39 @@ static const struct snd_soc_acpi_endpoint spk_3_endpoint = {
.group_id = 1
};
+static const struct snd_soc_acpi_endpoint jack_amp_g1_dmic_endpoints[] = {
+ /* Jack Endpoint */
+ {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ /* Amp Endpoint, work as spk_l_endpoint */
+ {
+ .num = 1,
+ .aggregated = 1,
+ .group_position = 0,
+ .group_id = 1,
+ },
+ /* DMIC Endpoint */
+ {
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+static const struct snd_soc_acpi_adr_device rt712_vb_1_group1_adr[] = {
+ {
+ .adr = 0x000130025D071201ull,
+ .num_endpoints = ARRAY_SIZE(jack_amp_g1_dmic_endpoints),
+ .endpoints = jack_amp_g1_dmic_endpoints,
+ .name_prefix = "rt712"
+ }
+};
+
static const struct snd_soc_acpi_adr_device rt711_rt1316_group_adr[] = {
{
.adr = 0x000030025D071101ull,
@@ -153,7 +187,106 @@ static const struct snd_soc_acpi_endpoint cs42l43_endpoints[] = {
},
};
-static const struct snd_soc_acpi_adr_device cs42l43_0_adr[] = {
+static const struct snd_soc_acpi_adr_device cs35l56x4_l1u3210_adr[] = {
+ {
+ .adr = 0x00013301FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "AMP1"
+ },
+ {
+ .adr = 0x00013201FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "AMP2"
+ },
+ {
+ .adr = 0x00013101FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_2_endpoint,
+ .name_prefix = "AMP3"
+ },
+ {
+ .adr = 0x00013001FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_3_endpoint,
+ .name_prefix = "AMP4"
+ },
+};
+
+static const struct snd_soc_acpi_adr_device cs35l63x2_l0u01_adr[] = {
+ {
+ .adr = 0x00003001FA356301ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "AMP1"
+ },
+ {
+ .adr = 0x00003101FA356301ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "AMP2"
+ },
+};
+
+static const struct snd_soc_acpi_adr_device cs35l63x2_l1u01_adr[] = {
+ {
+ .adr = 0x00013001FA356301ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "AMP1"
+ },
+ {
+ .adr = 0x00013101FA356301ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "AMP2"
+ },
+};
+
+static const struct snd_soc_acpi_adr_device cs35l63x2_l1u13_adr[] = {
+ {
+ .adr = 0x00013101FA356301ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "AMP1"
+ },
+ {
+ .adr = 0x00013301FA356301ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "AMP2"
+ },
+};
+
+static const struct snd_soc_acpi_adr_device cs35l63x4_l0u0246_adr[] = {
+ {
+ .adr = 0x00003001FA356301ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "AMP1"
+ },
+ {
+ .adr = 0x00003201FA356301ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "AMP2"
+ },
+ {
+ .adr = 0x00003401FA356301ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_2_endpoint,
+ .name_prefix = "AMP3"
+ },
+ {
+ .adr = 0x00003601FA356301ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_3_endpoint,
+ .name_prefix = "AMP4"
+ },
+};
+
+static const struct snd_soc_acpi_adr_device cs42l43_l0u0_adr[] = {
{
.adr = 0x00003001FA424301ull,
.num_endpoints = ARRAY_SIZE(cs42l43_endpoints),
@@ -162,7 +295,25 @@ static const struct snd_soc_acpi_adr_device cs42l43_0_adr[] = {
}
};
-static const struct snd_soc_acpi_adr_device cs42l43_1_cs35l56x4_1_adr[] = {
+static const struct snd_soc_acpi_adr_device cs42l43_l0u1_adr[] = {
+ {
+ .adr = 0x00003101FA424301ull,
+ .num_endpoints = ARRAY_SIZE(cs42l43_endpoints),
+ .endpoints = cs42l43_endpoints,
+ .name_prefix = "cs42l43"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs42l43b_l0u1_adr[] = {
+ {
+ .adr = 0x00003101FA2A3B01ull,
+ .num_endpoints = ARRAY_SIZE(cs42l43_endpoints),
+ .endpoints = cs42l43_endpoints,
+ .name_prefix = "cs42l43"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs42l43_l1u0_cs35l56x4_l1u0123_adr[] = {
{
.adr = 0x00013001FA424301ull,
.num_endpoints = ARRAY_SIZE(cs42l43_endpoints),
@@ -195,61 +346,164 @@ static const struct snd_soc_acpi_adr_device cs42l43_1_cs35l56x4_1_adr[] = {
},
};
-static const struct snd_soc_acpi_adr_device cs35l56x4_1_adr[] = {
+static const struct snd_soc_acpi_adr_device cs42l45_l0u0_adr[] = {
{
- .adr = 0x00013301FA355601ull,
- .num_endpoints = 1,
- .endpoints = &spk_l_endpoint,
- .name_prefix = "AMP1"
+ .adr = 0x00003001FA424501ull,
+ /* Re-use endpoints, but cs42l45 has no speaker */
+ .num_endpoints = ARRAY_SIZE(cs42l43_endpoints) - 1,
+ .endpoints = cs42l43_endpoints,
+ .name_prefix = "cs42l45"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs42l45_l1u0_adr[] = {
+ {
+ .adr = 0x00013001FA424501ull,
+ /* Re-use endpoints, but cs42l45 has no speaker */
+ .num_endpoints = ARRAY_SIZE(cs42l43_endpoints) - 1,
+ .endpoints = cs42l43_endpoints,
+ .name_prefix = "cs42l45"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr acp70_cs35l56x4_l1u3210[] = {
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(cs35l56x4_l1u3210_adr),
+ .adr_d = cs35l56x4_l1u3210_adr,
},
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr acp70_cs35l63x4_l0u0246[] = {
{
- .adr = 0x00013201FA355601ull,
- .num_endpoints = 1,
- .endpoints = &spk_r_endpoint,
- .name_prefix = "AMP2"
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs35l63x4_l0u0246_adr),
+ .adr_d = cs35l63x4_l0u0246_adr,
},
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr acp70_cs42l43_l0u1[] = {
{
- .adr = 0x00013101FA355601ull,
- .num_endpoints = 1,
- .endpoints = &spk_2_endpoint,
- .name_prefix = "AMP3"
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs42l43_l0u1_adr),
+ .adr_d = cs42l43_l0u1_adr,
},
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr acp70_cs42l43b_l0u1[] = {
{
- .adr = 0x00013001FA355601ull,
- .num_endpoints = 1,
- .endpoints = &spk_3_endpoint,
- .name_prefix = "AMP4"
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs42l43b_l0u1_adr),
+ .adr_d = cs42l43b_l0u1_adr,
},
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr acp70_cs42l43_l0u0_cs35l56x4_l1u3210[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs42l43_l0u0_adr),
+ .adr_d = cs42l43_l0u0_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(cs35l56x4_l1u3210_adr),
+ .adr_d = cs35l56x4_l1u3210_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr acp70_cs42l43_l1u0_cs35l56x4_l1u0123[] = {
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(cs42l43_l1u0_cs35l56x4_l1u0123_adr),
+ .adr_d = cs42l43_l1u0_cs35l56x4_l1u0123_adr,
+ },
+ {}
};
-static const struct snd_soc_acpi_link_adr acp70_cs42l43_l1_cs35l56x4_l1[] = {
+static const struct snd_soc_acpi_link_adr acp70_cs42l45_l0u0[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs42l45_l0u0_adr),
+ .adr_d = cs42l45_l0u0_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr acp70_cs42l45_l0u0_cs35l63x2_l1u01[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs42l45_l0u0_adr),
+ .adr_d = cs42l45_l0u0_adr,
+ },
{
.mask = BIT(1),
- .num_adr = ARRAY_SIZE(cs42l43_1_cs35l56x4_1_adr),
- .adr_d = cs42l43_1_cs35l56x4_1_adr,
+ .num_adr = ARRAY_SIZE(cs35l63x2_l1u01_adr),
+ .adr_d = cs35l63x2_l1u01_adr,
},
{}
};
-static const struct snd_soc_acpi_link_adr acp70_cs42l43_l0_cs35l56x4_l1[] = {
+static const struct snd_soc_acpi_link_adr acp70_cs42l45_l0u0_cs35l63x2_l1u13[] = {
{
.mask = BIT(0),
- .num_adr = ARRAY_SIZE(cs42l43_0_adr),
- .adr_d = cs42l43_0_adr,
+ .num_adr = ARRAY_SIZE(cs42l45_l0u0_adr),
+ .adr_d = cs42l45_l0u0_adr,
},
{
.mask = BIT(1),
- .num_adr = ARRAY_SIZE(cs35l56x4_1_adr),
- .adr_d = cs35l56x4_1_adr,
+ .num_adr = ARRAY_SIZE(cs35l63x2_l1u13_adr),
+ .adr_d = cs35l63x2_l1u13_adr,
},
{}
};
-static const struct snd_soc_acpi_link_adr acp70_cs35l56x4_l1[] = {
+static const struct snd_soc_acpi_link_adr acp70_cs42l45_l1u0[] = {
{
.mask = BIT(1),
- .num_adr = ARRAY_SIZE(cs35l56x4_1_adr),
- .adr_d = cs35l56x4_1_adr,
+ .num_adr = ARRAY_SIZE(cs42l45_l1u0_adr),
+ .adr_d = cs42l45_l1u0_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr acp70_cs42l45_l1u0_cs35l63x2_l0u01[] = {
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(cs42l45_l1u0_adr),
+ .adr_d = cs42l45_l1u0_adr,
+ },
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs35l63x2_l0u01_adr),
+ .adr_d = cs35l63x2_l0u01_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr acp70_cs42l45_l1u0_cs35l63x4_l0u0246[] = {
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(cs42l45_l1u0_adr),
+ .adr_d = cs42l45_l1u0_adr,
+ },
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs35l63x4_l0u0246_adr),
+ .adr_d = cs35l63x4_l0u0246_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr acp70_alc712_vb_l1[] = {
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt712_vb_1_group1_adr),
+ .adr_d = rt712_vb_1_group1_adr,
},
{}
};
@@ -284,28 +538,79 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[] = {
.drv_name = "amd_sdw",
},
{
- .link_mask = BIT(0),
- .links = acp70_rt722_only,
+ .link_mask = BIT(0) | BIT(1),
+ .links = acp70_4_in_1_sdca,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(0) | BIT(1),
- .links = acp70_4_in_1_sdca,
+ .links = acp70_cs42l43_l0u0_cs35l56x4_l1u3210,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(0) | BIT(1),
+ .links = acp70_cs42l45_l1u0_cs35l63x4_l0u0246,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(0) | BIT(1),
+ .links = acp70_cs42l45_l0u0_cs35l63x2_l1u01,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(0) | BIT(1),
- .links = acp70_cs42l43_l0_cs35l56x4_l1,
+ .links = acp70_cs42l45_l0u0_cs35l63x2_l1u13,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(0) | BIT(1),
+ .links = acp70_cs42l45_l1u0_cs35l63x2_l0u01,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(1),
+ .links = acp70_cs42l43_l1u0_cs35l56x4_l1u0123,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(1),
+ .links = acp70_cs35l56x4_l1u3210,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(0),
+ .links = acp70_cs35l63x4_l0u0246,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(0),
+ .links = acp70_rt722_only,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(0),
+ .links = acp70_cs42l43_l0u1,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(0),
+ .links = acp70_cs42l43b_l0u1,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(0),
+ .links = acp70_cs42l45_l0u0,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(1),
- .links = acp70_cs42l43_l1_cs35l56x4_l1,
+ .links = acp70_cs42l45_l1u0,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(1),
- .links = acp70_cs35l56x4_l1,
+ .links = acp70_alc712_vb_l1,
+ .machine_check = snd_soc_acpi_amd_sdca_is_device_rt712_vb,
.drv_name = "amd_sdw",
},
{},
@@ -327,3 +632,6 @@ EXPORT_SYMBOL(snd_soc_acpi_amd_acp70_sof_sdw_machines);
MODULE_DESCRIPTION("AMD ACP7.0 & ACP7.1 tables and support for ACPI enumeration");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+#if IS_ENABLED(CONFIG_SND_SOC_ACPI_AMD_SDCA_QUIRKS)
+MODULE_IMPORT_NS("SND_SOC_ACPI_AMD_SDCA_QUIRKS");
+#endif
diff --git a/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.c b/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.c
new file mode 100644
index 000000000000..63bf9e3c0ae1
--- /dev/null
+++ b/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * soc-acpi-amd-sdca-quirks.c - tables and support for SDCA quirks
+ *
+ * Copyright(c) 2025 Advanced Micro Devices, Inc. All rights reserved.
+ *
+ */
+
+#include <linux/soundwire/sdw_amd.h>
+#include <sound/sdca.h>
+#include <sound/soc-acpi.h>
+#include "soc-acpi-amd-sdca-quirks.h"
+
+/*
+ * Pretend machine quirk. The argument type is not the traditional
+ * 'struct snd_soc_acpi_mach' pointer but instead the sdw_amd_ctx
+ * which contains the peripheral information required for the
+ * SoundWire/SDCA filter on the SMART_MIC setup and interface
+ * revision. When the return value is false, the entry in the
+ * 'snd_soc_acpi_mach' table needs to be skipped.
+ */
+bool snd_soc_acpi_amd_sdca_is_device_rt712_vb(void *arg)
+{
+ struct sdw_amd_ctx *ctx = arg;
+ int i;
+
+ if (!ctx)
+ return false;
+
+ for (i = 0; i < ctx->peripherals->num_peripherals; i++) {
+ if (sdca_device_quirk_match(ctx->peripherals->array[i],
+ SDCA_QUIRKS_RT712_VB))
+ return true;
+ }
+
+ return false;
+}
+EXPORT_SYMBOL_NS(snd_soc_acpi_amd_sdca_is_device_rt712_vb, "SND_SOC_ACPI_AMD_SDCA_QUIRKS");
+
+MODULE_DESCRIPTION("ASoC ACPI AMD SDCA quirks");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("SND_SOC_SDCA");
diff --git a/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.h b/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.h
new file mode 100644
index 000000000000..0e644e71e76f
--- /dev/null
+++ b/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * soc-acpi-amd-sdca-quirks.h - tables and support for SDCA quirks
+ *
+ * Copyright(c) 2025 Advanced Micro Devices, Inc. All rights reserved.
+ *
+ */
+
+#ifndef _SND_SOC_ACPI_AMD_SDCA_QUIRKS
+#define _SND_SOC_ACPI_AMD_SDCA_QUIRKS
+
+#if IS_ENABLED(CONFIG_SND_SOC_ACPI_AMD_SDCA_QUIRKS)
+
+bool snd_soc_acpi_amd_sdca_is_device_rt712_vb(void *arg);
+
+#else
+
+static inline bool snd_soc_acpi_amd_sdca_is_device_rt712_vb(void *arg)
+{
+ return false;
+}
+
+#endif
+
+#endif
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 061791e61907..adb3fb923be3 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -46,7 +46,6 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_AK4554
imply SND_SOC_AK4613
imply SND_SOC_AK4619
- imply SND_SOC_AK4641
imply SND_SOC_AK4642
imply SND_SOC_AK4671
imply SND_SOC_AK5386
@@ -212,6 +211,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_RT1305
imply SND_SOC_RT1308
imply SND_SOC_RT5514
+ imply SND_SOC_RT5575
imply SND_SOC_RT5616
imply SND_SOC_RT5631
imply SND_SOC_RT5640
@@ -624,11 +624,6 @@ config SND_SOC_AK4619
tristate "AKM AK4619 CODEC"
depends on I2C
-config SND_SOC_AK4641
- tristate
- depends on I2C
- depends on GPIOLIB_LEGACY
-
config SND_SOC_AK4642
tristate "AKM AK4642 CODEC"
depends on I2C
@@ -785,9 +780,14 @@ config SND_SOC_CROS_EC_CODEC
config SND_SOC_CS_AMP_LIB
tristate
+config SND_SOC_CS_AMP_LIB_TEST_HOOKS
+ bool
+ depends on SND_SOC_CS_AMP_LIB
+
config SND_SOC_CS_AMP_LIB_TEST
tristate "KUnit test for Cirrus Logic cs-amp-lib" if !KUNIT_ALL_TESTS
depends on SND_SOC_CS_AMP_LIB && KUNIT
+ select SND_SOC_CS_AMP_LIB_TEST_HOOKS
default KUNIT_ALL_TESTS
help
This builds KUnit tests for the Cirrus Logic common
@@ -927,6 +927,33 @@ config SND_SOC_CS35L56_CAL_SET_CTRL
On most platforms this is not needed.
If unsure select "N".
+
+config SND_SOC_CS35L56_TEST
+ tristate "KUnit test for Cirrus Logic cs35l56 driver" if !KUNIT_ALL_TESTS
+ depends on SND_SOC_CS35L56 && KUNIT
+ default KUNIT_ALL_TESTS
+ select SND_SOC_CS_AMP_LIB_TEST_HOOKS
+ help
+ This builds KUnit tests for the Cirrus Logic cs35l56
+ codec driver.
+
+ For more information on KUnit and unit tests in general,
+ please refer to the KUnit documentation in
+ Documentation/dev-tools/kunit/.
+ If in doubt, say "N".
+
+config SND_SOC_CS35L56_SHARED_TEST
+ tristate "KUnit test for Cirrus Logic cs35l56-shared" if !KUNIT_ALL_TESTS
+ depends on SND_SOC_CS35L56_SHARED && KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ This builds KUnit tests for the Cirrus Logic cs35l56-shared
+ module.
+
+ For more information on KUnit and unit tests in general,
+ please refer to the KUnit documentation in
+ Documentation/dev-tools/kunit/.
+ If in doubt, say "N".
endmenu
config SND_SOC_CS40L50
@@ -1783,6 +1810,16 @@ config SND_SOC_RT5514_SPI_BUILTIN
bool # force RT5514_SPI to be built-in to avoid link errors
default SND_SOC_RT5514=y && SND_SOC_RT5514_SPI=m
+config SND_SOC_RT5575
+ tristate "Realtek ALC5575 Codec - I2C"
+ depends on I2C
+
+config SND_SOC_RT5575_SPI
+ bool "Realtek ALC5575 Codec - SPI"
+ depends on SPI_MASTER && I2C
+ depends on SND_SOC_RT5575
+ depends on SPI_MASTER=y || SND_SOC_RT5575=m
+
config SND_SOC_RT5616
tristate "Realtek RT5616 CODEC"
depends on I2C
@@ -2146,6 +2183,7 @@ config SND_SOC_TAS2781_I2C
config SND_SOC_TAS2783_SDW
tristate "Texas Instruments TAS2783 speaker amplifier (sdw)"
depends on SOUNDWIRE
+ depends on SND_SOC_SDCA
depends on EFI
select REGMAP_SOUNDWIRE
select REGMAP_SOUNDWIRE_MBQ
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index d687d4f74363..3ddee5298721 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -40,7 +40,6 @@ snd-soc-ak4535-y := ak4535.o
snd-soc-ak4554-y := ak4554.o
snd-soc-ak4613-y := ak4613.o
snd-soc-ak4619-y := ak4619.o
-snd-soc-ak4641-y := ak4641.o
snd-soc-ak4642-y := ak4642.o
snd-soc-ak4671-y := ak4671.o
snd-soc-ak5386-y := ak5386.o
@@ -78,9 +77,11 @@ snd-soc-cs35l45-spi-y := cs35l45-spi.o
snd-soc-cs35l45-i2c-y := cs35l45-i2c.o
snd-soc-cs35l56-y := cs35l56.o
snd-soc-cs35l56-shared-y := cs35l56-shared.o
+snd-soc-cs35l56-shared-test-y := cs35l56-shared-test.o
snd-soc-cs35l56-i2c-y := cs35l56-i2c.o
snd-soc-cs35l56-spi-y := cs35l56-spi.o
snd-soc-cs35l56-sdw-y := cs35l56-sdw.o
+snd-soc-cs35l56-test-y := cs35l56-test.o
snd-soc-cs40l50-y := cs40l50-codec.o
snd-soc-cs42l42-y := cs42l42.o
snd-soc-cs42l42-i2c-y := cs42l42-i2c.o
@@ -253,6 +254,8 @@ snd-soc-rt286-y := rt286.o
snd-soc-rt298-y := rt298.o
snd-soc-rt5514-y := rt5514.o
snd-soc-rt5514-spi-y := rt5514-spi.o
+snd-soc-rt5575-y := rt5575.o
+snd-soc-rt5575-$(CONFIG_SND_SOC_RT5575_SPI) += rt5575-spi.o
snd-soc-rt5616-y := rt5616.o
snd-soc-rt5631-y := rt5631.o
snd-soc-rt5640-y := rt5640.o
@@ -472,7 +475,6 @@ obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
obj-$(CONFIG_SND_SOC_AK4554) += snd-soc-ak4554.o
obj-$(CONFIG_SND_SOC_AK4613) += snd-soc-ak4613.o
obj-$(CONFIG_SND_SOC_AK4619) += snd-soc-ak4619.o
-obj-$(CONFIG_SND_SOC_AK4641) += snd-soc-ak4641.o
obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o
obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o
obj-$(CONFIG_SND_SOC_AK5386) += snd-soc-ak5386.o
@@ -511,9 +513,11 @@ obj-$(CONFIG_SND_SOC_CS35L45_SPI) += snd-soc-cs35l45-spi.o
obj-$(CONFIG_SND_SOC_CS35L45_I2C) += snd-soc-cs35l45-i2c.o
obj-$(CONFIG_SND_SOC_CS35L56) += snd-soc-cs35l56.o
obj-$(CONFIG_SND_SOC_CS35L56_SHARED) += snd-soc-cs35l56-shared.o
+obj-$(CONFIG_SND_SOC_CS35L56_SHARED_TEST) += snd-soc-cs35l56-shared-test.o
obj-$(CONFIG_SND_SOC_CS35L56_I2C) += snd-soc-cs35l56-i2c.o
obj-$(CONFIG_SND_SOC_CS35L56_SPI) += snd-soc-cs35l56-spi.o
obj-$(CONFIG_SND_SOC_CS35L56_SDW) += snd-soc-cs35l56-sdw.o
+obj-$(CONFIG_SND_SOC_CS35L56_TEST) += snd-soc-cs35l56-test.o
obj-$(CONFIG_SND_SOC_CS40L50) += snd-soc-cs40l50.o
obj-$(CONFIG_SND_SOC_CS42L42_CORE) += snd-soc-cs42l42.o
obj-$(CONFIG_SND_SOC_CS42L42) += snd-soc-cs42l42-i2c.o
@@ -686,6 +690,7 @@ obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.o
obj-$(CONFIG_SND_SOC_RT5514) += snd-soc-rt5514.o
obj-$(CONFIG_SND_SOC_RT5514_SPI) += snd-soc-rt5514-spi.o
obj-$(CONFIG_SND_SOC_RT5514_SPI_BUILTIN) += snd-soc-rt5514-spi.o
+obj-$(CONFIG_SND_SOC_RT5575) += snd-soc-rt5575.o
obj-$(CONFIG_SND_SOC_RT5616) += snd-soc-rt5616.o
obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o
diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c
deleted file mode 100644
index 9db8cdb26d33..000000000000
--- a/sound/soc/codecs/ak4641.c
+++ /dev/null
@@ -1,641 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * ak4641.c -- AK4641 ALSA Soc Audio driver
- *
- * Copyright (C) 2008 Harald Welte <laforge@gnufiish.org>
- * Copyright (C) 2011 Dmitry Artamonow <mad_soft@inbox.ru>
- *
- * Based on ak4535.c by Richard Purdie
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/gpio.h>
-#include <linux/pm.h>
-#include <linux/i2c.h>
-#include <linux/regmap.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/initval.h>
-#include <sound/tlv.h>
-#include <sound/ak4641.h>
-
-/* AK4641 register space */
-#define AK4641_PM1 0x00
-#define AK4641_PM2 0x01
-#define AK4641_SIG1 0x02
-#define AK4641_SIG2 0x03
-#define AK4641_MODE1 0x04
-#define AK4641_MODE2 0x05
-#define AK4641_DAC 0x06
-#define AK4641_MIC 0x07
-#define AK4641_TIMER 0x08
-#define AK4641_ALC1 0x09
-#define AK4641_ALC2 0x0a
-#define AK4641_PGA 0x0b
-#define AK4641_LATT 0x0c
-#define AK4641_RATT 0x0d
-#define AK4641_VOL 0x0e
-#define AK4641_STATUS 0x0f
-#define AK4641_EQLO 0x10
-#define AK4641_EQMID 0x11
-#define AK4641_EQHI 0x12
-#define AK4641_BTIF 0x13
-
-/* codec private data */
-struct ak4641_priv {
- struct regmap *regmap;
- unsigned int sysclk;
- int deemph;
- int playback_fs;
-};
-
-/*
- * ak4641 register cache
- */
-static const struct reg_default ak4641_reg_defaults[] = {
- { 0, 0x00 }, { 1, 0x80 }, { 2, 0x00 }, { 3, 0x80 },
- { 4, 0x02 }, { 5, 0x00 }, { 6, 0x11 }, { 7, 0x05 },
- { 8, 0x00 }, { 9, 0x00 }, { 10, 0x36 }, { 11, 0x10 },
- { 12, 0x00 }, { 13, 0x00 }, { 14, 0x57 }, { 15, 0x00 },
- { 16, 0x88 }, { 17, 0x88 }, { 18, 0x08 }, { 19, 0x08 }
-};
-
-static const int deemph_settings[] = {44100, 0, 48000, 32000};
-
-static int ak4641_set_deemph(struct snd_soc_component *component)
-{
- struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component);
- int i, best = 0;
-
- for (i = 0 ; i < ARRAY_SIZE(deemph_settings); i++) {
- /* if deemphasis is on, select the nearest available rate */
- if (ak4641->deemph && deemph_settings[i] != 0 &&
- abs(deemph_settings[i] - ak4641->playback_fs) <
- abs(deemph_settings[best] - ak4641->playback_fs))
- best = i;
-
- if (!ak4641->deemph && deemph_settings[i] == 0)
- best = i;
- }
-
- dev_dbg(component->dev, "Set deemphasis %d\n", best);
-
- return snd_soc_component_update_bits(component, AK4641_DAC, 0x3, best);
-}
-
-static int ak4641_put_deemph(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component);
- int deemph = ucontrol->value.integer.value[0];
-
- if (deemph > 1)
- return -EINVAL;
-
- ak4641->deemph = deemph;
-
- return ak4641_set_deemph(component);
-}
-
-static int ak4641_get_deemph(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component);
-
- ucontrol->value.integer.value[0] = ak4641->deemph;
- return 0;
-};
-
-static const char *ak4641_mono_out[] = {"(L + R)/2", "Hi-Z"};
-static const char *ak4641_hp_out[] = {"Stereo", "Mono"};
-static const char *ak4641_mic_select[] = {"Internal", "External"};
-static const char *ak4641_mic_or_dac[] = {"Microphone", "Voice DAC"};
-
-
-static const DECLARE_TLV_DB_SCALE(mono_gain_tlv, -1700, 2300, 0);
-static const DECLARE_TLV_DB_SCALE(mic_boost_tlv, 0, 2000, 0);
-static const DECLARE_TLV_DB_SCALE(eq_tlv, -1050, 150, 0);
-static const DECLARE_TLV_DB_SCALE(master_tlv, -12750, 50, 0);
-static const DECLARE_TLV_DB_SCALE(mic_stereo_sidetone_tlv, -2700, 300, 0);
-static const DECLARE_TLV_DB_SCALE(mic_mono_sidetone_tlv, -400, 400, 0);
-static const DECLARE_TLV_DB_SCALE(capture_tlv, -800, 50, 0);
-static const DECLARE_TLV_DB_SCALE(alc_tlv, -800, 50, 0);
-static const DECLARE_TLV_DB_SCALE(aux_in_tlv, -2100, 300, 0);
-
-
-static SOC_ENUM_SINGLE_DECL(ak4641_mono_out_enum,
- AK4641_SIG1, 6, ak4641_mono_out);
-static SOC_ENUM_SINGLE_DECL(ak4641_hp_out_enum,
- AK4641_MODE2, 2, ak4641_hp_out);
-static SOC_ENUM_SINGLE_DECL(ak4641_mic_select_enum,
- AK4641_MIC, 1, ak4641_mic_select);
-static SOC_ENUM_SINGLE_DECL(ak4641_mic_or_dac_enum,
- AK4641_BTIF, 4, ak4641_mic_or_dac);
-
-static const struct snd_kcontrol_new ak4641_snd_controls[] = {
- SOC_ENUM("Mono 1 Output", ak4641_mono_out_enum),
- SOC_SINGLE_TLV("Mono 1 Gain Volume", AK4641_SIG1, 7, 1, 1,
- mono_gain_tlv),
- SOC_ENUM("Headphone Output", ak4641_hp_out_enum),
- SOC_SINGLE_BOOL_EXT("Playback Deemphasis Switch", 0,
- ak4641_get_deemph, ak4641_put_deemph),
-
- SOC_SINGLE_TLV("Mic Boost Volume", AK4641_MIC, 0, 1, 0, mic_boost_tlv),
-
- SOC_SINGLE("ALC Operation Time", AK4641_TIMER, 0, 3, 0),
- SOC_SINGLE("ALC Recovery Time", AK4641_TIMER, 2, 3, 0),
- SOC_SINGLE("ALC ZC Time", AK4641_TIMER, 4, 3, 0),
-
- SOC_SINGLE("ALC 1 Switch", AK4641_ALC1, 5, 1, 0),
-
- SOC_SINGLE_TLV("ALC Volume", AK4641_ALC2, 0, 71, 0, alc_tlv),
- SOC_SINGLE("Left Out Enable Switch", AK4641_SIG2, 1, 1, 0),
- SOC_SINGLE("Right Out Enable Switch", AK4641_SIG2, 0, 1, 0),
-
- SOC_SINGLE_TLV("Capture Volume", AK4641_PGA, 0, 71, 0, capture_tlv),
-
- SOC_DOUBLE_R_TLV("Master Playback Volume", AK4641_LATT,
- AK4641_RATT, 0, 255, 1, master_tlv),
-
- SOC_SINGLE_TLV("AUX In Volume", AK4641_VOL, 0, 15, 0, aux_in_tlv),
-
- SOC_SINGLE("Equalizer Switch", AK4641_DAC, 2, 1, 0),
- SOC_SINGLE_TLV("EQ1 100 Hz Volume", AK4641_EQLO, 0, 15, 1, eq_tlv),
- SOC_SINGLE_TLV("EQ2 250 Hz Volume", AK4641_EQLO, 4, 15, 1, eq_tlv),
- SOC_SINGLE_TLV("EQ3 1 kHz Volume", AK4641_EQMID, 0, 15, 1, eq_tlv),
- SOC_SINGLE_TLV("EQ4 3.5 kHz Volume", AK4641_EQMID, 4, 15, 1, eq_tlv),
- SOC_SINGLE_TLV("EQ5 10 kHz Volume", AK4641_EQHI, 0, 15, 1, eq_tlv),
-};
-
-/* Mono 1 Mixer */
-static const struct snd_kcontrol_new ak4641_mono1_mixer_controls[] = {
- SOC_DAPM_SINGLE_TLV("Mic Mono Sidetone Volume", AK4641_VOL, 7, 1, 0,
- mic_mono_sidetone_tlv),
- SOC_DAPM_SINGLE("Mic Mono Sidetone Switch", AK4641_SIG1, 4, 1, 0),
- SOC_DAPM_SINGLE("Mono Playback Switch", AK4641_SIG1, 5, 1, 0),
-};
-
-/* Stereo Mixer */
-static const struct snd_kcontrol_new ak4641_stereo_mixer_controls[] = {
- SOC_DAPM_SINGLE_TLV("Mic Sidetone Volume", AK4641_VOL, 4, 7, 0,
- mic_stereo_sidetone_tlv),
- SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4641_SIG2, 4, 1, 0),
- SOC_DAPM_SINGLE("Playback Switch", AK4641_SIG2, 7, 1, 0),
- SOC_DAPM_SINGLE("Aux Bypass Switch", AK4641_SIG2, 5, 1, 0),
-};
-
-/* Input Mixer */
-static const struct snd_kcontrol_new ak4641_input_mixer_controls[] = {
- SOC_DAPM_SINGLE("Mic Capture Switch", AK4641_MIC, 2, 1, 0),
- SOC_DAPM_SINGLE("Aux Capture Switch", AK4641_MIC, 5, 1, 0),
-};
-
-/* Mic mux */
-static const struct snd_kcontrol_new ak4641_mic_mux_control =
- SOC_DAPM_ENUM("Mic Select", ak4641_mic_select_enum);
-
-/* Input mux */
-static const struct snd_kcontrol_new ak4641_input_mux_control =
- SOC_DAPM_ENUM("Input Select", ak4641_mic_or_dac_enum);
-
-/* mono 2 switch */
-static const struct snd_kcontrol_new ak4641_mono2_control =
- SOC_DAPM_SINGLE("Switch", AK4641_SIG1, 0, 1, 0);
-
-/* ak4641 dapm widgets */
-static const struct snd_soc_dapm_widget ak4641_dapm_widgets[] = {
- SND_SOC_DAPM_MIXER("Stereo Mixer", SND_SOC_NOPM, 0, 0,
- &ak4641_stereo_mixer_controls[0],
- ARRAY_SIZE(ak4641_stereo_mixer_controls)),
- SND_SOC_DAPM_MIXER("Mono1 Mixer", SND_SOC_NOPM, 0, 0,
- &ak4641_mono1_mixer_controls[0],
- ARRAY_SIZE(ak4641_mono1_mixer_controls)),
- SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0,
- &ak4641_input_mixer_controls[0],
- ARRAY_SIZE(ak4641_input_mixer_controls)),
- SND_SOC_DAPM_MUX("Mic Mux", SND_SOC_NOPM, 0, 0,
- &ak4641_mic_mux_control),
- SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
- &ak4641_input_mux_control),
- SND_SOC_DAPM_SWITCH("Mono 2 Enable", SND_SOC_NOPM, 0, 0,
- &ak4641_mono2_control),
-
- SND_SOC_DAPM_OUTPUT("LOUT"),
- SND_SOC_DAPM_OUTPUT("ROUT"),
- SND_SOC_DAPM_OUTPUT("MOUT1"),
- SND_SOC_DAPM_OUTPUT("MOUT2"),
- SND_SOC_DAPM_OUTPUT("MICOUT"),
-
- SND_SOC_DAPM_ADC("ADC", "HiFi Capture", AK4641_PM1, 0, 0),
- SND_SOC_DAPM_PGA("Mic", AK4641_PM1, 1, 0, NULL, 0),
- SND_SOC_DAPM_PGA("AUX In", AK4641_PM1, 2, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Mono Out", AK4641_PM1, 3, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Line Out", AK4641_PM1, 4, 0, NULL, 0),
-
- SND_SOC_DAPM_DAC("DAC", "HiFi Playback", AK4641_PM2, 0, 0),
- SND_SOC_DAPM_PGA("Mono Out 2", AK4641_PM2, 3, 0, NULL, 0),
-
- SND_SOC_DAPM_ADC("Voice ADC", "Voice Capture", AK4641_BTIF, 0, 0),
- SND_SOC_DAPM_DAC("Voice DAC", "Voice Playback", AK4641_BTIF, 1, 0),
-
- SND_SOC_DAPM_MICBIAS("Mic Int Bias", AK4641_MIC, 3, 0),
- SND_SOC_DAPM_MICBIAS("Mic Ext Bias", AK4641_MIC, 4, 0),
-
- SND_SOC_DAPM_INPUT("MICIN"),
- SND_SOC_DAPM_INPUT("MICEXT"),
- SND_SOC_DAPM_INPUT("AUX"),
- SND_SOC_DAPM_INPUT("AIN"),
-};
-
-static const struct snd_soc_dapm_route ak4641_audio_map[] = {
- /* Stereo Mixer */
- {"Stereo Mixer", "Playback Switch", "DAC"},
- {"Stereo Mixer", "Mic Sidetone Switch", "Input Mux"},
- {"Stereo Mixer", "Aux Bypass Switch", "AUX In"},
-
- /* Mono 1 Mixer */
- {"Mono1 Mixer", "Mic Mono Sidetone Switch", "Input Mux"},
- {"Mono1 Mixer", "Mono Playback Switch", "DAC"},
-
- /* Mic */
- {"Mic", NULL, "AIN"},
- {"Mic Mux", "Internal", "Mic Int Bias"},
- {"Mic Mux", "External", "Mic Ext Bias"},
- {"Mic Int Bias", NULL, "MICIN"},
- {"Mic Ext Bias", NULL, "MICEXT"},
- {"MICOUT", NULL, "Mic Mux"},
-
- /* Input Mux */
- {"Input Mux", "Microphone", "Mic"},
- {"Input Mux", "Voice DAC", "Voice DAC"},
-
- /* Line Out */
- {"LOUT", NULL, "Line Out"},
- {"ROUT", NULL, "Line Out"},
- {"Line Out", NULL, "Stereo Mixer"},
-
- /* Mono 1 Out */
- {"MOUT1", NULL, "Mono Out"},
- {"Mono Out", NULL, "Mono1 Mixer"},
-
- /* Mono 2 Out */
- {"MOUT2", NULL, "Mono 2 Enable"},
- {"Mono 2 Enable", "Switch", "Mono Out 2"},
- {"Mono Out 2", NULL, "Stereo Mixer"},
-
- {"Voice ADC", NULL, "Mono 2 Enable"},
-
- /* Aux In */
- {"AUX In", NULL, "AUX"},
-
- /* ADC */
- {"ADC", NULL, "Input Mixer"},
- {"Input Mixer", "Mic Capture Switch", "Mic"},
- {"Input Mixer", "Aux Capture Switch", "AUX In"},
-};
-
-static int ak4641_set_dai_sysclk(struct snd_soc_dai *codec_dai,
- int clk_id, unsigned int freq, int dir)
-{
- struct snd_soc_component *component = codec_dai->component;
- struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component);
-
- ak4641->sysclk = freq;
- return 0;
-}
-
-static int ak4641_i2s_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_component *component = dai->component;
- struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component);
- int rate = params_rate(params), fs = 256;
- u8 mode2;
-
- if (rate)
- fs = ak4641->sysclk / rate;
- else
- return -EINVAL;
-
- /* set fs */
- switch (fs) {
- case 1024:
- mode2 = (0x2 << 5);
- break;
- case 512:
- mode2 = (0x1 << 5);
- break;
- case 256:
- mode2 = (0x0 << 5);
- break;
- default:
- dev_err(component->dev, "Error: unsupported fs=%d\n", fs);
- return -EINVAL;
- }
-
- snd_soc_component_update_bits(component, AK4641_MODE2, (0x3 << 5), mode2);
-
- /* Update de-emphasis filter for the new rate */
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- ak4641->playback_fs = rate;
- ak4641_set_deemph(component);
- }
-
- return 0;
-}
-
-static int ak4641_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai,
- unsigned int fmt)
-{
- struct snd_soc_component *component = codec_dai->component;
- u8 btif;
- int ret;
-
- /* interface format */
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_I2S:
- btif = (0x3 << 5);
- break;
- case SND_SOC_DAIFMT_LEFT_J:
- btif = (0x2 << 5);
- break;
- case SND_SOC_DAIFMT_DSP_A: /* MSB after FRM */
- btif = (0x0 << 5);
- break;
- case SND_SOC_DAIFMT_DSP_B: /* MSB during FRM */
- btif = (0x1 << 5);
- break;
- default:
- return -EINVAL;
- }
-
- ret = snd_soc_component_update_bits(component, AK4641_BTIF, (0x3 << 5), btif);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static int ak4641_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
- unsigned int fmt)
-{
- struct snd_soc_component *component = codec_dai->component;
- u8 mode1 = 0;
-
- /* interface format */
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_I2S:
- mode1 = 0x02;
- break;
- case SND_SOC_DAIFMT_LEFT_J:
- mode1 = 0x01;
- break;
- default:
- return -EINVAL;
- }
-
- return snd_soc_component_write(component, AK4641_MODE1, mode1);
-}
-
-static int ak4641_mute(struct snd_soc_dai *dai, int mute, int direction)
-{
- struct snd_soc_component *component = dai->component;
-
- return snd_soc_component_update_bits(component, AK4641_DAC, 0x20, mute ? 0x20 : 0);
-}
-
-static int ak4641_set_bias_level(struct snd_soc_component *component,
- enum snd_soc_bias_level level)
-{
- struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
- struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component);
- struct ak4641_platform_data *pdata = component->dev->platform_data;
- int ret;
-
- switch (level) {
- case SND_SOC_BIAS_ON:
- /* unmute */
- snd_soc_component_update_bits(component, AK4641_DAC, 0x20, 0);
- break;
- case SND_SOC_BIAS_PREPARE:
- /* mute */
- snd_soc_component_update_bits(component, AK4641_DAC, 0x20, 0x20);
- break;
- case SND_SOC_BIAS_STANDBY:
- if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
- if (pdata && gpio_is_valid(pdata->gpio_power))
- gpio_set_value(pdata->gpio_power, 1);
- mdelay(1);
- if (pdata && gpio_is_valid(pdata->gpio_npdn))
- gpio_set_value(pdata->gpio_npdn, 1);
- mdelay(1);
-
- ret = regcache_sync(ak4641->regmap);
- if (ret) {
- dev_err(component->dev,
- "Failed to sync cache: %d\n", ret);
- return ret;
- }
- }
- snd_soc_component_update_bits(component, AK4641_PM1, 0x80, 0x80);
- snd_soc_component_update_bits(component, AK4641_PM2, 0x80, 0);
- break;
- case SND_SOC_BIAS_OFF:
- snd_soc_component_update_bits(component, AK4641_PM1, 0x80, 0);
- if (pdata && gpio_is_valid(pdata->gpio_npdn))
- gpio_set_value(pdata->gpio_npdn, 0);
- if (pdata && gpio_is_valid(pdata->gpio_power))
- gpio_set_value(pdata->gpio_power, 0);
- regcache_mark_dirty(ak4641->regmap);
- break;
- }
- return 0;
-}
-
-#define AK4641_RATES (SNDRV_PCM_RATE_8000_48000)
-#define AK4641_RATES_BT (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
- SNDRV_PCM_RATE_16000)
-#define AK4641_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
-
-static const struct snd_soc_dai_ops ak4641_i2s_dai_ops = {
- .hw_params = ak4641_i2s_hw_params,
- .set_fmt = ak4641_i2s_set_dai_fmt,
- .mute_stream = ak4641_mute,
- .set_sysclk = ak4641_set_dai_sysclk,
- .no_capture_mute = 1,
-};
-
-static const struct snd_soc_dai_ops ak4641_pcm_dai_ops = {
- .hw_params = NULL, /* rates are controlled by BT chip */
- .set_fmt = ak4641_pcm_set_dai_fmt,
- .mute_stream = ak4641_mute,
- .set_sysclk = ak4641_set_dai_sysclk,
- .no_capture_mute = 1,
-};
-
-static struct snd_soc_dai_driver ak4641_dai[] = {
-{
- .name = "ak4641-hifi",
- .id = 1,
- .playback = {
- .stream_name = "HiFi Playback",
- .channels_min = 1,
- .channels_max = 2,
- .rates = AK4641_RATES,
- .formats = AK4641_FORMATS,
- },
- .capture = {
- .stream_name = "HiFi Capture",
- .channels_min = 1,
- .channels_max = 2,
- .rates = AK4641_RATES,
- .formats = AK4641_FORMATS,
- },
- .ops = &ak4641_i2s_dai_ops,
- .symmetric_rate = 1,
-},
-{
- .name = "ak4641-voice",
- .id = 1,
- .playback = {
- .stream_name = "Voice Playback",
- .channels_min = 1,
- .channels_max = 1,
- .rates = AK4641_RATES_BT,
- .formats = AK4641_FORMATS,
- },
- .capture = {
- .stream_name = "Voice Capture",
- .channels_min = 1,
- .channels_max = 1,
- .rates = AK4641_RATES_BT,
- .formats = AK4641_FORMATS,
- },
- .ops = &ak4641_pcm_dai_ops,
- .symmetric_rate = 1,
-},
-};
-
-static const struct snd_soc_component_driver soc_component_dev_ak4641 = {
- .controls = ak4641_snd_controls,
- .num_controls = ARRAY_SIZE(ak4641_snd_controls),
- .dapm_widgets = ak4641_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(ak4641_dapm_widgets),
- .dapm_routes = ak4641_audio_map,
- .num_dapm_routes = ARRAY_SIZE(ak4641_audio_map),
- .set_bias_level = ak4641_set_bias_level,
- .suspend_bias_off = 1,
- .idle_bias_on = 1,
- .use_pmdown_time = 1,
- .endianness = 1,
-};
-
-static const struct regmap_config ak4641_regmap = {
- .reg_bits = 8,
- .val_bits = 8,
-
- .max_register = AK4641_BTIF,
- .reg_defaults = ak4641_reg_defaults,
- .num_reg_defaults = ARRAY_SIZE(ak4641_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
-};
-
-static int ak4641_i2c_probe(struct i2c_client *i2c)
-{
- struct ak4641_platform_data *pdata = i2c->dev.platform_data;
- struct ak4641_priv *ak4641;
- int ret;
-
- ak4641 = devm_kzalloc(&i2c->dev, sizeof(struct ak4641_priv),
- GFP_KERNEL);
- if (!ak4641)
- return -ENOMEM;
-
- ak4641->regmap = devm_regmap_init_i2c(i2c, &ak4641_regmap);
- if (IS_ERR(ak4641->regmap))
- return PTR_ERR(ak4641->regmap);
-
- if (pdata) {
- if (gpio_is_valid(pdata->gpio_power)) {
- ret = gpio_request_one(pdata->gpio_power,
- GPIOF_OUT_INIT_LOW, "ak4641 power");
- if (ret)
- goto err_out;
- }
- if (gpio_is_valid(pdata->gpio_npdn)) {
- ret = gpio_request_one(pdata->gpio_npdn,
- GPIOF_OUT_INIT_LOW, "ak4641 npdn");
- if (ret)
- goto err_gpio;
-
- udelay(1); /* > 150 ns */
- gpio_set_value(pdata->gpio_npdn, 1);
- }
- }
-
- i2c_set_clientdata(i2c, ak4641);
-
- ret = devm_snd_soc_register_component(&i2c->dev,
- &soc_component_dev_ak4641,
- ak4641_dai, ARRAY_SIZE(ak4641_dai));
- if (ret != 0)
- goto err_gpio2;
-
- return 0;
-
-err_gpio2:
- if (pdata) {
- if (gpio_is_valid(pdata->gpio_power))
- gpio_set_value(pdata->gpio_power, 0);
- if (gpio_is_valid(pdata->gpio_npdn))
- gpio_free(pdata->gpio_npdn);
- }
-err_gpio:
- if (pdata && gpio_is_valid(pdata->gpio_power))
- gpio_free(pdata->gpio_power);
-err_out:
- return ret;
-}
-
-static void ak4641_i2c_remove(struct i2c_client *i2c)
-{
- struct ak4641_platform_data *pdata = i2c->dev.platform_data;
-
- if (pdata) {
- if (gpio_is_valid(pdata->gpio_power)) {
- gpio_set_value(pdata->gpio_power, 0);
- gpio_free(pdata->gpio_power);
- }
- if (gpio_is_valid(pdata->gpio_npdn))
- gpio_free(pdata->gpio_npdn);
- }
-}
-
-static const struct i2c_device_id ak4641_i2c_id[] = {
- { "ak4641" },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, ak4641_i2c_id);
-
-static struct i2c_driver ak4641_i2c_driver = {
- .driver = {
- .name = "ak4641",
- },
- .probe = ak4641_i2c_probe,
- .remove = ak4641_i2c_remove,
- .id_table = ak4641_i2c_id,
-};
-
-module_i2c_driver(ak4641_i2c_driver);
-
-MODULE_DESCRIPTION("SoC AK4641 driver");
-MODULE_AUTHOR("Harald Welte <laforge@gnufiish.org>");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c
index a9946dcdc9f6..ec229b315f9f 100644
--- a/sound/soc/codecs/alc5623.c
+++ b/sound/soc/codecs/alc5623.c
@@ -678,14 +678,9 @@ static int alc5623_set_dai_fmt(struct snd_soc_dai *codec_dai,
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
- case SND_SOC_DAIFMT_IB_IF:
- iface |= ALC5623_DAI_MAIN_I2S_BCLK_POL_CTRL;
- break;
case SND_SOC_DAIFMT_IB_NF:
iface |= ALC5623_DAI_MAIN_I2S_BCLK_POL_CTRL;
break;
- case SND_SOC_DAIFMT_NB_IF:
- break;
default:
return -EINVAL;
}
diff --git a/sound/soc/codecs/aw87390.c b/sound/soc/codecs/aw87390.c
index d7fd865c349f..613daccca3af 100644
--- a/sound/soc/codecs/aw87390.c
+++ b/sound/soc/codecs/aw87390.c
@@ -314,6 +314,45 @@ static int aw87390_drv_event(struct snd_soc_dapm_widget *w,
return ret;
}
+static int aw87391_rgds_drv_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct aw87390 *aw87390 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw87390->aw_pa;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (!IS_ERR(aw87390->vdd_reg)) {
+ if (regulator_enable(aw87390->vdd_reg))
+ dev_warn(aw_dev->dev, "Failed to enable vdd\n");
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(aw_dev->regmap, AW87391_SYSCTRL_REG,
+ AW87391_REG_VER_SEL_LOW | AW87391_REG_EN_ADAP |
+ AW87391_REG_EN_2X | AW87391_EN_SPK |
+ AW87391_EN_PA | AW87391_REG_EN_CP |
+ AW87391_EN_SW);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(aw_dev->regmap, AW87390_SYSCTRL_REG,
+ AW87390_POWER_DOWN_VALUE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (!IS_ERR(aw87390->vdd_reg)) {
+ if (regulator_disable(aw87390->vdd_reg))
+ dev_warn(aw_dev->dev, "Failed to disable vdd\n");
+ }
+ break;
+ default:
+ dev_err(aw_dev->dev, "%s: invalid event %d\n", __func__, event);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static const struct snd_soc_dapm_widget aw87390_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("IN"),
SND_SOC_DAPM_PGA_E("SPK PA", SND_SOC_NOPM, 0, 0, NULL, 0, aw87390_drv_event,
@@ -321,6 +360,14 @@ static const struct snd_soc_dapm_widget aw87390_dapm_widgets[] = {
SND_SOC_DAPM_OUTPUT("OUT"),
};
+static const struct snd_soc_dapm_widget aw87391_rgds_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("IN"),
+ SND_SOC_DAPM_PGA_E("SPK PA", SND_SOC_NOPM, 0, 0, NULL, 0, aw87391_rgds_drv_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+};
+
static const struct snd_soc_dapm_route aw87390_dapm_routes[] = {
{ "SPK PA", NULL, "IN" },
{ "OUT", NULL, "SPK PA" },
@@ -339,6 +386,80 @@ static int aw87390_codec_probe(struct snd_soc_component *component)
return 0;
}
+/*
+ * Firmware typically is used to load the sequence of init commands,
+ * however for the Anbernic RG-DS we don't have a firmware file just
+ * a list of registers and values. Most of these values are undocumented
+ * in the AW87391 datasheet.
+ */
+static void aw87391_rgds_codec_init(struct aw87390 *aw87390)
+{
+ struct aw_device *aw_dev = aw87390->aw_pa;
+
+ /* Undocumented command per datasheet. */
+ regmap_write(aw_dev->regmap, 0x64, 0x3a);
+
+ /* Bits 7:4 are undocumented but provided by manufacturer. */
+ regmap_write(aw_dev->regmap, AW87391_CP_REG,
+ (5 << 4) | AW87391_REG_CP_OVP_8_50V);
+
+ regmap_write(aw_dev->regmap, AW87391_AGCPO_REG,
+ AW87391_AK1_S_016 | AW87391_AGC2PO_MW(500));
+
+ regmap_write(aw_dev->regmap, AW87391_AGC2PA_REG,
+ AW87391_RK_S_20_48 | AW87391_AK2_S_41 | AW87391_AK2F_S_41);
+
+ /* Undocumented commands per datasheet. */
+ regmap_write(aw_dev->regmap, 0x5d, 0x00);
+ regmap_write(aw_dev->regmap, 0x5e, 0xb4);
+ regmap_write(aw_dev->regmap, 0x5f, 0x30);
+ regmap_write(aw_dev->regmap, 0x60, 0x39);
+ regmap_write(aw_dev->regmap, 0x61, 0x10);
+ regmap_write(aw_dev->regmap, 0x62, 0x03);
+ regmap_write(aw_dev->regmap, 0x63, 0x7d);
+ regmap_write(aw_dev->regmap, 0x65, 0xa0);
+ regmap_write(aw_dev->regmap, 0x66, 0x21);
+ regmap_write(aw_dev->regmap, 0x67, 0x41);
+ regmap_write(aw_dev->regmap, 0x68, 0x3b);
+ regmap_write(aw_dev->regmap, 0x6e, 0x00);
+ regmap_write(aw_dev->regmap, 0x6f, 0x00);
+ regmap_write(aw_dev->regmap, 0x70, 0x00);
+ regmap_write(aw_dev->regmap, 0x71, 0x00);
+ regmap_write(aw_dev->regmap, 0x72, 0x34);
+ regmap_write(aw_dev->regmap, 0x73, 0x06);
+ regmap_write(aw_dev->regmap, 0x74, 0x10);
+ regmap_write(aw_dev->regmap, 0x75, 0x00);
+ regmap_write(aw_dev->regmap, 0x7a, 0x00);
+ regmap_write(aw_dev->regmap, 0x7b, 0x00);
+ regmap_write(aw_dev->regmap, 0x7c, 0x00);
+ regmap_write(aw_dev->regmap, 0x7d, 0x00);
+
+ regmap_write(aw_dev->regmap, AW87391_PAG_REG, AW87391_GAIN_12DB);
+ regmap_write(aw_dev->regmap, AW87391_SYSCTRL_REG,
+ AW87391_EN_PA | AW87391_REG_EN_CP | AW87391_EN_SW);
+ regmap_write(aw_dev->regmap, AW87391_SYSCTRL_REG,
+ AW87391_REG_VER_SEL_LOW | AW87391_REG_EN_ADAP |
+ AW87391_REG_EN_2X | AW87391_EN_SPK | AW87391_EN_PA |
+ AW87391_REG_EN_CP | AW87391_EN_SW);
+ regmap_write(aw_dev->regmap, AW87391_PAG_REG, AW87391_GAIN_15DB);
+}
+
+static int aw87391_rgds_codec_probe(struct snd_soc_component *component)
+{
+ struct aw87390 *aw87390 = snd_soc_component_get_drvdata(component);
+
+ aw87390->vdd_reg = devm_regulator_get_optional(aw87390->aw_pa->dev,
+ "vdd");
+ if (IS_ERR(aw87390->vdd_reg) && PTR_ERR(aw87390->vdd_reg) != -ENODEV)
+ return dev_err_probe(aw87390->aw_pa->dev,
+ PTR_ERR(aw87390->vdd_reg),
+ "Could not get vdd regulator\n");
+
+ aw87391_rgds_codec_init(aw87390);
+
+ return 0;
+}
+
static const struct snd_soc_component_driver soc_codec_dev_aw87390 = {
.probe = aw87390_codec_probe,
.dapm_widgets = aw87390_dapm_widgets,
@@ -349,6 +470,14 @@ static const struct snd_soc_component_driver soc_codec_dev_aw87390 = {
.num_controls = ARRAY_SIZE(aw87390_controls),
};
+static const struct snd_soc_component_driver soc_codec_dev_anbernic_rgds = {
+ .probe = aw87391_rgds_codec_probe,
+ .dapm_widgets = aw87391_rgds_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aw87391_rgds_dapm_widgets),
+ .dapm_routes = aw87390_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(aw87390_dapm_routes),
+};
+
static void aw87390_parse_channel_dt(struct aw87390 *aw87390)
{
struct aw_device *aw_dev = aw87390->aw_pa;
@@ -366,6 +495,10 @@ static int aw87390_init(struct aw87390 *aw87390, struct i2c_client *i2c, struct
unsigned int chip_id;
int ret;
+ aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL);
+ if (!aw_dev)
+ return -ENOMEM;
+
/* read chip id */
ret = regmap_read(regmap, AW87390_ID_REG, &chip_id);
if (ret) {
@@ -373,22 +506,24 @@ static int aw87390_init(struct aw87390 *aw87390, struct i2c_client *i2c, struct
return ret;
}
- if (chip_id != AW87390_CHIP_ID) {
+ switch (chip_id) {
+ case AW87390_CHIP_ID:
+ aw_dev->chip_id = AW87390_CHIP_ID;
+ break;
+ case AW87391_CHIP_ID:
+ aw_dev->chip_id = AW87391_CHIP_ID;
+ break;
+ default:
dev_err(&i2c->dev, "unsupported device\n");
return -ENXIO;
}
dev_dbg(&i2c->dev, "chip id = 0x%x\n", chip_id);
- aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL);
- if (!aw_dev)
- return -ENOMEM;
-
aw87390->aw_pa = aw_dev;
aw_dev->i2c = i2c;
aw_dev->regmap = regmap;
aw_dev->dev = &i2c->dev;
- aw_dev->chip_id = AW87390_CHIP_ID;
aw_dev->acf = NULL;
aw_dev->prof_info.prof_desc = NULL;
aw_dev->prof_info.count = 0;
@@ -406,6 +541,7 @@ static int aw87390_init(struct aw87390 *aw87390, struct i2c_client *i2c, struct
static int aw87390_i2c_probe(struct i2c_client *i2c)
{
struct aw87390 *aw87390;
+ const struct snd_soc_component_driver *priv;
int ret;
ret = i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C);
@@ -434,16 +570,38 @@ static int aw87390_i2c_probe(struct i2c_client *i2c)
if (ret)
return ret;
- ret = devm_snd_soc_register_component(&i2c->dev,
- &soc_codec_dev_aw87390, NULL, 0);
+ switch (aw87390->aw_pa->chip_id) {
+ case AW87390_CHIP_ID:
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_aw87390, NULL, 0);
+ break;
+ case AW87391_CHIP_ID:
+ priv = of_device_get_match_data(&i2c->dev);
+ if (!priv)
+ return dev_err_probe(&i2c->dev, -EINVAL,
+ "aw87391 not currently supported\n");
+ ret = devm_snd_soc_register_component(&i2c->dev, priv, NULL, 0);
+ break;
+ default:
+ return -ENXIO;
+ }
+
if (ret)
dev_err(&i2c->dev, "failed to register aw87390: %d\n", ret);
return ret;
}
+static const struct of_device_id aw87390_of_match[] = {
+ { .compatible = "awinic,aw87390" },
+ { .compatible = "anbernic,rgds-amp", .data = &soc_codec_dev_anbernic_rgds },
+ {},
+};
+MODULE_DEVICE_TABLE(of, aw87390_of_match);
+
static const struct i2c_device_id aw87390_i2c_id[] = {
{ AW87390_I2C_NAME },
+ { AW87391_I2C_NAME },
{ }
};
MODULE_DEVICE_TABLE(i2c, aw87390_i2c_id);
@@ -451,6 +609,7 @@ MODULE_DEVICE_TABLE(i2c, aw87390_i2c_id);
static struct i2c_driver aw87390_i2c_driver = {
.driver = {
.name = AW87390_I2C_NAME,
+ .of_match_table = of_match_ptr(aw87390_of_match),
},
.probe = aw87390_i2c_probe,
.id_table = aw87390_i2c_id,
diff --git a/sound/soc/codecs/aw87390.h b/sound/soc/codecs/aw87390.h
index d0d049e65991..f48b207e4bb4 100644
--- a/sound/soc/codecs/aw87390.h
+++ b/sound/soc/codecs/aw87390.h
@@ -52,6 +52,90 @@
#define AW87390_I2C_NAME "aw87390"
#define AW87390_ACF_FILE "aw87390_acf.bin"
+#define AW87391_SYSCTRL_REG (0x01)
+#define AW87391_REG_VER_SEL_LOW (0 << 6)
+#define AW87391_REG_VER_SEL_NORMAL (1 << 6)
+#define AW87391_REG_VER_SEL_SUPER (2 << 6)
+#define AW87391_REG_EN_ADAP BIT(5)
+#define AW87391_REG_EN_2X BIT(4)
+#define AW87391_EN_SPK BIT(3)
+#define AW87391_EN_PA BIT(2)
+#define AW87391_REG_EN_CP BIT(1)
+#define AW87391_EN_SW BIT(0)
+
+#define AW87391_CP_REG (0x02)
+#define AW87391_REG_CP_OVP_6_50V 0
+#define AW87391_REG_CP_OVP_6_75V 1
+#define AW87391_REG_CP_OVP_7_00V 2
+#define AW87391_REG_CP_OVP_7_25V 3
+#define AW87391_REG_CP_OVP_7_50V 4
+#define AW87391_REG_CP_OVP_7_75V 5
+#define AW87391_REG_CP_OVP_8_00V 6
+#define AW87391_REG_CP_OVP_8_25V 7
+#define AW87391_REG_CP_OVP_8_50V 8
+
+#define AW87391_PAG_REG (0x03)
+#define AW87391_GAIN_12DB 0
+#define AW87391_GAIN_15DB 1
+#define AW87391_GAIN_18DB 2
+#define AW87391_GAIN_21DB 3
+#define AW87391_GAIN_24DB 4
+
+#define AW87391_AGCPO_REG (0x04)
+#define AW87391_AK1_S_016 (2 << 5)
+#define AW87391_AK1_S_032 (3 << 5)
+#define AW87391_PD_AGC1_PWRDN BIT(4)
+/* AGC2PO supports values between 500mW (0000) to 1600mW (1011) */
+#define AW87391_AGC2PO_MW(n) ((n / 100) - 5)
+
+#define AW87391_AGC2PA_REG (0x05)
+#define AW87391_RK_S_5_12 (0 << 5)
+#define AW87391_RK_S_10_24 (1 << 5)
+#define AW87391_RK_S_20_48 (2 << 5)
+#define AW87391_RK_S_41 (3 << 5)
+#define AW87391_RK_S_82 (4 << 5)
+#define AW87391_RK_S_164 (5 << 5)
+#define AW87391_RK_S_328 (6 << 5)
+#define AW87391_RK_S_656 (7 << 5)
+#define AW87391_AK2_S_1_28 (0 << 2)
+#define AW87391_AK2_S_2_56 (1 << 2)
+#define AW87391_AK2_S_10_24 (2 << 2)
+#define AW87391_AK2_S_41 (3 << 2)
+#define AW87391_AK2_S_82 (4 << 2)
+#define AW87391_AK2_S_164 (5 << 2)
+#define AW87391_AK2_S_328 (6 << 2)
+#define AW87391_AK2_S_656 (7 << 2)
+#define AW87391_AK2F_S_10_24 0
+#define AW87391_AK2F_S_20_48 1
+#define AW87391_AK2F_S_41 2
+#define AW87391_AK2F_S_82 3
+
+#define AW87391_SYSST_REG (0x06)
+#define AW87391_UVLO BIT(7)
+#define AW87391_OTN BIT(6)
+#define AW87391_OC_FLAG BIT(5)
+#define AW87391_ADAP_CP BIT(4)
+#define AW87391_STARTOK BIT(3)
+#define AW87391_CP_OVP BIT(2)
+#define AW87391_PORN BIT(1)
+
+#define AW87391_SYSINT_REG (0x07)
+#define AW87391_UVLOI BIT(7)
+#define AW87391_ONTI BIT(6)
+#define AW87391_OC_FLAGI BIT(5)
+#define AW87391_ADAP_CPI BIT(4)
+#define AW87391_STARTOKI BIT(3)
+#define AW87391_CP_OVPI BIT(2)
+#define AW87391_PORNI BIT(1)
+
+#define AW87391_DFT_THGEN0_REG (0x63)
+#define AW87391_ADAPVTH_01W (0 << 2)
+#define AW87391_ADAPVTH_02W (1 << 2)
+#define AW87391_ADAPVTH_03W (2 << 2)
+#define AW87391_ADAPVTH_04W (3 << 2)
+
+#define AW87391_I2C_NAME "aw87391"
+
#define AW87390_PROFILE_EXT(xname, profile_info, profile_get, profile_set) \
{ \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
@@ -63,6 +147,7 @@
enum aw87390_id {
AW87390_CHIP_ID = 0x76,
+ AW87391_CHIP_ID = 0xc1,
};
enum {
@@ -80,6 +165,7 @@ struct aw87390 {
struct mutex lock;
struct regmap *regmap;
struct aw_container *aw_cfg;
+ struct regulator *vdd_reg;
};
#endif
diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c
index 8f37bfb974ae..29b3fc8a1ea4 100644
--- a/sound/soc/codecs/aw88261.c
+++ b/sound/soc/codecs/aw88261.c
@@ -11,6 +11,7 @@
#include <linux/i2c.h>
#include <linux/firmware.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include <sound/soc.h>
#include "aw88261.h"
#include "aw88395/aw88395_data_type.h"
@@ -1190,6 +1191,10 @@ static int aw88261_init(struct aw88261 *aw88261, struct i2c_client *i2c, struct
unsigned int chip_id;
int ret;
+ ret = devm_regulator_get_enable(&i2c->dev, "dvdd");
+ if (ret)
+ return dev_err_probe(&i2c->dev, ret, "Failed to enable dvdd supply\n");
+
/* read chip id */
ret = regmap_read(regmap, AW88261_ID_REG, &chip_id);
if (ret) {
@@ -1264,14 +1269,21 @@ static int aw88261_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id aw88261_i2c_id[] = {
- { AW88261_I2C_NAME },
+ { "aw88261" },
{ }
};
MODULE_DEVICE_TABLE(i2c, aw88261_i2c_id);
+static const struct of_device_id aw88261_of_table[] = {
+ { .compatible = "awinic,aw88261" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, aw88261_of_table);
+
static struct i2c_driver aw88261_i2c_driver = {
.driver = {
- .name = AW88261_I2C_NAME,
+ .name = "aw88261",
+ .of_match_table = aw88261_of_table,
},
.probe = aw88261_i2c_probe,
.id_table = aw88261_i2c_id,
diff --git a/sound/soc/codecs/aw88261.h b/sound/soc/codecs/aw88261.h
index 734d0f93ced9..1fee589608d6 100644
--- a/sound/soc/codecs/aw88261.h
+++ b/sound/soc/codecs/aw88261.h
@@ -370,8 +370,6 @@
#define AW88261_START_RETRIES (5)
#define AW88261_START_WORK_DELAY_MS (0)
-#define AW88261_I2C_NAME "aw88261"
-
#define AW88261_RATES (SNDRV_PCM_RATE_8000_48000 | \
SNDRV_PCM_RATE_96000)
#define AW88261_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
diff --git a/sound/soc/codecs/cs-amp-lib-test.c b/sound/soc/codecs/cs-amp-lib-test.c
index 923f1857e45b..2b1529343f11 100644
--- a/sound/soc/codecs/cs-amp-lib-test.c
+++ b/sound/soc/codecs/cs-amp-lib-test.c
@@ -16,6 +16,7 @@
#include <linux/list.h>
#include <linux/module.h>
#include <linux/overflow.h>
+#include <linux/pci_ids.h>
#include <linux/platform_device.h>
#include <linux/random.h>
#include <sound/cs-amp-lib.h>
@@ -56,6 +57,8 @@ struct cs_amp_lib_test_ctl_write_entry {
struct cs_amp_lib_test_param {
int num_amps;
int amp_index;
+ char *vendor_sysid;
+ char *expected_sysid;
};
static struct cirrus_amp_efi_data *cs_amp_lib_test_cal_blob_dup(struct kunit *test)
@@ -2305,6 +2308,98 @@ static void cs_amp_lib_test_spkid_hp_31(struct kunit *test)
KUNIT_EXPECT_EQ(test, 1, cs_amp_get_vendor_spkid(dev));
}
+static efi_status_t cs_amp_lib_test_get_efi_vendor_sysid(efi_char16_t *name,
+ efi_guid_t *guid,
+ u32 *returned_attr,
+ unsigned long *size,
+ void *buf)
+{
+ struct kunit *test = kunit_get_current_test();
+ const struct cs_amp_lib_test_param *param = test->param_value;
+ unsigned int len;
+
+ KUNIT_ASSERT_NOT_NULL(test, param->vendor_sysid);
+ len = strlen(param->vendor_sysid);
+
+ if (*size < len) {
+ *size = len;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ KUNIT_ASSERT_NOT_NULL(test, buf);
+ memcpy(buf, param->vendor_sysid, len);
+
+ return EFI_SUCCESS;
+}
+
+/* Fetch SSIDExV2 string from UEFI */
+static void cs_amp_lib_test_ssidexv2_fetch(struct kunit *test)
+{
+ const struct cs_amp_lib_test_param *param = test->param_value;
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+ const char *got;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_vendor_sysid);
+
+ got = cs_amp_devm_get_vendor_specific_variant_id(dev, PCI_VENDOR_ID_DELL, 0xabcd);
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, got);
+ KUNIT_EXPECT_STREQ(test, got, param->expected_sysid);
+}
+
+/* Invalid SSIDExV2 string should be ignored */
+static void cs_amp_lib_test_ssidexv2_fetch_invalid(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+ const char *got;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_vendor_sysid);
+
+ got = cs_amp_devm_get_vendor_specific_variant_id(dev, PCI_VENDOR_ID_DELL, 0xabcd);
+ KUNIT_EXPECT_NOT_NULL(test, got);
+ KUNIT_EXPECT_EQ(test, PTR_ERR_OR_ZERO(got), -ENOENT);
+}
+
+static void cs_amp_lib_test_ssidexv2_not_dell(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+ const char *got;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_vendor_sysid);
+
+ /* Not returned if SSID vendor is not Dell */
+ got = cs_amp_devm_get_vendor_specific_variant_id(dev, PCI_VENDOR_ID_CIRRUS, 0xabcd);
+ KUNIT_EXPECT_NOT_NULL(test, got);
+ KUNIT_EXPECT_EQ(test, PTR_ERR_OR_ZERO(got), -ENOENT);
+}
+
+static void cs_amp_lib_test_vendor_variant_id_not_found(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+ const char *got;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_none);
+
+ got = cs_amp_devm_get_vendor_specific_variant_id(dev, PCI_VENDOR_ID_DELL, 0xabcd);
+ KUNIT_EXPECT_NOT_NULL(test, got);
+ KUNIT_EXPECT_EQ(test, PTR_ERR_OR_ZERO(got), -ENOENT);
+
+ got = cs_amp_devm_get_vendor_specific_variant_id(dev, -1, -1);
+ KUNIT_EXPECT_NOT_NULL(test, got);
+ KUNIT_EXPECT_EQ(test, PTR_ERR_OR_ZERO(got), -ENOENT);
+}
+
static int cs_amp_lib_test_case_init(struct kunit *test)
{
struct cs_amp_lib_test_priv *priv;
@@ -2375,6 +2470,71 @@ static void cs_amp_lib_test_get_cal_param_desc(const struct cs_amp_lib_test_para
KUNIT_ARRAY_PARAM(cs_amp_lib_test_get_cal, cs_amp_lib_test_get_cal_param_cases,
cs_amp_lib_test_get_cal_param_desc);
+static const struct cs_amp_lib_test_param cs_amp_lib_test_ssidexv2_param_cases[] = {
+ { .vendor_sysid = "abcd_00", .expected_sysid = "00" },
+ { .vendor_sysid = "abcd_01", .expected_sysid = "01" },
+ { .vendor_sysid = "abcd_XY", .expected_sysid = "XY" },
+
+ { .vendor_sysid = "1028abcd_00", .expected_sysid = "00" },
+ { .vendor_sysid = "1028abcd_01", .expected_sysid = "01" },
+ { .vendor_sysid = "1028abcd_XY", .expected_sysid = "XY" },
+
+ { .vendor_sysid = "abcd_00_WF", .expected_sysid = "00" },
+ { .vendor_sysid = "abcd_01_WF", .expected_sysid = "01" },
+ { .vendor_sysid = "abcd_XY_WF", .expected_sysid = "XY" },
+
+ { .vendor_sysid = "1028abcd_00_WF", .expected_sysid = "00" },
+ { .vendor_sysid = "1028abcd_01_WF", .expected_sysid = "01" },
+ { .vendor_sysid = "1028abcd_XY_WF", .expected_sysid = "XY" },
+
+ { .vendor_sysid = "abcd_00_AA_BB", .expected_sysid = "00" },
+ { .vendor_sysid = "abcd_01_AA_BB", .expected_sysid = "01" },
+ { .vendor_sysid = "abcd_XY_AA_BB", .expected_sysid = "XY" },
+
+ { .vendor_sysid = "1028abcd_00_AA_BB", .expected_sysid = "00" },
+ { .vendor_sysid = "1028abcd_01_AA_BB", .expected_sysid = "01" },
+ { .vendor_sysid = "1028abcd_XY_A_BB", .expected_sysid = "XY" },
+};
+
+static void cs_amp_lib_test_ssidexv2_param_desc(const struct cs_amp_lib_test_param *param,
+ char *desc)
+{
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "vendor_sysid:'%s' expected_sysid:'%s'",
+ param->vendor_sysid, param->expected_sysid);
+}
+
+KUNIT_ARRAY_PARAM(cs_amp_lib_test_ssidexv2, cs_amp_lib_test_ssidexv2_param_cases,
+ cs_amp_lib_test_ssidexv2_param_desc);
+
+static const struct cs_amp_lib_test_param cs_amp_lib_test_ssidexv2_invalid_param_cases[] = {
+ { .vendor_sysid = "abcd" },
+ { .vendor_sysid = "abcd_0" },
+ { .vendor_sysid = "abcd_1" },
+ { .vendor_sysid = "abcd_0_1" },
+ { .vendor_sysid = "abcd_1_1" },
+ { .vendor_sysid = "abcd_1_X" },
+ { .vendor_sysid = "abcd_1_X" },
+ { .vendor_sysid = "abcd_000" },
+ { .vendor_sysid = "abcd_010" },
+ { .vendor_sysid = "abcd_000_01" },
+ { .vendor_sysid = "abcd_000_01" },
+
+ { .vendor_sysid = "1234abcd" },
+ { .vendor_sysid = "1234abcd_0" },
+ { .vendor_sysid = "1234abcd_1" },
+ { .vendor_sysid = "1234abcd_0_1" },
+ { .vendor_sysid = "1234abcd_1_1" },
+ { .vendor_sysid = "1234abcd_1_X" },
+ { .vendor_sysid = "1234abcd_1_X" },
+ { .vendor_sysid = "1234abcd_000" },
+ { .vendor_sysid = "1234abcd_010" },
+ { .vendor_sysid = "1234abcd_000_01" },
+ { .vendor_sysid = "1234abcd_000_01" },
+};
+
+KUNIT_ARRAY_PARAM(cs_amp_lib_test_ssidexv2_invalid, cs_amp_lib_test_ssidexv2_invalid_param_cases,
+ cs_amp_lib_test_ssidexv2_param_desc);
+
static struct kunit_case cs_amp_lib_test_cases[] = {
/* Tests for getting calibration data from EFI */
KUNIT_CASE(cs_amp_lib_test_cal_data_too_short_test),
@@ -2434,6 +2594,15 @@ static struct kunit_case cs_amp_lib_test_cases[] = {
KUNIT_CASE(cs_amp_lib_test_spkid_hp_30),
KUNIT_CASE(cs_amp_lib_test_spkid_hp_31),
+ /* Test cases for SSIDExV2 */
+ KUNIT_CASE_PARAM(cs_amp_lib_test_ssidexv2_fetch,
+ cs_amp_lib_test_ssidexv2_gen_params),
+ KUNIT_CASE_PARAM(cs_amp_lib_test_ssidexv2_fetch_invalid,
+ cs_amp_lib_test_ssidexv2_invalid_gen_params),
+ KUNIT_CASE_PARAM(cs_amp_lib_test_ssidexv2_not_dell,
+ cs_amp_lib_test_ssidexv2_gen_params),
+ KUNIT_CASE(cs_amp_lib_test_vendor_variant_id_not_found),
+
{ } /* terminator */
};
diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c
index d8f8b0259cd1..8b131975143d 100644
--- a/sound/soc/codecs/cs-amp-lib.c
+++ b/sound/soc/codecs/cs-amp-lib.c
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/overflow.h>
+#include <linux/pci_ids.h>
#include <linux/slab.h>
#include <linux/timekeeping.h>
#include <linux/types.h>
@@ -36,6 +37,10 @@
#define HP_CALIBRATION_EFI_GUID \
EFI_GUID(0x53559579, 0x8753, 0x4f5c, 0x91, 0x30, 0xe8, 0x2a, 0xcf, 0xb8, 0xd8, 0x93)
+#define DELL_SSIDEXV2_EFI_NAME L"SSIDexV2Data"
+#define DELL_SSIDEXV2_EFI_GUID \
+ EFI_GUID(0x6a5f35df, 0x1432, 0x4656, 0x85, 0x97, 0x31, 0x04, 0xd5, 0xbf, 0x3a, 0xb0)
+
static const struct cs_amp_lib_cal_efivar {
efi_char16_t *name;
efi_guid_t *guid;
@@ -206,7 +211,7 @@ int cs_amp_write_cal_coeffs(struct cs_dsp *dsp,
const struct cirrus_amp_cal_controls *controls,
const struct cirrus_amp_cal_data *data)
{
- if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
+ if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS))
return _cs_amp_write_cal_coeffs(dsp, controls, data);
else
return -ENODEV;
@@ -225,7 +230,7 @@ int cs_amp_read_cal_coeffs(struct cs_dsp *dsp,
const struct cirrus_amp_cal_controls *controls,
struct cirrus_amp_cal_data *data)
{
- if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
+ if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS))
return _cs_amp_read_cal_coeffs(dsp, controls, data);
else
return -ENODEV;
@@ -244,10 +249,7 @@ int cs_amp_write_ambient_temp(struct cs_dsp *dsp,
const struct cirrus_amp_cal_controls *controls,
u32 temp)
{
- if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
- return cs_amp_write_cal_coeff(dsp, controls, controls->ambient, temp);
- else
- return -ENODEV;
+ return cs_amp_write_cal_coeff(dsp, controls, controls->ambient, temp);
}
EXPORT_SYMBOL_NS_GPL(cs_amp_write_ambient_temp, "SND_SOC_CS_AMP_LIB");
@@ -304,6 +306,29 @@ static int cs_amp_convert_efi_status(efi_status_t status)
}
}
+static void *cs_amp_alloc_get_efi_variable(efi_char16_t *name,
+ efi_guid_t *guid,
+ u32 *returned_attr)
+{
+ efi_status_t status;
+ unsigned long size = 0;
+
+ status = cs_amp_get_efi_variable(name, guid, NULL, &size, NULL);
+ if (status != EFI_BUFFER_TOO_SMALL)
+ return ERR_PTR(cs_amp_convert_efi_status(status));
+
+ /* Over-alloc to ensure strings are always NUL-terminated */
+ void *buf __free(kfree) = kzalloc(size + 1, GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ status = cs_amp_get_efi_variable(name, guid, returned_attr, &size, buf);
+ if (status != EFI_SUCCESS)
+ return ERR_PTR(cs_amp_convert_efi_status(status));
+
+ return_ptr(buf);
+}
+
static struct cirrus_amp_efi_data *cs_amp_get_cal_efi_buffer(struct device *dev,
efi_char16_t **name,
efi_guid_t **guid,
@@ -452,7 +477,7 @@ static int _cs_amp_set_efi_calibration_data(struct device *dev, int amp_index, i
{
u64 cal_target = cs_amp_cal_target_u64(in_data);
unsigned long num_entries;
- struct cirrus_amp_efi_data *data __free(kfree) = NULL;
+ struct cirrus_amp_efi_data *data;
efi_char16_t *name = CIRRUS_LOGIC_CALIBRATION_EFI_NAME;
efi_guid_t *guid = &CIRRUS_LOGIC_CALIBRATION_EFI_GUID;
u32 attr = CS_AMP_CAL_DEFAULT_EFI_ATTR;
@@ -515,28 +540,33 @@ alloc_new:
num_entries = max(num_amps, amp_index + 1);
if (!data || (data->count < num_entries)) {
- struct cirrus_amp_efi_data *old_data __free(kfree) = no_free_ptr(data);
+ struct cirrus_amp_efi_data *new_data;
unsigned int new_data_size = struct_size(data, data, num_entries);
- data = kzalloc(new_data_size, GFP_KERNEL);
- if (!data)
- return -ENOMEM;
+ new_data = kzalloc(new_data_size, GFP_KERNEL);
+ if (!new_data) {
+ ret = -ENOMEM;
+ goto err;
+ }
- if (old_data)
- memcpy(data, old_data, struct_size(old_data, data, old_data->count));
+ if (data) {
+ memcpy(new_data, data, struct_size(data, data, data->count));
+ kfree(data);
+ }
+ data = new_data;
data->count = num_entries;
data->size = new_data_size;
}
data->data[amp_index] = *in_data;
ret = cs_amp_set_cal_efi_buffer(dev, name, guid, attr, data);
- if (ret) {
+ if (ret)
dev_err(dev, "Failed writing calibration to EFI: %d\n", ret);
- return ret;
- }
+err:
+ kfree(data);
- return 0;
+ return ret;
}
/**
@@ -578,7 +608,7 @@ alloc_new:
int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index,
struct cirrus_amp_cal_data *out_data)
{
- if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
+ if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS))
return _cs_amp_get_efi_calibration_data(dev, target_uid, amp_index, out_data);
else
return -ENOENT;
@@ -614,7 +644,7 @@ EXPORT_SYMBOL_NS_GPL(cs_amp_get_efi_calibration_data, "SND_SOC_CS_AMP_LIB");
int cs_amp_set_efi_calibration_data(struct device *dev, int amp_index, int num_amps,
const struct cirrus_amp_cal_data *in_data)
{
- if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) {
+ if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS)) {
scoped_guard(mutex, &cs_amp_efi_cal_write_lock) {
return _cs_amp_set_efi_calibration_data(dev, amp_index,
num_amps, in_data);
@@ -687,7 +717,7 @@ int cs_amp_get_vendor_spkid(struct device *dev)
int i, ret;
if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE) &&
- !IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
+ !IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS))
return -ENOENT;
for (i = 0; i < ARRAY_SIZE(cs_amp_spkid_byte_types); i++) {
@@ -700,6 +730,92 @@ int cs_amp_get_vendor_spkid(struct device *dev)
}
EXPORT_SYMBOL_NS_GPL(cs_amp_get_vendor_spkid, "SND_SOC_CS_AMP_LIB");
+static const char *cs_amp_devm_get_dell_ssidex(struct device *dev,
+ int ssid_vendor, int ssid_device)
+{
+ unsigned int hex_prefix;
+ char audio_id[4];
+ char delim;
+ char *p;
+ int ret;
+
+ if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE) &&
+ !IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS))
+ return ERR_PTR(-ENOENT);
+
+ char *ssidex_buf __free(kfree) = cs_amp_alloc_get_efi_variable(DELL_SSIDEXV2_EFI_NAME,
+ &DELL_SSIDEXV2_EFI_GUID,
+ NULL);
+ ret = PTR_ERR_OR_ZERO(ssidex_buf);
+ if (ret == -ENOENT)
+ return ERR_PTR(-ENOENT);
+ else if (ret < 0)
+ return ssidex_buf;
+
+ /*
+ * SSIDExV2 string is a series of underscore delimited fields.
+ * First field is all or part of the SSID. Second field should be
+ * a 2-character audio hardware id, followed by other identifiers.
+ * Older models did not have the 2-character audio id, so reject
+ * the string if the second field is not 2 characters.
+ */
+ ret = sscanf(ssidex_buf, "%8x_%2s%c", &hex_prefix, audio_id, &delim);
+ if (ret < 2)
+ return ERR_PTR(-ENOENT);
+
+ if ((ret == 3) && (delim != '_'))
+ return ERR_PTR(-ENOENT);
+
+ if (strlen(audio_id) != 2)
+ return ERR_PTR(-ENOENT);
+
+ p = devm_kstrdup(dev, audio_id, GFP_KERNEL);
+ if (!p)
+ return ERR_PTR(-ENOMEM);
+
+ return p;
+}
+
+/**
+ * cs_amp_devm_get_vendor_specific_variant_id - get variant ID string
+ * @dev: pointer to struct device
+ * @ssid_vendor: PCI Subsystem Vendor (-1 if unknown)
+ * @ssid_device: PCI Subsystem Device (-1 if unknown)
+ *
+ * Known vendor-specific hardware identifiers are checked and if one is
+ * found its content is returned as a NUL-terminated string. The returned
+ * string is devm-managed.
+ *
+ * The returned string is not guaranteed to be globally unique.
+ * Generally it should be combined with some other qualifier, such as
+ * PCI SSID, to create a globally unique ID.
+ *
+ * If the caller has a PCI SSID it should pass it in @ssid_vendor and
+ * @ssid_device. If the vendor-spefic ID contains this SSID it will be
+ * stripped from the returned string to prevent duplication.
+ *
+ * If the caller does not have a PCI SSID, pass -1 for @ssid_vendor and
+ * @ssid_device.
+ *
+ * Return:
+ * * a pointer to a devm-managed string
+ * * ERR_PTR(-ENOENT) if no vendor-specific qualifier
+ * * ERR_PTR error value
+ */
+const char *cs_amp_devm_get_vendor_specific_variant_id(struct device *dev,
+ int ssid_vendor,
+ int ssid_device)
+{
+ KUNIT_STATIC_STUB_REDIRECT(cs_amp_devm_get_vendor_specific_variant_id,
+ dev, ssid_vendor, ssid_device);
+
+ if ((ssid_vendor == PCI_VENDOR_ID_DELL) || (ssid_vendor < 0))
+ return cs_amp_devm_get_dell_ssidex(dev, ssid_vendor, ssid_device);
+
+ return ERR_PTR(-ENOENT);
+}
+EXPORT_SYMBOL_NS_GPL(cs_amp_devm_get_vendor_specific_variant_id, "SND_SOC_CS_AMP_LIB");
+
/**
* cs_amp_create_debugfs - create a debugfs directory for a device
*
@@ -733,7 +849,7 @@ static const struct cs_amp_test_hooks cs_amp_test_hook_ptrs = {
};
const struct cs_amp_test_hooks * const cs_amp_test_hooks =
- PTR_IF(IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST), &cs_amp_test_hook_ptrs);
+ PTR_IF(IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS), &cs_amp_test_hook_ptrs);
EXPORT_SYMBOL_NS_GPL(cs_amp_test_hooks, "SND_SOC_CS_AMP_LIB");
MODULE_DESCRIPTION("Cirrus Logic amplifier library");
diff --git a/sound/soc/codecs/cs35l56-shared-test.c b/sound/soc/codecs/cs35l56-shared-test.c
new file mode 100644
index 000000000000..94db02aef7dc
--- /dev/null
+++ b/sound/soc/codecs/cs35l56-shared-test.c
@@ -0,0 +1,680 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// KUnit test for the Cirrus Logic cs35l56-shared module.
+//
+// Copyright (C) 2026 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <kunit/resource.h>
+#include <kunit/test.h>
+#include <kunit/static_stub.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/device/faux.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/regmap.h>
+#include <linux/seq_buf.h>
+#include <sound/cs35l56.h>
+
+struct cs35l56_shared_test_priv {
+ struct kunit *test;
+ struct faux_device *amp_dev;
+ struct regmap *registers;
+ struct cs35l56_base *cs35l56_base;
+ u8 applied_pad_pull_state[CS35L56_MAX_GPIO];
+};
+
+struct cs35l56_shared_test_param {
+ int spkid_gpios[4];
+ int spkid_pulls[4];
+ unsigned long gpio_status;
+ int spkid;
+};
+
+KUNIT_DEFINE_ACTION_WRAPPER(faux_device_destroy_wrapper, faux_device_destroy,
+ struct faux_device *)
+
+KUNIT_DEFINE_ACTION_WRAPPER(regmap_exit_wrapper, regmap_exit, struct regmap *)
+
+static const struct regmap_config cs35l56_shared_test_mock_registers_regmap = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = CS35L56_DSP1_PMEM_5114,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static const struct regmap_bus cs35l56_shared_test_mock_registers_regmap_bus = {
+ /* No handlers because it is always in cache-only */
+};
+
+static unsigned int cs35l56_shared_test_read_gpio_status(struct cs35l56_shared_test_priv *priv)
+{
+ const struct cs35l56_shared_test_param *param = priv->test->param_value;
+ unsigned int reg_offs, pad_cfg, val;
+ unsigned int status = 0;
+ unsigned int mask = 1;
+
+ for (reg_offs = 0; reg_offs < CS35L56_MAX_GPIO * sizeof(u32); reg_offs += sizeof(u32)) {
+ regmap_read(priv->registers, CS35L56_SYNC_GPIO1_CFG + reg_offs, &pad_cfg);
+ regmap_read(priv->registers, CS35L56_GPIO1_CTRL1 + reg_offs, &val);
+
+ /* Only read a value if set as an input pin and as a GPIO */
+ val &= (CS35L56_GPIO_DIR_MASK | CS35L56_GPIO_FN_MASK);
+ if ((pad_cfg & CS35L56_PAD_GPIO_IE) &&
+ (val == (CS35L56_GPIO_DIR_MASK | CS35L56_GPIO_FN_GPIO)))
+ status |= (param->gpio_status & mask);
+
+ mask <<= 1;
+ }
+
+ return status;
+}
+
+static int cs35l56_shared_test_updt_gpio_pres(struct cs35l56_shared_test_priv *priv,
+ unsigned int reg, unsigned int val)
+{
+ int i, ret;
+
+ ret = regmap_write(priv->registers, reg, val);
+ if (ret)
+ return ret;
+
+ if (val & CS35L56_UPDT_GPIO_PRES) {
+ /* Simulate transferring register state to internal latches */
+ for (i = 0; i < ARRAY_SIZE(priv->applied_pad_pull_state); i++) {
+ reg = CS35L56_SYNC_GPIO1_CFG + (i * sizeof(u32));
+ regmap_read(priv->registers, reg, &val);
+ val = FIELD_GET(CS35L56_PAD_GPIO_PULL_MASK, val);
+ priv->applied_pad_pull_state[i] = val;
+ }
+ }
+
+ return 0;
+}
+
+static int cs35l56_shared_test_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct cs35l56_shared_test_priv *priv = context;
+
+ switch (reg) {
+ case CS35L56_SYNC_GPIO1_CFG ... CS35L56_ASP2_DIO_GPIO13_CFG:
+ case CS35L56_GPIO1_CTRL1 ... CS35L56_GPIO13_CTRL1:
+ return regmap_read(priv->registers, reg, val);
+ case CS35L56_UPDATE_REGS:
+ *val = 0;
+ return 0;
+ case CS35L56_GPIO_STATUS1:
+ *val = cs35l56_shared_test_read_gpio_status(priv);
+ return 0;
+ default:
+ kunit_fail_current_test("Bad regmap read address %#x\n", reg);
+ return -EINVAL;
+ }
+}
+
+static int cs35l56_shared_test_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct cs35l56_shared_test_priv *priv = context;
+
+ switch (reg) {
+ case CS35L56_UPDATE_REGS:
+ return cs35l56_shared_test_updt_gpio_pres(priv, reg, val);
+ case CS35L56_SYNC_GPIO1_CFG ... CS35L56_ASP2_DIO_GPIO13_CFG:
+ case CS35L56_GPIO1_CTRL1 ... CS35L56_GPIO13_CTRL1:
+ return regmap_write(priv->registers, reg, val);
+ default:
+ kunit_fail_current_test("Bad regmap write address %#x\n", reg);
+ return -EINVAL;
+ }
+}
+
+static const struct regmap_bus cs35l56_shared_test_regmap_bus = {
+ .reg_read = cs35l56_shared_test_reg_read,
+ .reg_write = cs35l56_shared_test_reg_write,
+ .reg_format_endian_default = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian_default = REGMAP_ENDIAN_LITTLE,
+};
+
+/*
+ * Self-test that the mock GPIO registers obey the configuration bits.
+ * Other tests rely on the mocked registers only returning a GPIO state
+ * if the pin is correctly set as a GPIO input.
+ */
+static void cs35l56_shared_test_mock_gpio_status_selftest(struct kunit *test)
+{
+ const struct cs35l56_shared_test_param *param = test->param_value;
+ struct cs35l56_shared_test_priv *priv = test->priv;
+ struct cs35l56_base *cs35l56_base = priv->cs35l56_base;
+ unsigned int reg, val;
+
+ KUNIT_ASSERT_NOT_NULL(test, param);
+
+ /* Set all pins non-GPIO and output. Mock GPIO_STATUS should read 0 */
+ for (reg = CS35L56_GPIO1_CTRL1; reg <= CS35L56_GPIO13_CTRL1; reg += sizeof(u32))
+ KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, 0));
+
+ /* Set all pads as inputs */
+ for (reg = CS35L56_SYNC_GPIO1_CFG; reg <= CS35L56_ASP2_DIO_GPIO13_CFG; reg += sizeof(u32))
+ KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, CS35L56_PAD_GPIO_IE));
+
+ KUNIT_ASSERT_EQ(test, 0, regmap_read(cs35l56_base->regmap, CS35L56_GPIO_STATUS1, &val));
+ KUNIT_EXPECT_EQ(test, val, 0);
+
+ /* Set all pins as GPIO outputs. Mock GPIO_STATUS should read 0 */
+ for (reg = CS35L56_GPIO1_CTRL1; reg <= CS35L56_GPIO13_CTRL1; reg += sizeof(u32))
+ KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, CS35L56_GPIO_FN_GPIO));
+
+ KUNIT_ASSERT_EQ(test, 0, regmap_read(cs35l56_base->regmap, CS35L56_GPIO_STATUS1, &val));
+ KUNIT_EXPECT_EQ(test, val, 0);
+
+ /* Set all pins as non-GPIO inputs. Mock GPIO_STATUS should read 0 */
+ for (reg = CS35L56_GPIO1_CTRL1; reg <= CS35L56_GPIO13_CTRL1; reg += sizeof(u32))
+ KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, CS35L56_GPIO_DIR_MASK));
+
+ KUNIT_ASSERT_EQ(test, 0, regmap_read(cs35l56_base->regmap, CS35L56_GPIO_STATUS1, &val));
+ KUNIT_EXPECT_EQ(test, val, 0);
+
+ /* Set all pins as GPIO inputs. Mock GPIO_STATUS should match param->gpio_status */
+ for (reg = CS35L56_GPIO1_CTRL1; reg <= CS35L56_GPIO13_CTRL1; reg += sizeof(u32))
+ KUNIT_ASSERT_EQ(test, 0,
+ regmap_write(priv->registers, reg,
+ CS35L56_GPIO_DIR_MASK | CS35L56_GPIO_FN_GPIO));
+
+ KUNIT_ASSERT_EQ(test, 0, regmap_read(cs35l56_base->regmap, CS35L56_GPIO_STATUS1, &val));
+ KUNIT_EXPECT_EQ(test, val, param->gpio_status);
+
+ /* Set all pads as outputs. Mock GPIO_STATUS should read 0 */
+ for (reg = CS35L56_SYNC_GPIO1_CFG; reg <= CS35L56_ASP2_DIO_GPIO13_CFG; reg += sizeof(u32))
+ KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, 0));
+
+ KUNIT_ASSERT_EQ(test, 0, regmap_read(cs35l56_base->regmap, CS35L56_GPIO_STATUS1, &val));
+ KUNIT_EXPECT_EQ(test, val, 0);
+}
+
+/* Test that the listed chip pins are assembled into a speaker ID integer. */
+static void cs35l56_shared_test_get_onchip_speaker_id(struct kunit *test)
+{
+ const struct cs35l56_shared_test_param *param = test->param_value;
+ struct cs35l56_shared_test_priv *priv = test->priv;
+ struct cs35l56_base *cs35l56_base = priv->cs35l56_base;
+ unsigned int i, reg;
+
+ /* Set all pins non-GPIO and output */
+ for (reg = CS35L56_GPIO1_CTRL1; reg <= CS35L56_GPIO13_CTRL1; reg += sizeof(u32))
+ KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, 0));
+
+ for (reg = CS35L56_SYNC_GPIO1_CFG; reg <= CS35L56_ASP2_DIO_GPIO13_CFG; reg += sizeof(u32))
+ KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, 0));
+
+ /* Init GPIO array */
+ for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++) {
+ if (param->spkid_gpios[i] < 0)
+ break;
+
+ cs35l56_base->onchip_spkid_gpios[i] = param->spkid_gpios[i] - 1;
+ cs35l56_base->num_onchip_spkid_gpios++;
+ }
+
+ cs35l56_base->num_onchip_spkid_pulls = 0;
+
+ KUNIT_EXPECT_EQ(test, cs35l56_configure_onchip_spkid_pads(cs35l56_base), 0);
+ KUNIT_EXPECT_EQ(test, cs35l56_read_onchip_spkid(cs35l56_base), param->spkid);
+}
+
+/* Test that the listed chip pins and the corresponding pads are configured correctly. */
+static void cs35l56_shared_test_onchip_speaker_id_pad_config(struct kunit *test)
+{
+ const struct cs35l56_shared_test_param *param = test->param_value;
+ struct cs35l56_shared_test_priv *priv = test->priv;
+ struct cs35l56_base *cs35l56_base = priv->cs35l56_base;
+ unsigned int i, reg, val;
+
+ /* Init values in all pin registers */
+ for (reg = CS35L56_GPIO1_CTRL1; reg <= CS35L56_GPIO13_CTRL1; reg += sizeof(u32))
+ KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, 0));
+
+ for (reg = CS35L56_SYNC_GPIO1_CFG; reg <= CS35L56_ASP2_DIO_GPIO13_CFG; reg += sizeof(u32))
+ KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, 0));
+
+ /* Init GPIO array */
+ for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++) {
+ if (param->spkid_gpios[i] < 0)
+ break;
+
+ cs35l56_base->onchip_spkid_gpios[i] = param->spkid_gpios[i] - 1;
+ cs35l56_base->num_onchip_spkid_gpios++;
+ }
+
+ /* Init pulls array */
+ for (i = 0; i < ARRAY_SIZE(param->spkid_pulls); i++) {
+ if (param->spkid_pulls[i] < 0)
+ break;
+
+ cs35l56_base->onchip_spkid_pulls[i] = param->spkid_pulls[i];
+ cs35l56_base->num_onchip_spkid_pulls++;
+ }
+
+ KUNIT_EXPECT_EQ(test, cs35l56_configure_onchip_spkid_pads(cs35l56_base), 0);
+
+ for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++) {
+ if (param->spkid_gpios[i] < 0)
+ break;
+
+ /* Pad should be an input */
+ reg = CS35L56_SYNC_GPIO1_CFG + ((param->spkid_gpios[i] - 1) * sizeof(u32));
+ KUNIT_EXPECT_EQ(test, regmap_read(priv->registers, reg, &val), 0);
+ KUNIT_EXPECT_EQ(test, val & CS35L56_PAD_GPIO_IE, CS35L56_PAD_GPIO_IE);
+
+ /* Specified pulls should be set, others should be none */
+ if (i < cs35l56_base->num_onchip_spkid_pulls) {
+ KUNIT_EXPECT_EQ(test, val & CS35L56_PAD_GPIO_PULL_MASK,
+ FIELD_PREP(CS35L56_PAD_GPIO_PULL_MASK,
+ param->spkid_pulls[i]));
+ } else {
+ KUNIT_EXPECT_EQ(test, val & CS35L56_PAD_GPIO_PULL_MASK,
+ CS35L56_PAD_PULL_NONE);
+ }
+
+ /* Pulls for all specfied GPIOs should have been transferred to AO latch */
+ if (i < cs35l56_base->num_onchip_spkid_pulls) {
+ KUNIT_EXPECT_EQ(test,
+ priv->applied_pad_pull_state[param->spkid_gpios[i] - 1],
+ param->spkid_pulls[i]);
+ } else {
+ KUNIT_EXPECT_EQ(test,
+ priv->applied_pad_pull_state[param->spkid_gpios[i] - 1],
+ CS35L56_PAD_PULL_NONE);
+ }
+ }
+}
+
+/* Test that the listed chip pins are stashed correctly. */
+static void cs35l56_shared_test_stash_onchip_spkid_pins(struct kunit *test)
+{
+ const struct cs35l56_shared_test_param *param = test->param_value;
+ struct cs35l56_shared_test_priv *priv = test->priv;
+ struct cs35l56_base *cs35l56_base = priv->cs35l56_base;
+ u32 gpios[5], pulls[5];
+ int i, num_gpios, num_pulls;
+
+ static_assert(ARRAY_SIZE(gpios) >= ARRAY_SIZE(param->spkid_gpios));
+ static_assert(ARRAY_SIZE(pulls) >= ARRAY_SIZE(param->spkid_pulls));
+
+ num_gpios = 0;
+ for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++) {
+ if (param->spkid_gpios[i] < 0)
+ break;
+
+ gpios[i] = (u32)param->spkid_gpios[i];
+ num_gpios++;
+ }
+
+ num_pulls = 0;
+ for (i = 0; i < ARRAY_SIZE(param->spkid_pulls); i++) {
+ if (param->spkid_pulls[i] < 0)
+ break;
+
+ pulls[i] = (u32)param->spkid_pulls[i];
+ num_pulls++;
+ }
+
+ cs35l56_base->num_onchip_spkid_gpios = 0;
+ cs35l56_base->num_onchip_spkid_pulls = 0;
+
+ KUNIT_ASSERT_LE(test, num_gpios, ARRAY_SIZE(cs35l56_base->onchip_spkid_gpios));
+ KUNIT_ASSERT_LE(test, num_pulls, ARRAY_SIZE(cs35l56_base->onchip_spkid_pulls));
+
+ KUNIT_EXPECT_EQ(test,
+ cs35l56_check_and_save_onchip_spkid_gpios(cs35l56_base,
+ gpios, num_gpios,
+ pulls, num_pulls),
+ 0);
+
+ KUNIT_EXPECT_EQ(test, cs35l56_base->num_onchip_spkid_gpios, num_gpios);
+ KUNIT_EXPECT_EQ(test, cs35l56_base->num_onchip_spkid_pulls, num_pulls);
+
+ /* GPIO numbers are adjusted from 1-based to 0-based */
+ for (i = 0; i < num_gpios; i++)
+ KUNIT_EXPECT_EQ(test, cs35l56_base->onchip_spkid_gpios[i], gpios[i] - 1);
+
+ for (i = 0; i < num_pulls; i++)
+ KUNIT_EXPECT_EQ(test, cs35l56_base->onchip_spkid_pulls[i], pulls[i]);
+}
+
+/* Test that illegal GPIO numbers are rejected. */
+static void cs35l56_shared_test_stash_onchip_spkid_pins_reject_invalid(struct kunit *test)
+{
+ struct cs35l56_shared_test_priv *priv = test->priv;
+ struct cs35l56_base *cs35l56_base = priv->cs35l56_base;
+ u32 gpios[8] = { }, pulls[8] = { };
+
+ KUNIT_EXPECT_LE(test,
+ cs35l56_check_and_save_onchip_spkid_gpios(cs35l56_base,
+ gpios, 1,
+ pulls, 0),
+ 0);
+
+ switch (cs35l56_base->type) {
+ case 0x54:
+ case 0x56:
+ case 0x57:
+ gpios[0] = CS35L56_MAX_GPIO + 1;
+ break;
+ case 0x63:
+ gpios[0] = CS35L63_MAX_GPIO + 1;
+ break;
+ default:
+ kunit_fail_current_test("Unsupported type:%#x\n", cs35l56_base->type);
+ return;
+ }
+ KUNIT_EXPECT_LE(test,
+ cs35l56_check_and_save_onchip_spkid_gpios(cs35l56_base,
+ gpios, 1,
+ pulls, 0),
+ 0);
+
+ gpios[0] = 1;
+ pulls[0] = 3;
+ KUNIT_EXPECT_LE(test,
+ cs35l56_check_and_save_onchip_spkid_gpios(cs35l56_base,
+ gpios, 1,
+ pulls, 1),
+ 0);
+
+ static_assert(ARRAY_SIZE(gpios) > ARRAY_SIZE(cs35l56_base->onchip_spkid_gpios));
+ static_assert(ARRAY_SIZE(pulls) > ARRAY_SIZE(cs35l56_base->onchip_spkid_pulls));
+ KUNIT_EXPECT_EQ(test,
+ cs35l56_check_and_save_onchip_spkid_gpios(cs35l56_base,
+ gpios, ARRAY_SIZE(gpios),
+ pulls, 0),
+ -EOVERFLOW);
+ KUNIT_EXPECT_EQ(test,
+ cs35l56_check_and_save_onchip_spkid_gpios(cs35l56_base,
+ gpios, 1,
+ pulls, ARRAY_SIZE(pulls)),
+ -EOVERFLOW);
+}
+
+static void cs35l56_shared_test_onchip_speaker_id_not_defined(struct kunit *test)
+{
+ struct cs35l56_shared_test_priv *priv = test->priv;
+ struct cs35l56_base *cs35l56_base = priv->cs35l56_base;
+
+ memset(cs35l56_base->onchip_spkid_gpios, 0, sizeof(cs35l56_base->onchip_spkid_gpios));
+ memset(cs35l56_base->onchip_spkid_pulls, 0, sizeof(cs35l56_base->onchip_spkid_pulls));
+ cs35l56_base->num_onchip_spkid_gpios = 0;
+ cs35l56_base->num_onchip_spkid_pulls = 0;
+ KUNIT_EXPECT_EQ(test, cs35l56_configure_onchip_spkid_pads(cs35l56_base), 0);
+ KUNIT_EXPECT_EQ(test, cs35l56_read_onchip_spkid(cs35l56_base), -ENOENT);
+}
+
+static int cs35l56_shared_test_case_regmap_init(struct kunit *test,
+ const struct regmap_config *regmap_config)
+{
+ struct cs35l56_shared_test_priv *priv = test->priv;
+ struct cs35l56_base *cs35l56_base;
+
+ /*
+ * Create a dummy regmap to simulate a register map by holding the
+ * values of all simulated registers in the regmap cache.
+ */
+ priv->registers = regmap_init(&priv->amp_dev->dev,
+ &cs35l56_shared_test_mock_registers_regmap_bus,
+ priv,
+ &cs35l56_shared_test_mock_registers_regmap);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->registers);
+ KUNIT_ASSERT_EQ(test, 0,
+ kunit_add_action_or_reset(test, regmap_exit_wrapper,
+ priv->registers));
+ regcache_cache_only(priv->registers, true);
+
+ /* Create dummy regmap for cs35l56 driver */
+ cs35l56_base = priv->cs35l56_base;
+ cs35l56_base->regmap = regmap_init(cs35l56_base->dev,
+ &cs35l56_shared_test_regmap_bus,
+ priv,
+ regmap_config);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cs35l56_base->regmap);
+ KUNIT_ASSERT_EQ(test, 0,
+ kunit_add_action_or_reset(test, regmap_exit_wrapper,
+ cs35l56_base->regmap));
+
+ return 0;
+}
+
+static int cs35l56_shared_test_case_base_init(struct kunit *test, u8 type, u8 rev,
+ const struct regmap_config *regmap_config)
+{
+ struct cs35l56_shared_test_priv *priv;
+ int ret;
+
+ KUNIT_ASSERT_NOT_NULL(test, cs_amp_test_hooks);
+
+ priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ test->priv = priv;
+ priv->test = test;
+
+ /* Create dummy amp driver dev */
+ priv->amp_dev = faux_device_create("cs35l56_shared_test_drv", NULL, NULL);
+ KUNIT_ASSERT_NOT_NULL(test, priv->amp_dev);
+ KUNIT_ASSERT_EQ(test, 0,
+ kunit_add_action_or_reset(test,
+ faux_device_destroy_wrapper,
+ priv->amp_dev));
+
+ priv->cs35l56_base = kunit_kzalloc(test, sizeof(*priv->cs35l56_base), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, priv->cs35l56_base);
+ priv->cs35l56_base->dev = &priv->amp_dev->dev;
+ priv->cs35l56_base->type = type;
+ priv->cs35l56_base->rev = rev;
+
+ if (regmap_config) {
+ ret = cs35l56_shared_test_case_regmap_init(test, regmap_config);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cs35l56_shared_test_case_regmap_init_L56_B0_sdw(struct kunit *test)
+{
+ return cs35l56_shared_test_case_base_init(test, 0x56, 0xb0, &cs35l56_regmap_sdw);
+}
+
+static int cs35l56_shared_test_case_regmap_init_L56_B0_spi(struct kunit *test)
+{
+ return cs35l56_shared_test_case_base_init(test, 0x56, 0xb0, &cs35l56_regmap_spi);
+}
+
+static int cs35l56_shared_test_case_regmap_init_L56_B0_i2c(struct kunit *test)
+{
+ return cs35l56_shared_test_case_base_init(test, 0x56, 0xb0, &cs35l56_regmap_i2c);
+}
+
+static int cs35l56_shared_test_case_regmap_init_L56_B2_sdw(struct kunit *test)
+{
+ return cs35l56_shared_test_case_base_init(test, 0x56, 0xb2, &cs35l56_regmap_sdw);
+}
+
+static int cs35l56_shared_test_case_regmap_init_L56_B2_spi(struct kunit *test)
+{
+ return cs35l56_shared_test_case_base_init(test, 0x56, 0xb2, &cs35l56_regmap_spi);
+}
+
+static int cs35l56_shared_test_case_regmap_init_L56_B2_i2c(struct kunit *test)
+{
+ return cs35l56_shared_test_case_base_init(test, 0x56, 0xb2, &cs35l56_regmap_i2c);
+}
+
+static int cs35l56_shared_test_case_regmap_init_L63_A1_sdw(struct kunit *test)
+{
+ return cs35l56_shared_test_case_base_init(test, 0x63, 0xa1, &cs35l63_regmap_sdw);
+}
+
+static void cs35l56_shared_test_gpio_param_desc(const struct cs35l56_shared_test_param *param,
+ char *desc)
+{
+ DECLARE_SEQ_BUF(gpios, 1 + (2 * ARRAY_SIZE(param->spkid_gpios)));
+ DECLARE_SEQ_BUF(pulls, 1 + (2 * ARRAY_SIZE(param->spkid_pulls)));
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++) {
+ if (param->spkid_gpios[i] < 0)
+ break;
+
+ seq_buf_printf(&gpios, "%s%d", (i == 0) ? "" : ",", param->spkid_gpios[i]);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(param->spkid_pulls); i++) {
+ if (param->spkid_pulls[i] < 0)
+ break;
+
+ seq_buf_printf(&pulls, "%s%d", (i == 0) ? "" : ",", param->spkid_pulls[i]);
+ }
+
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "gpios:{%s} pulls:{%s} status:%#lx spkid:%d",
+ seq_buf_str(&gpios), seq_buf_str(&pulls), param->gpio_status, param->spkid);
+}
+
+static const struct cs35l56_shared_test_param cs35l56_shared_test_gpios_selftest_cases[] = {
+ { .spkid_gpios = { -1 }, .gpio_status = GENMASK(12, 0) },
+};
+KUNIT_ARRAY_PARAM(cs35l56_shared_test_gpios_selftest,
+ cs35l56_shared_test_gpios_selftest_cases,
+ cs35l56_shared_test_gpio_param_desc);
+
+static const struct cs35l56_shared_test_param cs35l56_shared_test_onchip_spkid_cases[] = {
+ { .spkid_gpios = { 1, -1 }, .gpio_status = 0, .spkid = 0 },
+ { .spkid_gpios = { 1, -1 }, .gpio_status = ~BIT(0), .spkid = 0 },
+ { .spkid_gpios = { 1, -1 }, .gpio_status = BIT(0), .spkid = 1 },
+
+ { .spkid_gpios = { 7, -1 }, .gpio_status = 0, .spkid = 0 },
+ { .spkid_gpios = { 7, -1 }, .gpio_status = ~BIT(6), .spkid = 0 },
+ { .spkid_gpios = { 7, -1 }, .gpio_status = BIT(6), .spkid = 1 },
+
+ { .spkid_gpios = { 1, 7, -1 }, .gpio_status = 0, .spkid = 0 },
+ { .spkid_gpios = { 1, 7, -1 }, .gpio_status = ~(BIT(0) | BIT(6)), .spkid = 0 },
+ { .spkid_gpios = { 1, 7, -1 }, .gpio_status = BIT(6), .spkid = 1 },
+ { .spkid_gpios = { 1, 7, -1 }, .gpio_status = BIT(0), .spkid = 2 },
+ { .spkid_gpios = { 1, 7, -1 }, .gpio_status = BIT(6) | BIT(0), .spkid = 3 },
+
+ { .spkid_gpios = { 7, 1, -1 }, .gpio_status = 0, .spkid = 0 },
+ { .spkid_gpios = { 7, 1, -1 }, .gpio_status = ~(BIT(6) | BIT(0)), .spkid = 0 },
+ { .spkid_gpios = { 7, 1, -1 }, .gpio_status = BIT(0), .spkid = 1 },
+ { .spkid_gpios = { 7, 1, -1 }, .gpio_status = BIT(6), .spkid = 2 },
+ { .spkid_gpios = { 7, 1, -1 }, .gpio_status = BIT(6) | BIT(0), .spkid = 3 },
+
+ { .spkid_gpios = { 3, 7, 1, -1 }, .gpio_status = 0, .spkid = 0 },
+ { .spkid_gpios = { 3, 7, 1, -1 }, .gpio_status = BIT(0), .spkid = 1 },
+ { .spkid_gpios = { 3, 7, 1, -1 }, .gpio_status = BIT(6), .spkid = 2 },
+ { .spkid_gpios = { 3, 7, 1, -1 }, .gpio_status = BIT(6) | BIT(0), .spkid = 3 },
+ { .spkid_gpios = { 3, 7, 1, -1 }, .gpio_status = BIT(2), .spkid = 4 },
+ { .spkid_gpios = { 3, 7, 1, -1 }, .gpio_status = BIT(2) | BIT(0), .spkid = 5 },
+ { .spkid_gpios = { 3, 7, 1, -1 }, .gpio_status = BIT(2) | BIT(6), .spkid = 6 },
+ { .spkid_gpios = { 3, 7, 1, -1 }, .gpio_status = BIT(2) | BIT(6) | BIT(0), .spkid = 7 },
+};
+KUNIT_ARRAY_PARAM(cs35l56_shared_test_onchip_spkid, cs35l56_shared_test_onchip_spkid_cases,
+ cs35l56_shared_test_gpio_param_desc);
+
+static const struct cs35l56_shared_test_param cs35l56_shared_test_onchip_spkid_pull_cases[] = {
+ { .spkid_gpios = { 1, -1 }, .spkid_pulls = { 1, -1 }, },
+ { .spkid_gpios = { 1, -1 }, .spkid_pulls = { 2, -1 }, },
+
+ { .spkid_gpios = { 7, -1 }, .spkid_pulls = { 1, -1 }, },
+ { .spkid_gpios = { 7, -1 }, .spkid_pulls = { 2, -1 }, },
+
+ { .spkid_gpios = { 1, 7, -1 }, .spkid_pulls = { 1, 1, -1 }, },
+ { .spkid_gpios = { 1, 7, -1 }, .spkid_pulls = { 2, 2, -1 }, },
+
+ { .spkid_gpios = { 7, 1, -1 }, .spkid_pulls = { 1, 1, -1 }, },
+ { .spkid_gpios = { 7, 1, -1 }, .spkid_pulls = { 2, 2, -1 }, },
+
+ { .spkid_gpios = { 3, 7, 1, -1 }, .spkid_pulls = { 1, 1, 1, -1 }, },
+ { .spkid_gpios = { 3, 7, 1, -1 }, .spkid_pulls = { 2, 2, 2, -1 }, },
+};
+KUNIT_ARRAY_PARAM(cs35l56_shared_test_onchip_spkid_pull,
+ cs35l56_shared_test_onchip_spkid_pull_cases,
+ cs35l56_shared_test_gpio_param_desc);
+
+static struct kunit_case cs35l56_shared_test_cases[] = {
+ /* Tests for speaker id */
+ KUNIT_CASE_PARAM(cs35l56_shared_test_mock_gpio_status_selftest,
+ cs35l56_shared_test_gpios_selftest_gen_params),
+ KUNIT_CASE_PARAM(cs35l56_shared_test_get_onchip_speaker_id,
+ cs35l56_shared_test_onchip_spkid_gen_params),
+ KUNIT_CASE_PARAM(cs35l56_shared_test_onchip_speaker_id_pad_config,
+ cs35l56_shared_test_onchip_spkid_gen_params),
+ KUNIT_CASE_PARAM(cs35l56_shared_test_onchip_speaker_id_pad_config,
+ cs35l56_shared_test_onchip_spkid_pull_gen_params),
+ KUNIT_CASE_PARAM(cs35l56_shared_test_stash_onchip_spkid_pins,
+ cs35l56_shared_test_onchip_spkid_pull_gen_params),
+ KUNIT_CASE(cs35l56_shared_test_stash_onchip_spkid_pins_reject_invalid),
+ KUNIT_CASE(cs35l56_shared_test_onchip_speaker_id_not_defined),
+ { }
+};
+
+static struct kunit_suite cs35l56_shared_test_suite_L56_B0_sdw = {
+ .name = "snd-soc-cs35l56-shared-test_L56_B0_sdw",
+ .init = cs35l56_shared_test_case_regmap_init_L56_B0_sdw,
+ .test_cases = cs35l56_shared_test_cases,
+};
+
+static struct kunit_suite cs35l56_shared_test_suite_L56_B2_sdw = {
+ .name = "snd-soc-cs35l56-shared-test_L56_B2_sdw",
+ .init = cs35l56_shared_test_case_regmap_init_L56_B2_sdw,
+ .test_cases = cs35l56_shared_test_cases,
+};
+
+static struct kunit_suite cs35l56_shared_test_suite_L63_A1_sdw = {
+ .name = "snd-soc-cs35l56-shared-test_L63_A1_sdw",
+ .init = cs35l56_shared_test_case_regmap_init_L63_A1_sdw,
+ .test_cases = cs35l56_shared_test_cases,
+};
+
+static struct kunit_suite cs35l56_shared_test_suite_L56_B0_spi = {
+ .name = "snd-soc-cs35l56-shared-test_L56_B0_spi",
+ .init = cs35l56_shared_test_case_regmap_init_L56_B0_spi,
+ .test_cases = cs35l56_shared_test_cases,
+};
+
+static struct kunit_suite cs35l56_shared_test_suite_L56_B2_spi = {
+ .name = "snd-soc-cs35l56-shared-test_L56_B2_spi",
+ .init = cs35l56_shared_test_case_regmap_init_L56_B2_spi,
+ .test_cases = cs35l56_shared_test_cases,
+};
+
+static struct kunit_suite cs35l56_shared_test_suite_L56_B0_i2c = {
+ .name = "snd-soc-cs35l56-shared-test_L56_B0_i2c",
+ .init = cs35l56_shared_test_case_regmap_init_L56_B0_i2c,
+ .test_cases = cs35l56_shared_test_cases,
+};
+
+static struct kunit_suite cs35l56_shared_test_suite_L56_B2_i2c = {
+ .name = "snd-soc-cs35l56-shared-test_L56_B2_i2c",
+ .init = cs35l56_shared_test_case_regmap_init_L56_B2_i2c,
+ .test_cases = cs35l56_shared_test_cases,
+};
+
+kunit_test_suites(
+ &cs35l56_shared_test_suite_L56_B0_sdw,
+ &cs35l56_shared_test_suite_L56_B2_sdw,
+ &cs35l56_shared_test_suite_L63_A1_sdw,
+
+ &cs35l56_shared_test_suite_L56_B0_spi,
+ &cs35l56_shared_test_suite_L56_B2_spi,
+
+ &cs35l56_shared_test_suite_L56_B0_i2c,
+ &cs35l56_shared_test_suite_L56_B2_i2c,
+);
+
+MODULE_IMPORT_NS("SND_SOC_CS35L56_SHARED");
+MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB");
+MODULE_DESCRIPTION("KUnit test for cs35l56-shared module");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c
index 60100c8f8c95..4707f28bfca2 100644
--- a/sound/soc/codecs/cs35l56-shared.c
+++ b/sound/soc/codecs/cs35l56-shared.c
@@ -5,13 +5,16 @@
// Copyright (C) 2023 Cirrus Logic, Inc. and
// Cirrus Logic International Semiconductor Ltd.
+#include <kunit/static_stub.h>
#include <linux/array_size.h>
+#include <linux/bitfield.h>
#include <linux/cleanup.h>
#include <linux/debugfs.h>
#include <linux/firmware/cirrus/wmfw.h>
#include <linux/fs.h>
#include <linux/gpio/consumer.h>
#include <linux/kstrtox.h>
+#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
@@ -182,6 +185,8 @@ static bool cs35l56_readable_reg(struct device *dev, unsigned int reg)
case CS35L56_OTP_MEM_53:
case CS35L56_OTP_MEM_54:
case CS35L56_OTP_MEM_55:
+ case CS35L56_SYNC_GPIO1_CFG ... CS35L56_ASP2_DIO_GPIO13_CFG:
+ case CS35L56_UPDATE_REGS:
case CS35L56_ASP1_ENABLES1:
case CS35L56_ASP1_CONTROL1:
case CS35L56_ASP1_CONTROL2:
@@ -213,6 +218,7 @@ static bool cs35l56_readable_reg(struct device *dev, unsigned int reg)
case CS35L56_IRQ1_MASK_8:
case CS35L56_IRQ1_MASK_18:
case CS35L56_IRQ1_MASK_20:
+ case CS35L56_GPIO_STATUS1 ... CS35L56_GPIO13_CTRL1:
case CS35L56_MIXER_NGATE_CH1_CFG:
case CS35L56_MIXER_NGATE_CH2_CFG:
case CS35L56_DSP_VIRTUAL1_MBOX_1:
@@ -262,6 +268,8 @@ static bool cs35l56_common_volatile_reg(unsigned int reg)
case CS35L56_GLOBAL_ENABLES: /* owned by firmware */
case CS35L56_BLOCK_ENABLES: /* owned by firmware */
case CS35L56_BLOCK_ENABLES2: /* owned by firmware */
+ case CS35L56_SYNC_GPIO1_CFG ... CS35L56_ASP2_DIO_GPIO13_CFG:
+ case CS35L56_UPDATE_REGS:
case CS35L56_REFCLK_INPUT: /* owned by firmware */
case CS35L56_GLOBAL_SAMPLE_RATE: /* owned by firmware */
case CS35L56_DACPCM1_INPUT: /* owned by firmware */
@@ -272,6 +280,7 @@ static bool cs35l56_common_volatile_reg(unsigned int reg)
case CS35L56_IRQ1_EINT_1 ... CS35L56_IRQ1_EINT_8:
case CS35L56_IRQ1_EINT_18:
case CS35L56_IRQ1_EINT_20:
+ case CS35L56_GPIO_STATUS1 ... CS35L56_GPIO13_CTRL1:
case CS35L56_MIXER_NGATE_CH1_CFG:
case CS35L56_MIXER_NGATE_CH2_CFG:
case CS35L56_DSP_VIRTUAL1_MBOX_1:
@@ -1552,6 +1561,169 @@ err:
}
EXPORT_SYMBOL_NS_GPL(cs35l56_get_speaker_id, "SND_SOC_CS35L56_SHARED");
+int cs35l56_check_and_save_onchip_spkid_gpios(struct cs35l56_base *cs35l56_base,
+ const u32 *gpios, int num_gpios,
+ const u32 *pulls, int num_pulls)
+{
+ int max_gpio;
+ int ret = 0;
+ int i;
+
+ if ((num_gpios > ARRAY_SIZE(cs35l56_base->onchip_spkid_gpios)) ||
+ (num_pulls > ARRAY_SIZE(cs35l56_base->onchip_spkid_pulls)))
+ return -EOVERFLOW;
+
+ switch (cs35l56_base->type) {
+ case 0x54:
+ case 0x56:
+ case 0x57:
+ max_gpio = CS35L56_MAX_GPIO;
+ break;
+ default:
+ max_gpio = CS35L63_MAX_GPIO;
+ break;
+ }
+
+ for (i = 0; i < num_gpios; i++) {
+ if (gpios[i] < 1 || gpios[i] > max_gpio) {
+ dev_err(cs35l56_base->dev, "Invalid spkid GPIO %d\n", gpios[i]);
+ /* Keep going so we log all bad values */
+ ret = -EINVAL;
+ }
+
+ /* Change to zero-based */
+ cs35l56_base->onchip_spkid_gpios[i] = gpios[i] - 1;
+ }
+
+ for (i = 0; i < num_pulls; i++) {
+ switch (pulls[i]) {
+ case 0:
+ cs35l56_base->onchip_spkid_pulls[i] = CS35L56_PAD_PULL_NONE;
+ break;
+ case 1:
+ cs35l56_base->onchip_spkid_pulls[i] = CS35L56_PAD_PULL_UP;
+ break;
+ case 2:
+ cs35l56_base->onchip_spkid_pulls[i] = CS35L56_PAD_PULL_DOWN;
+ break;
+ default:
+ dev_err(cs35l56_base->dev, "Invalid spkid pull %d\n", pulls[i]);
+ /* Keep going so we log all bad values */
+ ret = -EINVAL;
+ break;
+ }
+ }
+ if (ret)
+ return ret;
+
+ cs35l56_base->num_onchip_spkid_gpios = num_gpios;
+ cs35l56_base->num_onchip_spkid_pulls = num_pulls;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_check_and_save_onchip_spkid_gpios, "SND_SOC_CS35L56_SHARED");
+
+/* Caller must pm_runtime resume before calling this function */
+int cs35l56_configure_onchip_spkid_pads(struct cs35l56_base *cs35l56_base)
+{
+ struct regmap *regmap = cs35l56_base->regmap;
+ unsigned int addr_offset, val;
+ int num_gpios, num_pulls;
+ int i, ret;
+
+ KUNIT_STATIC_STUB_REDIRECT(cs35l56_configure_onchip_spkid_pads, cs35l56_base);
+
+ if (cs35l56_base->num_onchip_spkid_gpios == 0)
+ return 0;
+
+ num_gpios = min(cs35l56_base->num_onchip_spkid_gpios,
+ ARRAY_SIZE(cs35l56_base->onchip_spkid_gpios));
+ num_pulls = min(cs35l56_base->num_onchip_spkid_pulls,
+ ARRAY_SIZE(cs35l56_base->onchip_spkid_pulls));
+
+ for (i = 0; i < num_gpios; i++) {
+ addr_offset = cs35l56_base->onchip_spkid_gpios[i] * sizeof(u32);
+
+ /* Set unspecified pulls to NONE */
+ if (i < num_pulls) {
+ val = FIELD_PREP(CS35L56_PAD_GPIO_PULL_MASK,
+ cs35l56_base->onchip_spkid_pulls[i]);
+ } else {
+ val = FIELD_PREP(CS35L56_PAD_GPIO_PULL_MASK, CS35L56_PAD_PULL_NONE);
+ }
+
+ ret = regmap_update_bits(regmap, CS35L56_SYNC_GPIO1_CFG + addr_offset,
+ CS35L56_PAD_GPIO_PULL_MASK | CS35L56_PAD_GPIO_IE,
+ val | CS35L56_PAD_GPIO_IE);
+ if (ret) {
+ dev_err(cs35l56_base->dev, "GPIO%d set pad fail: %d\n",
+ cs35l56_base->onchip_spkid_gpios[i] + 1, ret);
+ return ret;
+ }
+ }
+
+ ret = regmap_write(regmap, CS35L56_UPDATE_REGS, CS35L56_UPDT_GPIO_PRES);
+ if (ret) {
+ dev_err(cs35l56_base->dev, "UPDT_GPIO_PRES failed:%d\n", ret);
+ return ret;
+ }
+
+ usleep_range(CS35L56_PAD_PULL_SETTLE_US, CS35L56_PAD_PULL_SETTLE_US * 2);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_configure_onchip_spkid_pads, "SND_SOC_CS35L56_SHARED");
+
+/* Caller must pm_runtime resume before calling this function */
+int cs35l56_read_onchip_spkid(struct cs35l56_base *cs35l56_base)
+{
+ struct regmap *regmap = cs35l56_base->regmap;
+ unsigned int addr_offset, val;
+ int num_gpios;
+ int speaker_id = 0;
+ int i, ret;
+
+ KUNIT_STATIC_STUB_REDIRECT(cs35l56_read_onchip_spkid, cs35l56_base);
+
+ if (cs35l56_base->num_onchip_spkid_gpios == 0)
+ return -ENOENT;
+
+ num_gpios = min(cs35l56_base->num_onchip_spkid_gpios,
+ ARRAY_SIZE(cs35l56_base->onchip_spkid_gpios));
+
+ for (i = 0; i < num_gpios; i++) {
+ addr_offset = cs35l56_base->onchip_spkid_gpios[i] * sizeof(u32);
+
+ ret = regmap_update_bits(regmap, CS35L56_GPIO1_CTRL1 + addr_offset,
+ CS35L56_GPIO_DIR_MASK | CS35L56_GPIO_FN_MASK,
+ CS35L56_GPIO_DIR_MASK | CS35L56_GPIO_FN_GPIO);
+ if (ret) {
+ dev_err(cs35l56_base->dev, "GPIO%u set func fail: %d\n",
+ cs35l56_base->onchip_spkid_gpios[i] + 1, ret);
+ return ret;
+ }
+ }
+
+ ret = regmap_read(regmap, CS35L56_GPIO_STATUS1, &val);
+ if (ret) {
+ dev_err(cs35l56_base->dev, "GPIO%d status read failed: %d\n",
+ cs35l56_base->onchip_spkid_gpios[i] + 1, ret);
+ return ret;
+ }
+
+ for (i = 0; i < num_gpios; i++) {
+ speaker_id <<= 1;
+
+ if (val & BIT(cs35l56_base->onchip_spkid_gpios[i]))
+ speaker_id |= 1;
+ }
+
+ dev_dbg(cs35l56_base->dev, "Onchip GPIO Speaker ID = %d\n", speaker_id);
+
+ return speaker_id;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_read_onchip_spkid, "SND_SOC_CS35L56_SHARED");
+
static const u32 cs35l56_bclk_valid_for_pll_freq_table[] = {
[0x0C] = 128000,
[0x0F] = 256000,
diff --git a/sound/soc/codecs/cs35l56-test.c b/sound/soc/codecs/cs35l56-test.c
new file mode 100644
index 000000000000..b6c8c08e3ade
--- /dev/null
+++ b/sound/soc/codecs/cs35l56-test.c
@@ -0,0 +1,639 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// KUnit test for the Cirrus Logic cs35l56 driver.
+//
+// Copyright (C) 2026 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <kunit/resource.h>
+#include <kunit/test.h>
+#include <kunit/static_stub.h>
+#include <linux/efi.h>
+#include <linux/device/faux.h>
+#include <linux/firmware/cirrus/cs_dsp.h>
+#include <linux/firmware/cirrus/wmfw.h>
+#include <linux/module.h>
+#include <linux/overflow.h>
+#include <linux/pci_ids.h>
+#include <linux/property.h>
+#include <linux/seq_buf.h>
+#include <linux/soundwire/sdw.h>
+#include <sound/cs35l56.h>
+#include <sound/cs-amp-lib.h>
+#include "cs35l56.h"
+
+KUNIT_DEFINE_ACTION_WRAPPER(faux_device_destroy_wrapper, faux_device_destroy,
+ struct faux_device *)
+
+KUNIT_DEFINE_ACTION_WRAPPER(software_node_unregister_node_group_wrapper,
+ software_node_unregister_node_group,
+ const struct software_node * const *)
+
+KUNIT_DEFINE_ACTION_WRAPPER(software_node_unregister_wrapper,
+ software_node_unregister,
+ const struct software_node *)
+
+KUNIT_DEFINE_ACTION_WRAPPER(device_remove_software_node_wrapper,
+ device_remove_software_node,
+ struct device *)
+
+struct cs35l56_test_priv {
+ struct faux_device *amp_dev;
+ struct cs35l56_private *cs35l56_priv;
+
+ const char *ssidexv2;
+
+ bool read_onchip_spkid_called;
+ bool configure_onchip_spkid_pads_called;
+};
+
+struct cs35l56_test_param {
+ u8 type;
+ u8 rev;
+
+ s32 spkid_gpios[4];
+ s32 spkid_pulls[4];
+};
+
+static const struct software_node cs35l56_test_dev_sw_node =
+ SOFTWARE_NODE("SWD1", NULL, NULL);
+
+static const struct software_node cs35l56_test_af01_sw_node =
+ SOFTWARE_NODE("AF01", NULL, &cs35l56_test_dev_sw_node);
+
+static const struct software_node *cs35l56_test_dev_and_af01_node_group[] = {
+ &cs35l56_test_dev_sw_node,
+ &cs35l56_test_af01_sw_node,
+ NULL
+};
+
+static const char *cs35l56_test_devm_get_vendor_specific_variant_id_none(struct device *dev,
+ int ssid_vendor,
+ int ssid_device)
+{
+ return ERR_PTR(-ENOENT);
+}
+
+static void cs35l56_test_l56_b0_suffix_sdw(struct kunit *test)
+{
+ struct cs35l56_test_priv *priv = test->priv;
+ struct cs35l56_private *cs35l56 = priv->cs35l56_priv;
+
+ /* Set device type info */
+ cs35l56->base.type = 0x56;
+ cs35l56->base.rev = 0xb0;
+
+ /* Set the ALSA name prefix */
+ cs35l56->component->name_prefix = "AMP1";
+
+ /* Set SoundWire link and UID number */
+ cs35l56->sdw_link_num = 1;
+ cs35l56->sdw_unique_id = 5;
+
+ kunit_activate_static_stub(test,
+ cs35l56_test_devm_get_vendor_specific_variant_id_none,
+ cs_amp_devm_get_vendor_specific_variant_id);
+
+ KUNIT_EXPECT_EQ(test, 0, cs35l56_set_fw_suffix(cs35l56));
+
+ /* Priority suffix should be the legacy ALSA prefix */
+ KUNIT_EXPECT_STREQ(test, cs35l56->dsp.fwf_suffix, "AMP1");
+
+ /* Fallback suffix should be the new SoundWire ID */
+ KUNIT_EXPECT_STREQ(test, cs35l56->fallback_fw_suffix, "l1u5");
+}
+
+static void cs35l56_test_suffix_sdw(struct kunit *test)
+{
+ struct cs35l56_test_priv *priv = test->priv;
+ struct cs35l56_private *cs35l56 = priv->cs35l56_priv;
+
+ /* Set the ALSA name prefix */
+ cs35l56->component->name_prefix = "AMP1";
+
+ /* Set SoundWire link and UID number */
+ cs35l56->sdw_link_num = 1;
+ cs35l56->sdw_unique_id = 5;
+
+ kunit_activate_static_stub(test,
+ cs35l56_test_devm_get_vendor_specific_variant_id_none,
+ cs_amp_devm_get_vendor_specific_variant_id);
+
+ KUNIT_EXPECT_EQ(test, 0, cs35l56_set_fw_suffix(cs35l56));
+
+ /* Suffix should be the SoundWire ID without a fallback */
+ KUNIT_EXPECT_STREQ(test, cs35l56->dsp.fwf_suffix, "l1u5");
+ KUNIT_EXPECT_NULL(test, cs35l56->fallback_fw_suffix);
+}
+
+static void cs35l56_test_suffix_i2cspi(struct kunit *test)
+{
+ struct cs35l56_test_priv *priv = test->priv;
+ struct cs35l56_private *cs35l56 = priv->cs35l56_priv;
+
+ /* Set the ALSA name prefix */
+ cs35l56->component->name_prefix = "AMP1";
+
+ kunit_activate_static_stub(test,
+ cs35l56_test_devm_get_vendor_specific_variant_id_none,
+ cs_amp_devm_get_vendor_specific_variant_id);
+
+ KUNIT_EXPECT_EQ(test, 0, cs35l56_set_fw_suffix(cs35l56));
+
+ /* Suffix strings should not be set: use default wm_adsp suffixing */
+ KUNIT_EXPECT_NULL(test, cs35l56->dsp.fwf_suffix);
+ KUNIT_EXPECT_NULL(test, cs35l56->fallback_fw_suffix);
+}
+
+static efi_status_t cs35l56_test_get_efi_ssidexv2(efi_char16_t *name,
+ efi_guid_t *guid,
+ u32 *returned_attr,
+ unsigned long *size,
+ void *buf)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct cs35l56_test_priv *priv = test->priv;
+ unsigned int len;
+
+ KUNIT_ASSERT_NOT_NULL(test, priv->ssidexv2);
+ len = strlen(priv->ssidexv2);
+
+ if (*size < len) {
+ *size = len;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ KUNIT_ASSERT_NOT_NULL(test, buf);
+ memcpy(buf, priv->ssidexv2, len);
+
+ return EFI_SUCCESS;
+}
+
+static void cs35l56_test_ssidexv2_suffix_sdw(struct kunit *test)
+{
+ struct cs35l56_test_priv *priv = test->priv;
+ struct cs35l56_private *cs35l56 = priv->cs35l56_priv;
+
+ /* Set the ALSA name prefix */
+ cs35l56->component->name_prefix = "AMP1";
+
+ /* Set SoundWire link and UID number */
+ cs35l56->sdw_link_num = 1;
+ cs35l56->sdw_unique_id = 5;
+
+ /* Set a SSID to enable lookup of SSIDExV2 */
+ snd_soc_card_set_pci_ssid(cs35l56->component->card, PCI_VENDOR_ID_DELL, 0x1234);
+
+ priv->ssidexv2 = "10281234_01_BB_CC";
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs35l56_test_get_efi_ssidexv2);
+
+ KUNIT_EXPECT_EQ(test, 0, cs35l56_set_fw_suffix(cs35l56));
+
+ /* Priority suffix should be the SSIDExV2 string with SoundWire ID */
+ KUNIT_EXPECT_STREQ(test, cs35l56->dsp.fwf_suffix, "01-l1u5");
+
+ /* Fallback suffix should be the SoundWireID */
+ KUNIT_EXPECT_STREQ(test, cs35l56->fallback_fw_suffix, "l1u5");
+}
+
+static void cs35l56_test_ssidexv2_suffix_i2cspi(struct kunit *test)
+{
+ struct cs35l56_test_priv *priv = test->priv;
+ struct cs35l56_private *cs35l56 = priv->cs35l56_priv;
+
+ /* Set the ALSA name prefix */
+ cs35l56->component->name_prefix = "AMP1";
+
+ /* Set a SSID to enable lookup of SSIDExV2 */
+ snd_soc_card_set_pci_ssid(cs35l56->component->card, PCI_VENDOR_ID_DELL, 0x1234);
+
+ priv->ssidexv2 = "10281234_01_BB_CC";
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs35l56_test_get_efi_ssidexv2);
+
+ KUNIT_EXPECT_EQ(test, 0, cs35l56_set_fw_suffix(cs35l56));
+
+ /* Priority suffix should be the SSIDExV2 string with ALSA name prefix */
+ KUNIT_EXPECT_STREQ(test, cs35l56->dsp.fwf_suffix, "01-AMP1");
+
+ /* Fallback suffix should be the ALSA name prefix */
+ KUNIT_EXPECT_STREQ(test, cs35l56->fallback_fw_suffix, "AMP1");
+}
+
+/*
+ * CS35L56 B0 SoundWire should ignore any SSIDExV2 suffix. It isn't needed
+ * on any products with B0 silicon and would interfere with the fallback
+ * to legacy naming convention for early B0-based laptops.
+ */
+static void cs35l56_test_l56_b0_ssidexv2_ignored_suffix_sdw(struct kunit *test)
+{
+ struct cs35l56_test_priv *priv = test->priv;
+ struct cs35l56_private *cs35l56 = priv->cs35l56_priv;
+
+ /* Set device type info */
+ cs35l56->base.type = 0x56;
+ cs35l56->base.rev = 0xb0;
+
+ /* Set the ALSA name prefix */
+ cs35l56->component->name_prefix = "AMP1";
+
+ /* Set SoundWire link and UID number */
+ cs35l56->sdw_link_num = 1;
+ cs35l56->sdw_unique_id = 5;
+
+ /* Set a SSID to enable lookup of SSIDExV2 */
+ snd_soc_card_set_pci_ssid(cs35l56->component->card, PCI_VENDOR_ID_DELL, 0x1234);
+
+ priv->ssidexv2 = "10281234_01_BB_CC";
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs35l56_test_get_efi_ssidexv2);
+
+ KUNIT_EXPECT_EQ(test, 0, cs35l56_set_fw_suffix(cs35l56));
+
+ /* Priority suffix should be the legacy ALSA prefix */
+ KUNIT_EXPECT_STREQ(test, cs35l56->dsp.fwf_suffix, "AMP1");
+
+ /* Fallback suffix should be the new SoundWire ID */
+ KUNIT_EXPECT_STREQ(test, cs35l56->fallback_fw_suffix, "l1u5");
+}
+
+/*
+ * Test that cs35l56_process_xu_properties() correctly parses the GPIO and
+ * pull values from properties into the arrays in struct cs35l56_base.
+ *
+ * This test creates the node tree:
+ *
+ * Node("SWD1") { // top-level device node
+ * Node("AF01") {
+ * Node("mipi-sdca-function-expansion-subproperties") {
+ * property: "01fa-spk-id-gpios-onchip"
+ * property: 01fa-spk-id-gpios-onchip-pull
+ * }
+ * }
+ * }
+ *
+ * Note that in ACPI "mipi-sdca-function-expansion-subproperties" is
+ * a special _DSD property that points to a Device(EXT0) node but behaves
+ * as an alias of the EXT0 node. The equivalent in software nodes is to
+ * create a Node named "mipi-sdca-function-expansion-subproperties" with
+ * the properties.
+ *
+ */
+static void cs35l56_test_parse_xu_onchip_spkid(struct kunit *test)
+{
+ const struct cs35l56_test_param *param = test->param_value;
+ struct cs35l56_test_priv *priv = test->priv;
+ struct cs35l56_private *cs35l56 = priv->cs35l56_priv;
+ struct software_node *ext0_node;
+ int num_gpios = 0;
+ int num_pulls = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++, num_gpios++) {
+ if (param->spkid_gpios[i] < 0)
+ break;
+ }
+ KUNIT_ASSERT_LE(test, num_gpios, ARRAY_SIZE(cs35l56->base.onchip_spkid_gpios));
+
+ for (i = 0; i < ARRAY_SIZE(param->spkid_pulls); i++, num_pulls++) {
+ if (param->spkid_pulls[i] < 0)
+ break;
+ }
+ KUNIT_ASSERT_LE(test, num_pulls, ARRAY_SIZE(cs35l56->base.onchip_spkid_pulls));
+
+ const struct property_entry ext0_props[] = {
+ PROPERTY_ENTRY_U32_ARRAY_LEN("01fa-spk-id-gpios-onchip",
+ param->spkid_gpios, num_gpios),
+ PROPERTY_ENTRY_U32_ARRAY_LEN("01fa-spk-id-gpios-onchip-pull",
+ param->spkid_pulls, num_pulls),
+ { }
+ };
+
+ KUNIT_ASSERT_EQ(test,
+ software_node_register_node_group(cs35l56_test_dev_and_af01_node_group),
+ 0);
+ KUNIT_ASSERT_EQ(test,
+ kunit_add_action_or_reset(test,
+ software_node_unregister_node_group_wrapper,
+ cs35l56_test_dev_and_af01_node_group),
+ 0);
+
+ ext0_node = kunit_kzalloc(test, sizeof(*ext0_node), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, ext0_node);
+ *ext0_node = SOFTWARE_NODE("mipi-sdca-function-expansion-subproperties",
+ ext0_props, &cs35l56_test_af01_sw_node);
+
+ KUNIT_ASSERT_EQ(test, software_node_register(ext0_node), 0);
+ KUNIT_ASSERT_EQ(test,
+ kunit_add_action_or_reset(test,
+ software_node_unregister_wrapper,
+ ext0_node),
+ 0);
+
+ KUNIT_ASSERT_EQ(test,
+ device_add_software_node(cs35l56->base.dev, &cs35l56_test_dev_sw_node), 0);
+ KUNIT_ASSERT_EQ(test, 0,
+ kunit_add_action_or_reset(test,
+ device_remove_software_node_wrapper,
+ cs35l56->base.dev));
+
+ KUNIT_EXPECT_EQ(test, cs35l56_process_xu_properties(cs35l56), 0);
+
+ KUNIT_EXPECT_EQ(test, cs35l56->base.num_onchip_spkid_gpios, num_gpios);
+ KUNIT_EXPECT_EQ(test, cs35l56->base.num_onchip_spkid_pulls, num_pulls);
+
+ for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++) {
+ if (param->spkid_gpios[i] < 0)
+ break;
+
+ /*
+ * cs35l56_process_xu_properties() stores the GPIO numbers
+ * zero-based, which is one less than the value in the property.
+ */
+ KUNIT_EXPECT_EQ_MSG(test, cs35l56->base.onchip_spkid_gpios[i],
+ param->spkid_gpios[i] - 1,
+ "i=%d", i);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(param->spkid_pulls); i++) {
+ if (param->spkid_pulls[i] < 0)
+ break;
+
+ KUNIT_EXPECT_EQ_MSG(test, cs35l56->base.onchip_spkid_pulls[i],
+ param->spkid_pulls[i], "i=%d", i);
+ }
+}
+
+static int cs35l56_test_dummy_read_onchip_spkid(struct cs35l56_base *cs35l56_base)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct cs35l56_test_priv *priv = test->priv;
+
+ priv->read_onchip_spkid_called = true;
+
+ return 4;
+}
+
+static int cs35l56_test_dummy_configure_onchip_spkid_pads(struct cs35l56_base *cs35l56_base)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct cs35l56_test_priv *priv = test->priv;
+
+ priv->configure_onchip_spkid_pads_called = true;
+
+ return 0;
+}
+
+static void cs35l56_test_set_fw_name_reads_onchip_spkid(struct kunit *test)
+{
+ struct cs35l56_test_priv *priv = test->priv;
+ struct cs35l56_private *cs35l56 = priv->cs35l56_priv;
+
+ /* Provide some on-chip GPIOs for spkid */
+ cs35l56->base.onchip_spkid_gpios[0] = 1;
+ cs35l56->base.num_onchip_spkid_gpios = 1;
+
+ cs35l56->speaker_id = -ENOENT;
+
+ kunit_activate_static_stub(test,
+ cs35l56_configure_onchip_spkid_pads,
+ cs35l56_test_dummy_configure_onchip_spkid_pads);
+ kunit_activate_static_stub(test,
+ cs35l56_read_onchip_spkid,
+ cs35l56_test_dummy_read_onchip_spkid);
+
+ priv->configure_onchip_spkid_pads_called = false;
+ priv->read_onchip_spkid_called = false;
+ KUNIT_EXPECT_EQ(test, cs35l56_set_fw_name(cs35l56->component), 0);
+ KUNIT_EXPECT_TRUE(test, priv->configure_onchip_spkid_pads_called);
+ KUNIT_EXPECT_TRUE(test, priv->read_onchip_spkid_called);
+ KUNIT_EXPECT_EQ(test, cs35l56->speaker_id,
+ cs35l56_test_dummy_read_onchip_spkid(&cs35l56->base));
+}
+
+static void cs35l56_test_set_fw_name_preserves_spkid_with_onchip_gpios(struct kunit *test)
+{
+ struct cs35l56_test_priv *priv = test->priv;
+ struct cs35l56_private *cs35l56 = priv->cs35l56_priv;
+
+ /* Provide some on-chip GPIOs for spkid */
+ cs35l56->base.onchip_spkid_gpios[0] = 1;
+ cs35l56->base.num_onchip_spkid_gpios = 1;
+
+ /* Simulate that the driver already got a spkid from somewhere */
+ cs35l56->speaker_id = 15;
+
+ KUNIT_EXPECT_EQ(test, cs35l56_set_fw_name(cs35l56->component), 0);
+ KUNIT_EXPECT_EQ(test, cs35l56->speaker_id, 15);
+}
+
+static void cs35l56_test_set_fw_name_preserves_spkid_without_onchip_gpios(struct kunit *test)
+{
+ struct cs35l56_test_priv *priv = test->priv;
+ struct cs35l56_private *cs35l56 = priv->cs35l56_priv;
+
+ cs35l56->base.num_onchip_spkid_gpios = 0;
+
+ /* Simulate that the driver already got a spkid from somewhere */
+ cs35l56->speaker_id = 15;
+
+ KUNIT_EXPECT_EQ(test, cs35l56_set_fw_name(cs35l56->component), 0);
+ KUNIT_EXPECT_EQ(test, cs35l56->speaker_id, 15);
+}
+
+static int cs35l56_test_case_init_common(struct kunit *test)
+{
+ struct cs35l56_test_priv *priv;
+ const struct cs35l56_test_param *param = test->param_value;
+ struct cs35l56_private *cs35l56;
+
+ KUNIT_ASSERT_NOT_NULL(test, cs_amp_test_hooks);
+
+ priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ test->priv = priv;
+
+ /* Create dummy amp driver dev */
+ priv->amp_dev = faux_device_create("cs35l56_test_drv", NULL, NULL);
+ KUNIT_ASSERT_NOT_NULL(test, priv->amp_dev);
+ KUNIT_ASSERT_EQ(test, 0,
+ kunit_add_action_or_reset(test,
+ faux_device_destroy_wrapper,
+ priv->amp_dev));
+
+ /* Construct minimal set of driver structs */
+ priv->cs35l56_priv = kunit_kzalloc(test, sizeof(*priv->cs35l56_priv), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, priv->cs35l56_priv);
+ cs35l56 = priv->cs35l56_priv;
+ cs35l56->base.dev = &priv->amp_dev->dev;
+
+ cs35l56->component = kunit_kzalloc(test, sizeof(*cs35l56->component), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, cs35l56->component);
+ cs35l56->component->dev = cs35l56->base.dev;
+ snd_soc_component_set_drvdata(cs35l56->component, cs35l56);
+
+ cs35l56->component->card = kunit_kzalloc(test, sizeof(*cs35l56->component->card),
+ GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, cs35l56->component->card);
+
+ if (param) {
+ cs35l56->base.type = param->type;
+ cs35l56->base.rev = param->rev;
+ }
+
+ return 0;
+}
+
+static int cs35l56_test_case_init_soundwire(struct kunit *test)
+{
+ struct cs35l56_test_priv *priv;
+ struct cs35l56_private *cs35l56;
+ int ret;
+
+ ret = cs35l56_test_case_init_common(test);
+ if (ret)
+ return ret;
+
+ priv = test->priv;
+ cs35l56 = priv->cs35l56_priv;
+
+ /* Dummy to indicate this is Soundwire */
+ cs35l56->sdw_peripheral = kunit_kzalloc(test, sizeof(*cs35l56->sdw_peripheral),
+ GFP_KERNEL);
+ if (!cs35l56->sdw_peripheral)
+ return -ENOMEM;
+
+
+ return 0;
+}
+
+static void cs35l56_test_gpio_param_desc(const struct cs35l56_test_param *param, char *desc)
+{
+ DECLARE_SEQ_BUF(gpios, 1 + (2 * ARRAY_SIZE(param->spkid_gpios)));
+ DECLARE_SEQ_BUF(pulls, 1 + (2 * ARRAY_SIZE(param->spkid_pulls)));
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++) {
+ if (param->spkid_gpios[i] < 0)
+ break;
+
+ seq_buf_printf(&gpios, "%s%d", (i == 0) ? "" : ",", param->spkid_gpios[i]);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(param->spkid_pulls); i++) {
+ if (param->spkid_pulls[i] < 0)
+ break;
+
+ seq_buf_printf(&pulls, "%s%d", (i == 0) ? "" : ",", param->spkid_pulls[i]);
+ }
+
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "gpios:{%s} pulls:{%s}",
+ seq_buf_str(&gpios), seq_buf_str(&pulls));
+}
+
+static const struct cs35l56_test_param cs35l56_test_onchip_spkid_cases[] = {
+ { .spkid_gpios = { 1, -1 }, .spkid_pulls = { 1, -1 }, },
+ { .spkid_gpios = { 1, -1 }, .spkid_pulls = { 2, -1 }, },
+
+ { .spkid_gpios = { 7, -1 }, .spkid_pulls = { 1, -1 }, },
+ { .spkid_gpios = { 7, -1 }, .spkid_pulls = { 2, -1 }, },
+
+ { .spkid_gpios = { 1, 7, -1 }, .spkid_pulls = { 1, 1, -1 }, },
+ { .spkid_gpios = { 1, 7, -1 }, .spkid_pulls = { 2, 2, -1 }, },
+
+ { .spkid_gpios = { 7, 1, -1 }, .spkid_pulls = { 1, 1, -1 }, },
+ { .spkid_gpios = { 7, 1, -1 }, .spkid_pulls = { 2, 2, -1 }, },
+
+ { .spkid_gpios = { 3, 7, 1, -1 }, .spkid_pulls = { 1, 1, 1, -1 }, },
+ { .spkid_gpios = { 3, 7, 1, -1 }, .spkid_pulls = { 2, 2, 2, -1 }, },
+};
+KUNIT_ARRAY_PARAM(cs35l56_test_onchip_spkid,
+ cs35l56_test_onchip_spkid_cases,
+ cs35l56_test_gpio_param_desc);
+
+static void cs35l56_test_type_rev_param_desc(const struct cs35l56_test_param *param,
+ char *desc)
+{
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "type: %02x rev: %02x",
+ param->type, param->rev);
+}
+
+static const struct cs35l56_test_param cs35l56_test_type_rev_ex_b0_param_cases[] = {
+ { .type = 0x56, .rev = 0xb2 },
+ { .type = 0x57, .rev = 0xb2 },
+ { .type = 0x63, .rev = 0xa1 },
+};
+KUNIT_ARRAY_PARAM(cs35l56_test_type_rev_ex_b0, cs35l56_test_type_rev_ex_b0_param_cases,
+ cs35l56_test_type_rev_param_desc);
+
+
+static const struct cs35l56_test_param cs35l56_test_type_rev_all_param_cases[] = {
+ { .type = 0x56, .rev = 0xb0 },
+ { .type = 0x56, .rev = 0xb2 },
+ { .type = 0x57, .rev = 0xb2 },
+ { .type = 0x63, .rev = 0xa1 },
+};
+KUNIT_ARRAY_PARAM(cs35l56_test_type_rev_all, cs35l56_test_type_rev_all_param_cases,
+ cs35l56_test_type_rev_param_desc);
+
+static struct kunit_case cs35l56_test_cases_soundwire[] = {
+ KUNIT_CASE(cs35l56_test_l56_b0_suffix_sdw),
+ KUNIT_CASE_PARAM(cs35l56_test_suffix_sdw, cs35l56_test_type_rev_ex_b0_gen_params),
+ KUNIT_CASE_PARAM(cs35l56_test_ssidexv2_suffix_sdw,
+ cs35l56_test_type_rev_ex_b0_gen_params),
+ KUNIT_CASE(cs35l56_test_l56_b0_ssidexv2_ignored_suffix_sdw),
+
+ KUNIT_CASE_PARAM(cs35l56_test_parse_xu_onchip_spkid,
+ cs35l56_test_onchip_spkid_gen_params),
+
+ KUNIT_CASE(cs35l56_test_set_fw_name_reads_onchip_spkid),
+ KUNIT_CASE(cs35l56_test_set_fw_name_preserves_spkid_with_onchip_gpios),
+ KUNIT_CASE(cs35l56_test_set_fw_name_preserves_spkid_without_onchip_gpios),
+
+ { } /* terminator */
+};
+
+static struct kunit_case cs35l56_test_cases_not_soundwire[] = {
+ KUNIT_CASE_PARAM(cs35l56_test_suffix_i2cspi, cs35l56_test_type_rev_all_gen_params),
+ KUNIT_CASE_PARAM(cs35l56_test_ssidexv2_suffix_i2cspi,
+ cs35l56_test_type_rev_all_gen_params),
+
+ KUNIT_CASE(cs35l56_test_set_fw_name_reads_onchip_spkid),
+ KUNIT_CASE(cs35l56_test_set_fw_name_preserves_spkid_with_onchip_gpios),
+ KUNIT_CASE(cs35l56_test_set_fw_name_preserves_spkid_without_onchip_gpios),
+
+ { } /* terminator */
+};
+
+static struct kunit_suite cs35l56_test_suite_soundwire = {
+ .name = "snd-soc-cs35l56-test-soundwire",
+ .init = cs35l56_test_case_init_soundwire,
+ .test_cases = cs35l56_test_cases_soundwire,
+};
+
+static struct kunit_suite cs35l56_test_suite_not_soundwire = {
+ .name = "snd-soc-cs35l56-test-not-soundwire",
+ .init = cs35l56_test_case_init_common,
+ .test_cases = cs35l56_test_cases_not_soundwire,
+};
+
+kunit_test_suites(
+ &cs35l56_test_suite_soundwire,
+ &cs35l56_test_suite_not_soundwire,
+);
+
+MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB");
+MODULE_IMPORT_NS("SND_SOC_CS35L56_SHARED");
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
+MODULE_DESCRIPTION("KUnit test for Cirrus Logic cs35l56 codec driver");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c
index 55b4d0d55712..2ff8b172b76e 100644
--- a/sound/soc/codecs/cs35l56.c
+++ b/sound/soc/codecs/cs35l56.c
@@ -5,6 +5,8 @@
// Copyright (C) 2023 Cirrus Logic, Inc. and
// Cirrus Logic International Semiconductor Ltd.
+#include <kunit/static_stub.h>
+#include <kunit/visibility.h>
#include <linux/acpi.h>
#include <linux/array_size.h>
#include <linux/completion.h>
@@ -1107,43 +1109,98 @@ static const struct snd_kcontrol_new cs35l56_cal_data_restore_controls[] = {
SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE),
};
-static int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56)
+VISIBLE_IF_KUNIT int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56)
{
+ unsigned short vendor, device;
+ const char *vendor_id;
+ int ret;
+
if (cs35l56->dsp.fwf_suffix)
return 0;
- if (!cs35l56->sdw_peripheral)
- return 0;
+ if (cs35l56->sdw_peripheral) {
+ cs35l56->dsp.fwf_suffix = devm_kasprintf(cs35l56->base.dev, GFP_KERNEL,
+ "l%uu%u",
+ cs35l56->sdw_link_num,
+ cs35l56->sdw_unique_id);
+ if (!cs35l56->dsp.fwf_suffix)
+ return -ENOMEM;
- cs35l56->dsp.fwf_suffix = devm_kasprintf(cs35l56->base.dev, GFP_KERNEL,
- "l%uu%u",
- cs35l56->sdw_link_num,
- cs35l56->sdw_unique_id);
- if (!cs35l56->dsp.fwf_suffix)
- return -ENOMEM;
+ /*
+ * There are published firmware files for L56 B0 silicon using
+ * the ALSA prefix as the filename suffix. Default to trying these
+ * first, with the new SoundWire suffix as a fallback.
+ * None of these older systems use a vendor-specific ID.
+ */
+ if ((cs35l56->base.type == 0x56) && (cs35l56->base.rev == 0xb0)) {
+ cs35l56->fallback_fw_suffix = cs35l56->dsp.fwf_suffix;
+ cs35l56->dsp.fwf_suffix = cs35l56->component->name_prefix;
+
+ return 0;
+ }
+ }
/*
- * There are published firmware files for L56 B0 silicon using
- * the ALSA prefix as the filename suffix. Default to trying these
- * first, with the new name as an alternate.
+ * Some manufacturers use the same SSID on multiple products and have
+ * a vendor-specific qualifier to distinguish different models.
+ * Models with the same SSID but different qualifier might require
+ * different audio firmware, or they might all have the same audio
+ * firmware.
+ * Try searching for a firmware with this qualifier first, else
+ * fallback to standard naming.
*/
- if ((cs35l56->base.type == 0x56) && (cs35l56->base.rev == 0xb0)) {
- cs35l56->fallback_fw_suffix = cs35l56->dsp.fwf_suffix;
- cs35l56->dsp.fwf_suffix = cs35l56->component->name_prefix;
+ if (snd_soc_card_get_pci_ssid(cs35l56->component->card, &vendor, &device) < 0) {
+ vendor_id = cs_amp_devm_get_vendor_specific_variant_id(cs35l56->base.dev, -1, -1);
+ } else {
+ vendor_id = cs_amp_devm_get_vendor_specific_variant_id(cs35l56->base.dev,
+ vendor, device);
+ }
+ ret = PTR_ERR_OR_ZERO(vendor_id);
+ if (ret == -ENOENT)
+ return 0;
+ else if (ret)
+ return ret;
+
+ if (vendor_id) {
+ if (cs35l56->dsp.fwf_suffix)
+ cs35l56->fallback_fw_suffix = cs35l56->dsp.fwf_suffix;
+ else
+ cs35l56->fallback_fw_suffix = cs35l56->component->name_prefix;
+
+ cs35l56->dsp.fwf_suffix = devm_kasprintf(cs35l56->base.dev, GFP_KERNEL,
+ "%s-%s",
+ vendor_id,
+ cs35l56->fallback_fw_suffix);
+ if (!cs35l56->dsp.fwf_suffix)
+ return -ENOMEM;
}
return 0;
}
+EXPORT_SYMBOL_IF_KUNIT(cs35l56_set_fw_suffix);
-static int cs35l56_component_probe(struct snd_soc_component *component)
+VISIBLE_IF_KUNIT int cs35l56_set_fw_name(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
- struct dentry *debugfs_root = component->debugfs_root;
unsigned short vendor, device;
int ret;
- BUILD_BUG_ON(ARRAY_SIZE(cs35l56_tx_input_texts) != ARRAY_SIZE(cs35l56_tx_input_values));
+ if ((cs35l56->speaker_id < 0) && cs35l56->base.num_onchip_spkid_gpios) {
+ PM_RUNTIME_ACQUIRE(cs35l56->base.dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ ret = cs35l56_configure_onchip_spkid_pads(&cs35l56->base);
+ if (ret)
+ return ret;
+
+ ret = cs35l56_read_onchip_spkid(&cs35l56->base);
+ if (ret < 0)
+ return ret;
+
+ cs35l56->speaker_id = ret;
+ }
if (!cs35l56->dsp.system_name &&
(snd_soc_card_get_pci_ssid(component->card, &vendor, &device) == 0)) {
@@ -1164,6 +1221,19 @@ static int cs35l56_component_probe(struct snd_soc_component *component)
return -ENOMEM;
}
+ return 0;
+}
+EXPORT_SYMBOL_IF_KUNIT(cs35l56_set_fw_name);
+
+static int cs35l56_component_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+ struct dentry *debugfs_root = component->debugfs_root;
+ int ret;
+
+ BUILD_BUG_ON(ARRAY_SIZE(cs35l56_tx_input_texts) != ARRAY_SIZE(cs35l56_tx_input_values));
+
if (!wait_for_completion_timeout(&cs35l56->init_completion,
msecs_to_jiffies(5000))) {
dev_err(cs35l56->base.dev, "%s: init_completion timed out\n", __func__);
@@ -1175,6 +1245,10 @@ static int cs35l56_component_probe(struct snd_soc_component *component)
return -ENOMEM;
cs35l56->component = component;
+ ret = cs35l56_set_fw_name(component);
+ if (ret)
+ return ret;
+
ret = cs35l56_set_fw_suffix(cs35l56);
if (ret)
return ret;
@@ -1488,6 +1562,105 @@ static int cs35l56_dsp_init(struct cs35l56_private *cs35l56)
return 0;
}
+static int cs35l56_read_fwnode_u32_array(struct device *dev,
+ struct fwnode_handle *parent_node,
+ const char *prop_name,
+ int max_count,
+ u32 *dest)
+{
+ int count, ret;
+
+ count = fwnode_property_count_u32(parent_node, prop_name);
+ if ((count == 0) || (count == -EINVAL) || (count == -ENODATA)) {
+ dev_dbg(dev, "%s not found in %s\n", prop_name, fwnode_get_name(parent_node));
+ return 0;
+ }
+
+ if (count < 0) {
+ dev_err(dev, "Get %s error:%d\n", prop_name, count);
+ return count;
+ }
+
+ if (count > max_count) {
+ dev_err(dev, "%s too many entries (%d)\n", prop_name, count);
+ return -EOVERFLOW;
+ }
+
+ ret = fwnode_property_read_u32_array(parent_node, prop_name, dest, count);
+ if (ret) {
+ dev_err(dev, "Error reading %s: %d\n", prop_name, ret);
+ return ret;
+ }
+
+ return count;
+}
+
+static int cs35l56_process_xu_onchip_speaker_id(struct cs35l56_private *cs35l56,
+ struct fwnode_handle *ext_node)
+{
+ static const char * const gpio_name = "01fa-spk-id-gpios-onchip";
+ static const char * const pull_name = "01fa-spk-id-gpios-onchip-pull";
+ u32 gpios[5], pulls[5];
+ int num_gpios, num_pulls;
+ int ret;
+
+ static_assert(ARRAY_SIZE(gpios) == ARRAY_SIZE(cs35l56->base.onchip_spkid_gpios));
+ static_assert(ARRAY_SIZE(pulls) == ARRAY_SIZE(cs35l56->base.onchip_spkid_pulls));
+
+ num_gpios = cs35l56_read_fwnode_u32_array(cs35l56->base.dev, ext_node, gpio_name,
+ ARRAY_SIZE(gpios), gpios);
+ if (num_gpios < 1)
+ return num_gpios;
+
+ num_pulls = cs35l56_read_fwnode_u32_array(cs35l56->base.dev, ext_node, pull_name,
+ ARRAY_SIZE(pulls), pulls);
+ if (num_pulls < 0)
+ return num_pulls;
+
+ if (num_pulls != num_gpios) {
+ dev_warn(cs35l56->base.dev, "%s count(%d) != %s count(%d)\n",
+ pull_name, num_pulls, gpio_name, num_gpios);
+ }
+
+ ret = cs35l56_check_and_save_onchip_spkid_gpios(&cs35l56->base,
+ gpios, num_gpios,
+ pulls, num_pulls);
+ if (ret) {
+ return dev_err_probe(cs35l56->base.dev, ret, "Error in %s/%s\n",
+ gpio_name, pull_name);
+ }
+
+ return 0;
+}
+
+VISIBLE_IF_KUNIT int cs35l56_process_xu_properties(struct cs35l56_private *cs35l56)
+{
+ struct fwnode_handle *ext_node = NULL;
+ struct fwnode_handle *link;
+ int ret;
+
+ if (!cs35l56->sdw_peripheral)
+ return 0;
+
+ fwnode_for_each_child_node(dev_fwnode(cs35l56->base.dev), link) {
+ ext_node = fwnode_get_named_child_node(link,
+ "mipi-sdca-function-expansion-subproperties");
+ if (ext_node) {
+ fwnode_handle_put(link);
+ break;
+ }
+ }
+
+ if (!ext_node)
+ return 0;
+
+ ret = cs35l56_process_xu_onchip_speaker_id(cs35l56, ext_node);
+ fwnode_handle_put(ext_node);
+
+ return ret;
+}
+EXPORT_SYMBOL_IF_KUNIT(cs35l56_process_xu_properties);
+
static int cs35l56_get_firmware_uid(struct cs35l56_private *cs35l56)
{
struct device *dev = cs35l56->base.dev;
@@ -1668,6 +1841,10 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56)
if (ret != 0)
goto err;
+ ret = cs35l56_process_xu_properties(cs35l56);
+ if (ret)
+ goto err;
+
ret = cs35l56_dsp_init(cs35l56);
if (ret < 0) {
dev_err_probe(cs35l56->base.dev, ret, "DSP init failed\n");
diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h
index 4c59f92f3206..691f857d0bd8 100644
--- a/sound/soc/codecs/cs35l56.h
+++ b/sound/soc/codecs/cs35l56.h
@@ -74,4 +74,10 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56);
int cs35l56_init(struct cs35l56_private *cs35l56);
void cs35l56_remove(struct cs35l56_private *cs35l56);
+#if IS_ENABLED(CONFIG_KUNIT)
+int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56);
+int cs35l56_set_fw_name(struct snd_soc_component *component);
+int cs35l56_process_xu_properties(struct cs35l56_private *cs35l56);
+#endif
+
#endif /* ifndef CS35L56_H */
diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c
index 77dfc83a3c01..d8cdd37e9112 100644
--- a/sound/soc/codecs/cs4271.c
+++ b/sound/soc/codecs/cs4271.c
@@ -528,7 +528,7 @@ static int cs4271_soc_resume(struct snd_soc_component *component)
ret = clk_prepare_enable(cs4271->clk);
if (ret) {
dev_err(component->dev, "Failed to enable clk: %d\n", ret);
- return ret;
+ goto err_disable_regulators;
}
/* Do a proper reset after power up */
@@ -537,15 +537,21 @@ static int cs4271_soc_resume(struct snd_soc_component *component)
/* Restore codec state */
ret = regcache_sync(cs4271->regmap);
if (ret < 0)
- return ret;
+ goto err_disable_clk;
/* then disable the power-down bit */
ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2,
CS4271_MODE2_PDN, 0);
if (ret < 0)
- return ret;
+ goto err_disable_clk;
return 0;
+
+err_disable_clk:
+ clk_disable_unprepare(cs4271->clk);
+err_disable_regulators:
+ regulator_bulk_disable(ARRAY_SIZE(cs4271->supplies), cs4271->supplies);
+ return ret;
}
#else
#define cs4271_soc_suspend NULL
diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c
index 0a2b50cdea95..19f69a523f22 100644
--- a/sound/soc/codecs/da7213.c
+++ b/sound/soc/codecs/da7213.c
@@ -79,8 +79,8 @@ static const char * const da7213_audio_hpf_corner_txt[] = {
};
static SOC_ENUM_SINGLE_DECL(da7213_dac_audio_hpf_corner,
- DA7213_DAC_FILTERS1
- , DA7213_AUDIO_HPF_CORNER_SHIFT,
+ DA7213_DAC_FILTERS1,
+ DA7213_AUDIO_HPF_CORNER_SHIFT,
da7213_audio_hpf_corner_txt);
static SOC_ENUM_SINGLE_DECL(da7213_adc_audio_hpf_corner,
diff --git a/sound/soc/codecs/es8323.c b/sound/soc/codecs/es8323.c
index eb85b71e87f3..605375b154c8 100644
--- a/sound/soc/codecs/es8323.c
+++ b/sound/soc/codecs/es8323.c
@@ -12,6 +12,7 @@
// Further cleanup and restructuring by:
// Binbin Zhou <zhoubinbin@loongson.cn>
+#include <linux/bitfield.h>
#include <linux/module.h>
#include <linux/acpi.h>
#include <linux/clk.h>
@@ -32,7 +33,6 @@ struct es8323_priv {
struct clk *mclk;
struct regmap *regmap;
struct snd_pcm_hw_constraint_list *sysclk_constraints;
- struct snd_soc_component *component;
};
/* es8323 register cache */
@@ -52,7 +52,7 @@ static const struct reg_default es8323_reg_defaults[] = {
{ ES8323_ADCCONTROL4, 0x00 },
{ ES8323_ADCCONTROL5, 0x06 },
{ ES8323_ADCCONTROL6, 0x30 },
- { ES8323_ADC_MUTE, 0x30 },
+ { ES8323_ADCCONTROL7, 0x30 },
{ ES8323_LADC_VOL, 0xc0 },
{ ES8323_RADC_VOL, 0xc0 },
{ ES8323_ADCCONTROL10, 0x38 },
@@ -62,7 +62,7 @@ static const struct reg_default es8323_reg_defaults[] = {
{ ES8323_ADCCONTROL14, 0x00 },
{ ES8323_DACCONTROL1, 0x00 },
{ ES8323_DACCONTROL2, 0x06 },
- { ES8323_DAC_MUTE, 0x30 },
+ { ES8323_DACCONTROL3, 0x30 },
{ ES8323_LDAC_VOL, 0xc0 },
{ ES8323_RDAC_VOL, 0xc0 },
{ ES8323_DACCONTROL6, 0x08 },
@@ -119,29 +119,43 @@ static const struct snd_kcontrol_new es8323_snd_controls[] = {
SOC_ENUM("ALC Capture NG Type", es8323_alc_ng_type_enum),
SOC_ENUM("Playback De-emphasis", es8323_playback_deemphasis_enum),
SOC_ENUM("Capture Polarity", es8323_capture_polarity_enum),
- SOC_SINGLE("ALC Capture ZC Switch", ES8323_ADCCONTROL13, 6, 1, 0),
- SOC_SINGLE("ALC Capture Decay Time", ES8323_ADCCONTROL12, 4, 15, 0),
- SOC_SINGLE("ALC Capture Attack Time", ES8323_ADCCONTROL12, 0, 15, 0),
- SOC_SINGLE("ALC Capture NG Threshold", ES8323_ADCCONTROL14, 3, 31, 0),
- SOC_SINGLE("ALC Capture NG Switch", ES8323_ADCCONTROL14, 0, 1, 0),
- SOC_SINGLE("ZC Timeout Switch", ES8323_ADCCONTROL13, 6, 1, 0),
- SOC_SINGLE("Capture Mute Switch", ES8323_ADC_MUTE, 2, 1, 0),
- SOC_SINGLE_TLV("Left Channel Capture Volume", ES8323_ADCCONTROL1, 4, 8,
- 0, es8323_bypass_tlv),
- SOC_SINGLE_TLV("Right Channel Capture Volume", ES8323_ADCCONTROL1, 0,
- 8, 0, es8323_bypass_tlv),
- SOC_SINGLE_TLV("Left Mixer Left Bypass Volume", ES8323_DACCONTROL17, 3,
- 7, 1, es8323_bypass_tlv2),
+
+ SOC_SINGLE("ALC Capture ZC Switch", ES8323_ADCCONTROL13,
+ ES8323_ADCCONTROL13_ALCZC_OFF, 1, 0),
+ SOC_SINGLE("ALC Capture Decay Time", ES8323_ADCCONTROL12,
+ ES8323_ADCCONTROL12_ALCDCY_OFF, 15, 0),
+ SOC_SINGLE("ALC Capture Attack Time", ES8323_ADCCONTROL12,
+ ES8323_ADCCONTROL12_ALCATK_OFF, 15, 0),
+ SOC_SINGLE("ALC Capture NG Threshold", ES8323_ADCCONTROL14,
+ ES8323_ADCCONTROL14_NGTH_OFF, 31, 0),
+ SOC_SINGLE("ALC Capture NG Switch", ES8323_ADCCONTROL14,
+ ES8323_ADCCONTROL14_NGAT_OFF, 1, 0),
+ SOC_SINGLE("ZC Timeout Switch", ES8323_ADCCONTROL13,
+ ES8323_ADCCONTROL13_TIMEOUT_OFF, 1, 0),
+ SOC_SINGLE("Capture Mute Switch", ES8323_ADCCONTROL7,
+ ES8323_ADCCONTROL7_ADCMUTE_OFF, 1, 0),
+
+ SOC_SINGLE_TLV("Left Channel Capture Volume", ES8323_ADCCONTROL1,
+ ES8323_ADCCONTROL1_MICAMPL_OFF, 8, 0, es8323_bypass_tlv),
+ SOC_SINGLE_TLV("Right Channel Capture Volume", ES8323_ADCCONTROL1,
+ ES8323_ADCCONTROL1_MICAMPR_OFF, 8, 0, es8323_bypass_tlv),
+ SOC_SINGLE_TLV("Left Mixer Left Bypass Volume", ES8323_DACCONTROL17,
+ ES8323_DACCONTROL17_LI2LOVOL_OFF, 7, 1, es8323_bypass_tlv2),
SOC_SINGLE_TLV("Right Mixer Right Bypass Volume", ES8323_DACCONTROL20,
- 3, 7, 1, es8323_bypass_tlv2),
- SOC_DOUBLE_R_TLV("PCM Volume", ES8323_LDAC_VOL, ES8323_RDAC_VOL,
+ ES8323_DACCONTROL20_RI2ROVOL_OFF, 7, 1, es8323_bypass_tlv2),
+
+ SOC_DOUBLE_R_TLV("PCM Volume",
+ ES8323_LDAC_VOL, ES8323_RDAC_VOL,
0, 192, 1, es8323_dac_tlv),
- SOC_DOUBLE_R_TLV("Capture Digital Volume", ES8323_LADC_VOL,
- ES8323_RADC_VOL, 0, 192, 1, es8323_adc_tlv),
- SOC_DOUBLE_R_TLV("Output 1 Playback Volume", ES8323_LOUT1_VOL,
- ES8323_ROUT1_VOL, 0, 33, 0, es8323_out_tlv),
- SOC_DOUBLE_R_TLV("Output 2 Playback Volume", ES8323_LOUT2_VOL,
- ES8323_ROUT2_VOL, 0, 33, 0, es8323_out_tlv),
+ SOC_DOUBLE_R_TLV("Capture Digital Volume",
+ ES8323_LADC_VOL, ES8323_RADC_VOL,
+ 0, 192, 1, es8323_adc_tlv),
+ SOC_DOUBLE_R_TLV("Output 1 Playback Volume",
+ ES8323_LOUT1_VOL, ES8323_ROUT1_VOL,
+ 0, 33, 0, es8323_out_tlv),
+ SOC_DOUBLE_R_TLV("Output 2 Playback Volume",
+ ES8323_LOUT2_VOL, ES8323_ROUT2_VOL,
+ 0, 33, 0, es8323_out_tlv),
};
/* Left DAC Route */
@@ -198,21 +212,50 @@ static const struct snd_soc_dapm_widget es8323_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("RINPUT1"),
SND_SOC_DAPM_INPUT("RINPUT2"),
- SND_SOC_DAPM_MICBIAS("Mic Bias", SND_SOC_NOPM, 3, 1),
+ SND_SOC_DAPM_SUPPLY("Mic Bias", ES8323_ADCPOWER,
+ ES8323_ADCPOWER_PDNMICB_OFF, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mic Bias Gen", ES8323_ADCPOWER,
+ ES8323_ADCPOWER_PDNADCBIS_OFF, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DAC STM", ES8323_CHIPPOWER,
+ ES8323_CHIPPOWER_DACSTM_RESET, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC STM", ES8323_CHIPPOWER,
+ ES8323_CHIPPOWER_ADCSTM_RESET, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC DIG", ES8323_CHIPPOWER,
+ ES8323_CHIPPOWER_DACDIG_OFF, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC DIG", ES8323_CHIPPOWER,
+ ES8323_CHIPPOWER_ADCDIG_OFF, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC DLL", ES8323_CHIPPOWER,
+ ES8323_CHIPPOWER_DACDLL_OFF, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC DLL", ES8323_CHIPPOWER,
+ ES8323_CHIPPOWER_ADCDLL_OFF, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Vref", ES8323_CHIPPOWER,
+ ES8323_CHIPPOWER_ADCVREF_OFF, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Vref", ES8323_CHIPPOWER,
+ ES8323_CHIPPOWER_DACVREF_OFF, 1, NULL, 0),
/* Muxes */
- SND_SOC_DAPM_MUX("Left PGA Mux", SND_SOC_NOPM, 0, 0, &es8323_left_dac_mux_controls),
- SND_SOC_DAPM_MUX("Right PGA Mux", SND_SOC_NOPM, 0, 0, &es8323_right_dac_mux_controls),
+ SND_SOC_DAPM_MUX("Left PGA Mux", ES8323_ADCPOWER,
+ ES8323_ADCPOWER_PDNAINL_OFF, 1, &es8323_left_dac_mux_controls),
+ SND_SOC_DAPM_MUX("Right PGA Mux", ES8323_ADCPOWER,
+ ES8323_ADCPOWER_PDNAINR_OFF, 1, &es8323_right_dac_mux_controls),
+
SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0, &es8323_diffmux_controls),
+
SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0, &es8323_mono_adc_mux_controls),
SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0, &es8323_mono_adc_mux_controls),
+
SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0, &es8323_left_line_controls),
SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0, &es8323_right_line_controls),
- SND_SOC_DAPM_ADC("Right ADC", "Right Capture", SND_SOC_NOPM, 4, 1),
- SND_SOC_DAPM_ADC("Left ADC", "Left Capture", SND_SOC_NOPM, 5, 1),
- SND_SOC_DAPM_DAC("Right DAC", "Right Playback", ES8323_DACPOWER, 6, 1),
- SND_SOC_DAPM_DAC("Left DAC", "Left Playback", ES8323_DACPOWER, 7, 1),
+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture",
+ ES8323_ADCPOWER, ES8323_ADCPOWER_PDNADCR_OFF, 1),
+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture",
+ ES8323_ADCPOWER, ES8323_ADCPOWER_PDNADCL_OFF, 1),
+ SND_SOC_DAPM_DAC("Right DAC", "Right Playback",
+ ES8323_DACPOWER, ES8323_DACPOWER_PDNDACR_OFF, 1),
+ SND_SOC_DAPM_DAC("Left DAC", "Left Playback",
+ ES8323_DACPOWER, ES8323_DACPOWER_PDNDACL_OFF, 1),
SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
&es8323_left_mixer_controls[0],
@@ -221,14 +264,12 @@ static const struct snd_soc_dapm_widget es8323_dapm_widgets[] = {
&es8323_right_mixer_controls[0],
ARRAY_SIZE(es8323_right_mixer_controls)),
- SND_SOC_DAPM_PGA("Right ADC Power", SND_SOC_NOPM, 6, 1, NULL, 0),
- SND_SOC_DAPM_PGA("Left ADC Power", SND_SOC_NOPM, 7, 1, NULL, 0),
- SND_SOC_DAPM_PGA("Right Out 2", ES8323_DACPOWER, 2, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Left Out 2", ES8323_DACPOWER, 3, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Right Out 1", ES8323_DACPOWER, 4, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Left Out 1", ES8323_DACPOWER, 5, 0, NULL, 0),
- SND_SOC_DAPM_PGA("LAMP", ES8323_ADCCONTROL1, 4, 0, NULL, 0),
- SND_SOC_DAPM_PGA("RAMP", ES8323_ADCCONTROL1, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Out 2", ES8323_DACPOWER, ES8323_DACPOWER_ROUT2_OFF, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Left Out 2", ES8323_DACPOWER, ES8323_DACPOWER_LOUT2_OFF, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Out 1", ES8323_DACPOWER, ES8323_DACPOWER_ROUT1_OFF, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Left Out 1", ES8323_DACPOWER, ES8323_DACPOWER_LOUT1_OFF, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("LAMP", ES8323_ADCCONTROL1, ES8323_ADCCONTROL1_MICAMPL_OFF, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("RAMP", ES8323_ADCCONTROL1, ES8323_ADCCONTROL1_MICAMPR_OFF, 0, NULL, 0),
SND_SOC_DAPM_OUTPUT("LOUT1"),
SND_SOC_DAPM_OUTPUT("ROUT1"),
@@ -252,18 +293,30 @@ static const struct snd_soc_dapm_route es8323_dapm_routes[] = {
{"Differential Mux", "Line 2", "LINPUT2"},
{"Differential Mux", "Line 2", "RINPUT2"},
- {"Left ADC Mux", "Stereo", "Right PGA Mux"},
{"Left ADC Mux", "Stereo", "Left PGA Mux"},
{"Left ADC Mux", "Mono (Left)", "Left PGA Mux"},
- {"Right ADC Mux", "Stereo", "Left PGA Mux"},
{"Right ADC Mux", "Stereo", "Right PGA Mux"},
{"Right ADC Mux", "Mono (Right)", "Right PGA Mux"},
- {"Left ADC Power", NULL, "Left ADC Mux"},
- {"Right ADC Power", NULL, "Right ADC Mux"},
- {"Left ADC", NULL, "Left ADC Power"},
- {"Right ADC", NULL, "Right ADC Power"},
+ {"Left ADC", NULL, "Left ADC Mux"},
+ {"Right ADC", NULL, "Right ADC Mux"},
+
+ { "Mic Bias", NULL, "Mic Bias Gen" },
+
+ { "ADC DIG", NULL, "ADC STM" },
+ { "ADC DIG", NULL, "ADC Vref" },
+ { "ADC DIG", NULL, "ADC DLL" },
+
+ { "Left ADC", NULL, "ADC DIG" },
+ { "Right ADC", NULL, "ADC DIG" },
+
+ { "DAC DIG", NULL, "DAC STM" },
+ { "DAC DIG", NULL, "DAC Vref" },
+ { "DAC DIG", NULL, "DAC DLL" },
+
+ { "Left DAC", NULL, "DAC DIG" },
+ { "Right DAC", NULL, "DAC DIG" },
{"Left Line Mux", "Line 1L", "LINPUT1"},
{"Left Line Mux", "Line 2L", "LINPUT2"},
@@ -423,16 +476,18 @@ static int es8323_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int es8323_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
struct snd_soc_component *component = codec_dai->component;
- u8 iface = snd_soc_component_read(component, ES8323_MASTERMODE);
- u8 adciface = snd_soc_component_read(component, ES8323_ADC_IFACE);
- u8 daciface = snd_soc_component_read(component, ES8323_DAC_IFACE);
+ u8 format_mode, inv_mode;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_BC_FP:
- iface |= 0x80;
+ /* Master serial port mode */
+ snd_soc_component_update_bits(component, ES8323_MASTERMODE,
+ ES8323_MASTERMODE_MSC, ES8323_MASTERMODE_MSC);
break;
case SND_SOC_DAIFMT_BC_FC:
- iface &= 0x7f;
+ /* Slave serial port mode */
+ snd_soc_component_update_bits(component, ES8323_MASTERMODE,
+ ES8323_MASTERMODE_MSC, 0);
break;
default:
return -EINVAL;
@@ -441,55 +496,47 @@ static int es8323_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
/* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
- adciface &= 0xfc;
- daciface &= 0xf8;
+ format_mode = ES8323_FMT_I2S;
break;
case SND_SOC_DAIFMT_LEFT_J:
- adciface &= 0xfd;
- daciface &= 0xf9;
+ format_mode = ES8323_FMT_LEFT_J;
break;
case SND_SOC_DAIFMT_RIGHT_J:
- adciface &= 0xfe;
- daciface &= 0xfa;
+ format_mode = ES8323_FMT_RIGHT_J;
break;
case SND_SOC_DAIFMT_DSP_A:
case SND_SOC_DAIFMT_DSP_B:
- adciface &= 0xff;
- daciface &= 0xfb;
+ format_mode = ES8323_FMT_DSP;
break;
default:
return -EINVAL;
}
+ snd_soc_component_write_field(component, ES8323_ADCCONTROL4,
+ ES8323_ADCCONTROL4_ADCFORMAT, format_mode);
+ snd_soc_component_write_field(component, ES8323_DACCONTROL1,
+ ES8323_DACCONTROL1_DACFORMAT, format_mode);
+
/* clock inversion */
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
- iface &= 0xdf;
- adciface &= 0xdf;
- daciface &= 0xbf;
- break;
- case SND_SOC_DAIFMT_IB_IF:
- iface |= 0x20;
- adciface |= 0x20;
- daciface |= 0x40;
- break;
case SND_SOC_DAIFMT_IB_NF:
- iface |= 0x20;
- adciface &= 0xdf;
- daciface &= 0xbf;
+ inv_mode = 0;
break;
+ case SND_SOC_DAIFMT_IB_IF:
case SND_SOC_DAIFMT_NB_IF:
- iface &= 0xdf;
- adciface |= 0x20;
- daciface |= 0x40;
+ inv_mode = 1;
break;
default:
return -EINVAL;
}
- snd_soc_component_write(component, ES8323_MASTERMODE, iface);
- snd_soc_component_write(component, ES8323_ADC_IFACE, adciface);
- snd_soc_component_write(component, ES8323_DAC_IFACE, daciface);
+ snd_soc_component_update_bits(component, ES8323_MASTERMODE,
+ ES8323_MASTERMODE_BCLKINV, inv_mode);
+ snd_soc_component_update_bits(component, ES8323_ADCCONTROL4,
+ ES8323_ADCCONTROL4_ADCLRP, inv_mode);
+ snd_soc_component_update_bits(component, ES8323_DACCONTROL1,
+ ES8323_DACCONTROL1_DACLRP, inv_mode);
return 0;
}
@@ -515,66 +562,65 @@ static int es8323_pcm_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_component *component = dai->component;
struct es8323_priv *es8323 = snd_soc_component_get_drvdata(component);
- u16 srate = snd_soc_component_read(component, ES8323_MASTERMODE) & 0x80;
- u16 adciface = snd_soc_component_read(component, ES8323_ADC_IFACE) & 0xe3;
- u16 daciface = snd_soc_component_read(component, ES8323_DAC_IFACE) & 0xc7;
+ u8 wl_mode, fs;
int coeff;
coeff = get_coeff(es8323->sysclk, params_rate(params));
if (coeff < 0) {
coeff = get_coeff(es8323->sysclk / 2, params_rate(params));
- srate |= 0x40;
+ if (coeff < 0) {
+ dev_err(component->dev,
+ "Unable to configure sample rate %dHz with %dHz MCLK\n",
+ params_rate(params), es8323->sysclk);
+ return coeff;
+ }
+
+ snd_soc_component_update_bits(component, ES8323_MASTERMODE,
+ ES8323_MASTERMODE_MCLKDIV2,
+ ES8323_MASTERMODE_MCLKDIV2);
}
- if (coeff < 0) {
- dev_err(component->dev,
- "Unable to configure sample rate %dHz with %dHz MCLK\n",
- params_rate(params), es8323->sysclk);
- return coeff;
- }
+ fs = FIELD_PREP(ES8323_DACCONTROL2_DACFSMODE, es8323_coeff_div[coeff].usb)
+ | FIELD_PREP(ES8323_DACCONTROL2_DACFSRATIO, es8323_coeff_div[coeff].sr);
+
+ snd_soc_component_write_field(component, ES8323_ADCCONTROL5,
+ ES8323_ADCCONTROL5_ADCFS_MASK, fs);
- /* bit size */
+ snd_soc_component_write_field(component, ES8323_DACCONTROL2,
+ ES8323_DACCONTROL2_DACFS_MASK, fs);
+
+ /* serial audio data word length */
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
- adciface |= 0xc;
- daciface |= 0x18;
+ wl_mode = ES8323_S16_LE;
break;
case SNDRV_PCM_FORMAT_S20_3LE:
- adciface |= 0x4;
- daciface |= 0x8;
+ wl_mode = ES8323_S20_LE;
break;
case SNDRV_PCM_FORMAT_S24_LE:
+ wl_mode = ES8323_S24_LE;
break;
case SNDRV_PCM_FORMAT_S32_LE:
- adciface |= 0x10;
- daciface |= 0x20;
+ wl_mode = ES8323_S32_LE;
break;
+ default:
+ return -EINVAL;
}
- snd_soc_component_write(component, ES8323_DAC_IFACE, daciface);
- snd_soc_component_write(component, ES8323_ADC_IFACE, adciface);
+ snd_soc_component_write_field(component, ES8323_ADCCONTROL4,
+ ES8323_ADCCONTROL4_ADCWL, wl_mode);
- snd_soc_component_write(component, ES8323_MASTERMODE, srate);
- snd_soc_component_write(component, ES8323_ADCCONTROL5,
- es8323_coeff_div[coeff].sr |
- (es8323_coeff_div[coeff].usb) << 4);
- snd_soc_component_write(component, ES8323_DACCONTROL2,
- es8323_coeff_div[coeff].sr |
- (es8323_coeff_div[coeff].usb) << 4);
-
- snd_soc_component_write(component, ES8323_DACPOWER, 0x3c);
+ snd_soc_component_write_field(component, ES8323_DACCONTROL1,
+ ES8323_DACCONTROL1_DACWL, wl_mode);
return 0;
}
static int es8323_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
{
- struct snd_soc_component *component = dai->component;
- u32 val = mute ? 0x6 : 0x2;
-
- snd_soc_component_write(component, ES8323_DAC_MUTE, val);
-
- return 0;
+ return snd_soc_component_update_bits(dai->component, ES8323_DACCONTROL3,
+ ES8323_DACCONTROL3_DACMUTE,
+ mute ? ES8323_DACCONTROL3_DACMUTE : 0);
}
static const struct snd_soc_dai_ops es8323_ops = {
@@ -613,8 +659,6 @@ static int es8323_probe(struct snd_soc_component *component)
struct es8323_priv *es8323 = snd_soc_component_get_drvdata(component);
int ret;
- es8323->component = component;
-
es8323->mclk = devm_clk_get_optional(component->dev, "mclk");
if (IS_ERR(es8323->mclk)) {
dev_err(component->dev, "unable to get mclk\n");
@@ -631,7 +675,7 @@ static int es8323_probe(struct snd_soc_component *component)
}
snd_soc_component_write(component, ES8323_CONTROL2, 0x60);
- snd_soc_component_write(component, ES8323_CHIPPOWER, 0x00);
+ snd_soc_component_write(component, ES8323_DACCONTROL21, 0x80);
return 0;
}
@@ -644,37 +688,23 @@ static int es8323_set_bias_level(struct snd_soc_component *component,
switch (level) {
case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
ret = clk_prepare_enable(es8323->mclk);
if (ret)
return ret;
- snd_soc_component_write(component, ES8323_CHIPPOWER, 0xf0);
- usleep_range(18000, 20000);
- snd_soc_component_write(component, ES8323_DACPOWER, 0x3c);
- snd_soc_component_write(component, ES8323_ANAVOLMANAG, 0x7c);
snd_soc_component_write(component, ES8323_CHIPLOPOW1, 0x00);
snd_soc_component_write(component, ES8323_CHIPLOPOW2, 0x00);
- snd_soc_component_write(component, ES8323_CHIPPOWER, 0x00);
- snd_soc_component_write(component, ES8323_ADCPOWER, 0x09);
- snd_soc_component_write(component, ES8323_ADCCONTROL14, 0x00);
- break;
- case SND_SOC_BIAS_PREPARE:
+ snd_soc_component_update_bits(component, ES8323_ADCPOWER,
+ ES8323_ADCPOWER_PDNADCBIS, 0);
break;
case SND_SOC_BIAS_STANDBY:
- snd_soc_component_write(component, ES8323_ANAVOLMANAG, 0x7c);
- snd_soc_component_write(component, ES8323_CHIPLOPOW1, 0x00);
- snd_soc_component_write(component, ES8323_CHIPLOPOW2, 0x00);
- snd_soc_component_write(component, ES8323_CHIPPOWER, 0x00);
- snd_soc_component_write(component, ES8323_ADCPOWER, 0x59);
break;
case SND_SOC_BIAS_OFF:
- clk_disable_unprepare(es8323->mclk);
- snd_soc_component_write(component, ES8323_ADCPOWER, 0xff);
- snd_soc_component_write(component, ES8323_DACPOWER, 0xC0);
snd_soc_component_write(component, ES8323_CHIPLOPOW1, 0xff);
snd_soc_component_write(component, ES8323_CHIPLOPOW2, 0xff);
- snd_soc_component_write(component, ES8323_CHIPPOWER, 0xff);
- snd_soc_component_write(component, ES8323_ANAVOLMANAG, 0x7b);
+ clk_disable_unprepare(es8323->mclk);
break;
}
diff --git a/sound/soc/codecs/es8323.h b/sound/soc/codecs/es8323.h
index f986c9301dc6..6e37e0480f04 100644
--- a/sound/soc/codecs/es8323.h
+++ b/sound/soc/codecs/es8323.h
@@ -16,33 +16,124 @@
#define ES8323_CONTROL1 0x00
#define ES8323_CONTROL2 0x01
#define ES8323_CHIPPOWER 0x02
+
+#define ES8323_CHIPPOWER_DACVREF_OFF 0
+#define ES8323_CHIPPOWER_ADCVREF_OFF 1
+#define ES8323_CHIPPOWER_DACDLL_OFF 2
+#define ES8323_CHIPPOWER_ADCDLL_OFF 3
+#define ES8323_CHIPPOWER_DACSTM_RESET 4
+#define ES8323_CHIPPOWER_ADCSTM_RESET 5
+#define ES8323_CHIPPOWER_DACDIG_OFF 6
+#define ES8323_CHIPPOWER_ADCDIG_OFF 7
+
#define ES8323_ADCPOWER 0x03
+
+#define ES8323_ADCPOWER_INT1LP BIT(0)
+#define ES8323_ADCPOWER_FLASHLP BIT(1)
+#define ES8323_ADCPOWER_PDNADCBIS BIT(2)
+#define ES8323_ADCPOWER_PDNMICB BIT(3)
+
+#define ES8323_ADCPOWER_PDNADCBIS_OFF 2
+#define ES8323_ADCPOWER_PDNMICB_OFF 3
+#define ES8323_ADCPOWER_PDNADCR_OFF 4
+#define ES8323_ADCPOWER_PDNADCL_OFF 5
+#define ES8323_ADCPOWER_PDNAINR_OFF 6
+#define ES8323_ADCPOWER_PDNAINL_OFF 7
+
#define ES8323_DACPOWER 0x04
+
+#define ES8323_DACPOWER_ROUT2_OFF 2
+#define ES8323_DACPOWER_LOUT2_OFF 3
+#define ES8323_DACPOWER_ROUT1_OFF 4
+#define ES8323_DACPOWER_LOUT1_OFF 5
+#define ES8323_DACPOWER_PDNDACR_OFF 6
+#define ES8323_DACPOWER_PDNDACL_OFF 7
+
#define ES8323_CHIPLOPOW1 0x05
#define ES8323_CHIPLOPOW2 0x06
#define ES8323_ANAVOLMANAG 0x07
#define ES8323_MASTERMODE 0x08
+#define ES8323_MASTERMODE_BCLKDIV GENMASK(4, 0)
+#define ES8323_MASTERMODE_BCLKINV BIT(5)
+#define ES8323_MASTERMODE_MCLKDIV2 BIT(6)
+#define ES8323_MASTERMODE_MSC BIT(7)
+
/* ADC Control */
#define ES8323_ADCCONTROL1 0x09
+
+#define ES8323_ADCCONTROL1_MICAMPR_OFF 0
+#define ES8323_ADCCONTROL1_MICAMPL_OFF 4
+
#define ES8323_ADCCONTROL2 0x0a
#define ES8323_ADCCONTROL3 0x0b
#define ES8323_ADCCONTROL4 0x0c
+
+#define ES8323_ADCCONTROL4_ADCFORMAT GENMASK(1, 0)
+#define ES8323_FMT_I2S 0x0
+#define ES8323_FMT_LEFT_J 0x1
+#define ES8323_FMT_RIGHT_J 0x2
+#define ES8323_FMT_DSP 0x3
+#define ES8323_ADCCONTROL4_ADCWL GENMASK(4, 2)
+#define ES8323_S24_LE 0x0
+#define ES8323_S20_LE 0x1
+#define ES8323_S18_LE 0x2
+#define ES8323_S16_LE 0x3
+#define ES8323_S32_LE 0x4
+#define ES8323_ADCCONTROL4_ADCLRP BIT(5)
+#define ES8323_ADCCONTROL4_DATSEL GENMASK(7, 6)
+
#define ES8323_ADCCONTROL5 0x0d
+
+#define ES8323_ADCCONTROL5_ADCFSRATIO GENMASK(4, 0)
+#define ES8323_ADCCONTROL5_ADCFSMODE BIT(5)
+#define ES8323_ADCCONTROL5_ADCFS_MASK (ES8323_ADCCONTROL5_ADCFSRATIO |\
+ ES8323_ADCCONTROL5_ADCFSMODE)
+
#define ES8323_ADCCONTROL6 0x0e
-#define ES8323_ADC_MUTE 0x0f
+#define ES8323_ADCCONTROL7 0x0f
+
+#define ES8323_ADCCONTROL7_ADCMUTE_OFF 2
+
#define ES8323_LADC_VOL 0x10
#define ES8323_RADC_VOL 0x11
#define ES8323_ADCCONTROL10 0x12
#define ES8323_ADCCONTROL11 0x13
#define ES8323_ADCCONTROL12 0x14
+
+#define ES8323_ADCCONTROL12_ALCATK_OFF 0
+#define ES8323_ADCCONTROL12_ALCDCY_OFF 4
+
#define ES8323_ADCCONTROL13 0x15
+
+#define ES8323_ADCCONTROL13_TIMEOUT_OFF 5
+#define ES8323_ADCCONTROL13_ALCZC_OFF 6
+
#define ES8323_ADCCONTROL14 0x16
+#define ES8323_ADCCONTROL14_NGAT_OFF 0
+#define ES8323_ADCCONTROL14_NGG_OFF 1
+#define ES8323_ADCCONTROL14_NGTH_OFF 3
+
/* DAC Control */
#define ES8323_DACCONTROL1 0x17
+
+#define ES8323_DACCONTROL1_DACFORMAT GENMASK(1, 0)
+#define ES8323_DACCONTROL1_DACWL GENMASK(5, 3)
+#define ES8323_DACCONTROL1_DACLRP BIT(6)
+#define ES8323_DACCONTROL1_DACLRSWAP BIT(7)
+
#define ES8323_DACCONTROL2 0x18
-#define ES8323_DAC_MUTE 0x19
+
+#define ES8323_DACCONTROL2_DACFSRATIO GENMASK(4, 0)
+#define ES8323_DACCONTROL2_DACFSMODE BIT(5)
+#define ES8323_DACCONTROL2_DACFS_MASK (ES8323_DACCONTROL2_DACFSRATIO |\
+ ES8323_DACCONTROL2_DACFSMODE)
+
+#define ES8323_DACCONTROL3 0x19
+
+#define ES8323_DACCONTROL3_DACMUTE BIT(2)
+
#define ES8323_LDAC_VOL 0x1a
#define ES8323_RDAC_VOL 0x1b
#define ES8323_DACCONTROL6 0x1c
@@ -57,9 +148,15 @@
#define ES8323_DACCONTROL15 0x25
#define ES8323_DACCONTROL16 0x26
#define ES8323_DACCONTROL17 0x27
+
+#define ES8323_DACCONTROL17_LI2LOVOL_OFF 3
+
#define ES8323_DACCONTROL18 0x28
#define ES8323_DACCONTROL19 0x29
#define ES8323_DACCONTROL20 0x2a
+
+#define ES8323_DACCONTROL20_RI2ROVOL_OFF 3
+
#define ES8323_DACCONTROL21 0x2b
#define ES8323_DACCONTROL22 0x2c
#define ES8323_DACCONTROL23 0x2d
@@ -71,8 +168,4 @@
#define ES8323_DACCONTROL29 0x33
#define ES8323_DACCONTROL30 0x34
-#define ES8323_ADC_IFACE ES8323_ADCCONTROL4
-#define ES8323_ADC_SRATE ES8323_ADCCONTROL5
-#define ES8323_DAC_IFACE ES8323_DACCONTROL1
-#define ES8323_DAC_SRATE ES8323_DACCONTROL2
#endif
diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c
index 05b13661c38c..55a65ef99208 100644
--- a/sound/soc/codecs/es8326.c
+++ b/sound/soc/codecs/es8326.c
@@ -194,6 +194,8 @@ static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_pga_tlv, 0, 600, 0);
static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(softramp_rate, 0, 100, 0);
static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(drc_target_tlv, -3200, 200, 0);
static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(drc_recovery_tlv, -125, 250, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dre_gain_tlv, -9550, 400, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dre_gate_tlv, -9600, 600, 0);
static const char *const winsize[] = {
"0.25db/2 LRCK",
@@ -226,6 +228,8 @@ static const char *const hp_spkvol_switch[] = {
static const struct soc_enum dacpol =
SOC_ENUM_SINGLE(ES8326_DAC_DSM, 4, 4, dacpol_txt);
+static const struct soc_enum dre_winsize =
+ SOC_ENUM_SINGLE(ES8326_ADC_DRE, 0, 16, winsize);
static const struct soc_enum alc_winsize =
SOC_ENUM_SINGLE(ES8326_ADC_RAMPRATE, 4, 16, winsize);
static const struct soc_enum drc_winsize =
@@ -245,7 +249,18 @@ static const struct snd_kcontrol_new es8326_snd_controls[] = {
adc_vol_tlv),
SOC_DOUBLE_TLV("ADC PGA Volume", ES8326_ADC_SCALE, 4, 0, 5, 0, adc_pga_tlv),
SOC_SINGLE_TLV("ADC PGA Gain Volume", ES8326_PGAGAIN, 0, 10, 0, adc_analog_pga_tlv),
+ SOC_SINGLE("ADC PGA SE Switch", ES8326_PGAGAIN, 7, 1, 0),
SOC_SINGLE_TLV("ADC Ramp Rate", ES8326_ADC_RAMPRATE, 0, 0x0f, 0, softramp_rate),
+ SOC_SINGLE("ADC4 DRE Switch", ES8326_ADC_DRE, 4, 1, 0),
+ SOC_SINGLE("ADC3 DRE Switch", ES8326_ADC_DRE, 5, 1, 0),
+ SOC_SINGLE("ADC2 DRE Switch", ES8326_ADC_DRE, 6, 1, 0),
+ SOC_SINGLE("ADC1 DRE Switch", ES8326_ADC_DRE, 7, 1, 0),
+ SOC_ENUM("DRE Winsize", dre_winsize),
+ SOC_SINGLE("DRE Gain Switch", ES8326_ADC_DRE_GAIN, 5, 1, 0),
+ SOC_SINGLE_TLV("DRE Gain Volume", ES8326_ADC_DRE_GAIN,
+ 0, 0x1F, 0, dre_gain_tlv),
+ SOC_SINGLE_TLV("DRE Gate Volume", ES8326_ADC_DRE_GATE,
+ 4, 0x07, 0, dre_gate_tlv),
SOC_SINGLE("ALC Capture Switch", ES8326_ALC_RECOVERY, 3, 1, 0),
SOC_SINGLE_TLV("ALC Capture Recovery Level", ES8326_ALC_LEVEL,
0, 4, 0, drc_recovery_tlv),
@@ -934,11 +949,8 @@ static void es8326_jack_detect_handler(struct work_struct *work)
dev_dbg(comp->dev, "Headset detected\n");
snd_soc_jack_report(es8326->jack,
SND_JACK_HEADSET, SND_JACK_HEADSET);
-
regmap_update_bits(es8326->regmap, ES8326_PGA_PDN,
0x08, 0x08);
- regmap_update_bits(es8326->regmap, ES8326_PGAGAIN,
- 0x80, 0x80);
regmap_write(es8326->regmap, ES8326_ADC1_SRC, 0x00);
regmap_write(es8326->regmap, ES8326_ADC2_SRC, 0x00);
regmap_update_bits(es8326->regmap, ES8326_PGA_PDN,
diff --git a/sound/soc/codecs/es8326.h b/sound/soc/codecs/es8326.h
index c3e52e7bdef5..1c5b3ec70a1e 100644
--- a/sound/soc/codecs/es8326.h
+++ b/sound/soc/codecs/es8326.h
@@ -58,6 +58,9 @@
#define ES8326_ADC1_VOL 0x2c
#define ES8326_ADC2_VOL 0x2d
#define ES8326_ADC_RAMPRATE 0x2e
+#define ES8326_ADC_DRE 0x2f
+#define ES8326_ADC_DRE_GAIN 0x30
+#define ES8326_ADC_DRE_GATE 0x31
#define ES8326_ALC_RECOVERY 0x32
#define ES8326_ALC_LEVEL 0x33
#define ES8326_ADC_HPFS1 0x34
diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c
index 1e11175cfbbb..9838fe42cb6f 100644
--- a/sound/soc/codecs/es8328.c
+++ b/sound/soc/codecs/es8328.c
@@ -163,12 +163,11 @@ static int es8328_put_deemph(struct snd_kcontrol *kcontrol,
if (es8328->deemph == deemph)
return 0;
+ es8328->deemph = deemph;
ret = es8328_set_deemph(component);
if (ret < 0)
return ret;
- es8328->deemph = deemph;
-
return 1;
}
@@ -406,16 +405,6 @@ static const struct snd_soc_dapm_route es8328_dapm_routes[] = {
{ "Mic Bias", NULL, "Mic Bias Gen" },
- { "Left Line Mux", "Line 1", "LINPUT1" },
- { "Left Line Mux", "Line 2", "LINPUT2" },
- { "Left Line Mux", "PGA", "Left PGA Mux" },
- { "Left Line Mux", "Differential", "Differential Mux" },
-
- { "Right Line Mux", "Line 1", "RINPUT1" },
- { "Right Line Mux", "Line 2", "RINPUT2" },
- { "Right Line Mux", "PGA", "Right PGA Mux" },
- { "Right Line Mux", "Differential", "Differential Mux" },
-
{ "Left Mixer", NULL, "Left DAC" },
{ "Left Mixer", "Left Bypass Switch", "Left Line Mux" },
{ "Left Mixer", "Right Playback Switch", "Right DAC" },
@@ -471,6 +460,7 @@ static int es8328_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_component *component = dai->component;
struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
+ int ret;
int i;
int reg;
int wl;
@@ -504,9 +494,12 @@ static int es8328_hw_params(struct snd_pcm_substream *substream,
es8328->mclkdiv2 = 0;
}
- snd_soc_component_update_bits(component, ES8328_MASTERMODE,
- ES8328_MASTERMODE_MCLKDIV2,
- es8328->mclkdiv2 ? ES8328_MASTERMODE_MCLKDIV2 : 0);
+ ret = snd_soc_component_update_bits(component, ES8328_MASTERMODE,
+ ES8328_MASTERMODE_MCLKDIV2,
+ es8328->mclkdiv2 ?
+ ES8328_MASTERMODE_MCLKDIV2 : 0);
+ if (ret < 0)
+ return ret;
switch (params_width(params)) {
case 16:
@@ -529,18 +522,28 @@ static int es8328_hw_params(struct snd_pcm_substream *substream,
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- snd_soc_component_update_bits(component, ES8328_DACCONTROL1,
- ES8328_DACCONTROL1_DACWL_MASK,
- wl << ES8328_DACCONTROL1_DACWL_SHIFT);
+ ret = snd_soc_component_update_bits(component, ES8328_DACCONTROL1,
+ ES8328_DACCONTROL1_DACWL_MASK,
+ wl << ES8328_DACCONTROL1_DACWL_SHIFT);
+ if (ret < 0)
+ return ret;
es8328->playback_fs = params_rate(params);
- es8328_set_deemph(component);
- } else
- snd_soc_component_update_bits(component, ES8328_ADCCONTROL4,
- ES8328_ADCCONTROL4_ADCWL_MASK,
- wl << ES8328_ADCCONTROL4_ADCWL_SHIFT);
+ ret = es8328_set_deemph(component);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = snd_soc_component_update_bits(component, ES8328_ADCCONTROL4,
+ ES8328_ADCCONTROL4_ADCWL_MASK,
+ wl << ES8328_ADCCONTROL4_ADCWL_SHIFT);
+ if (ret < 0)
+ return ret;
+ }
- return snd_soc_component_update_bits(component, reg, ES8328_RATEMASK, ratio);
+ ret = snd_soc_component_update_bits(component, reg, ES8328_RATEMASK, ratio);
+ if (ret < 0)
+ return ret;
+ return 0;
}
static int es8328_set_sysclk(struct snd_soc_dai *codec_dai,
@@ -589,21 +592,26 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
{
struct snd_soc_component *component = codec_dai->component;
struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
+ int ret;
u8 dac_mode = 0;
u8 adc_mode = 0;
switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
case SND_SOC_DAIFMT_CBP_CFP:
/* Master serial port mode, with BCLK generated automatically */
- snd_soc_component_update_bits(component, ES8328_MASTERMODE,
- ES8328_MASTERMODE_MSC,
- ES8328_MASTERMODE_MSC);
+ ret = snd_soc_component_update_bits(component, ES8328_MASTERMODE,
+ ES8328_MASTERMODE_MSC,
+ ES8328_MASTERMODE_MSC);
+ if (ret < 0)
+ return ret;
es8328->provider = true;
break;
case SND_SOC_DAIFMT_CBC_CFC:
/* Slave serial port mode */
- snd_soc_component_update_bits(component, ES8328_MASTERMODE,
- ES8328_MASTERMODE_MSC, 0);
+ ret = snd_soc_component_update_bits(component, ES8328_MASTERMODE,
+ ES8328_MASTERMODE_MSC, 0);
+ if (ret < 0)
+ return ret;
es8328->provider = false;
break;
default:
@@ -632,10 +640,17 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF)
return -EINVAL;
- snd_soc_component_update_bits(component, ES8328_DACCONTROL1,
- ES8328_DACCONTROL1_DACFORMAT_MASK, dac_mode);
- snd_soc_component_update_bits(component, ES8328_ADCCONTROL4,
- ES8328_ADCCONTROL4_ADCFORMAT_MASK, adc_mode);
+ ret = snd_soc_component_update_bits(component, ES8328_DACCONTROL1,
+ ES8328_DACCONTROL1_DACFORMAT_MASK,
+ dac_mode);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_update_bits(component, ES8328_ADCCONTROL4,
+ ES8328_ADCCONTROL4_ADCFORMAT_MASK,
+ adc_mode);
+ if (ret < 0)
+ return ret;
return 0;
}
@@ -644,6 +659,7 @@ static int es8328_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ int ret;
switch (level) {
case SND_SOC_BIAS_ON:
@@ -651,43 +667,56 @@ static int es8328_set_bias_level(struct snd_soc_component *component,
case SND_SOC_BIAS_PREPARE:
/* VREF, VMID=2x50k, digital enabled */
- snd_soc_component_write(component, ES8328_CHIPPOWER, 0);
- snd_soc_component_update_bits(component, ES8328_CONTROL1,
- ES8328_CONTROL1_VMIDSEL_MASK |
- ES8328_CONTROL1_ENREF,
- ES8328_CONTROL1_VMIDSEL_50k |
- ES8328_CONTROL1_ENREF);
+ ret = snd_soc_component_write(component, ES8328_CHIPPOWER, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_update_bits(component, ES8328_CONTROL1,
+ ES8328_CONTROL1_VMIDSEL_MASK |
+ ES8328_CONTROL1_ENREF,
+ ES8328_CONTROL1_VMIDSEL_50k |
+ ES8328_CONTROL1_ENREF);
+ if (ret < 0)
+ return ret;
break;
case SND_SOC_BIAS_STANDBY:
if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
- snd_soc_component_update_bits(component, ES8328_CONTROL1,
- ES8328_CONTROL1_VMIDSEL_MASK |
- ES8328_CONTROL1_ENREF,
- ES8328_CONTROL1_VMIDSEL_5k |
- ES8328_CONTROL1_ENREF);
+ ret = snd_soc_component_update_bits(component, ES8328_CONTROL1,
+ ES8328_CONTROL1_VMIDSEL_MASK |
+ ES8328_CONTROL1_ENREF,
+ ES8328_CONTROL1_VMIDSEL_5k |
+ ES8328_CONTROL1_ENREF);
+ if (ret < 0)
+ return ret;
/* Charge caps */
msleep(100);
}
- snd_soc_component_write(component, ES8328_CONTROL2,
- ES8328_CONTROL2_OVERCURRENT_ON |
- ES8328_CONTROL2_THERMAL_SHUTDOWN_ON);
+ ret = snd_soc_component_write(component, ES8328_CONTROL2,
+ ES8328_CONTROL2_OVERCURRENT_ON |
+ ES8328_CONTROL2_THERMAL_SHUTDOWN_ON);
+ if (ret < 0)
+ return ret;
/* VREF, VMID=2*500k, digital stopped */
- snd_soc_component_update_bits(component, ES8328_CONTROL1,
- ES8328_CONTROL1_VMIDSEL_MASK |
- ES8328_CONTROL1_ENREF,
- ES8328_CONTROL1_VMIDSEL_500k |
- ES8328_CONTROL1_ENREF);
+ ret = snd_soc_component_update_bits(component, ES8328_CONTROL1,
+ ES8328_CONTROL1_VMIDSEL_MASK |
+ ES8328_CONTROL1_ENREF,
+ ES8328_CONTROL1_VMIDSEL_500k |
+ ES8328_CONTROL1_ENREF);
+ if (ret < 0)
+ return ret;
break;
case SND_SOC_BIAS_OFF:
- snd_soc_component_update_bits(component, ES8328_CONTROL1,
- ES8328_CONTROL1_VMIDSEL_MASK |
- ES8328_CONTROL1_ENREF,
- 0);
+ ret = snd_soc_component_update_bits(component, ES8328_CONTROL1,
+ ES8328_CONTROL1_VMIDSEL_MASK |
+ ES8328_CONTROL1_ENREF,
+ 0);
+ if (ret < 0)
+ return ret;
break;
}
return 0;
@@ -742,12 +771,9 @@ static int es8328_suspend(struct snd_soc_component *component)
static int es8328_resume(struct snd_soc_component *component)
{
- struct regmap *regmap = dev_get_regmap(component->dev, NULL);
- struct es8328_priv *es8328;
+ struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
int ret;
- es8328 = snd_soc_component_get_drvdata(component);
-
ret = clk_prepare_enable(es8328->clk);
if (ret) {
dev_err(component->dev, "unable to enable clock\n");
@@ -758,17 +784,23 @@ static int es8328_resume(struct snd_soc_component *component)
es8328->supplies);
if (ret) {
dev_err(component->dev, "unable to enable regulators\n");
- return ret;
+ goto err_clk;
}
- regcache_mark_dirty(regmap);
- ret = regcache_sync(regmap);
+ regcache_mark_dirty(es8328->regmap);
+ ret = regcache_sync(es8328->regmap);
if (ret) {
dev_err(component->dev, "unable to sync regcache\n");
- return ret;
+ goto err_regulators;
}
return 0;
+
+err_regulators:
+ regulator_bulk_disable(ARRAY_SIZE(es8328->supplies), es8328->supplies);
+err_clk:
+ clk_disable_unprepare(es8328->clk);
+ return ret;
}
static int es8328_component_probe(struct snd_soc_component *component)
diff --git a/sound/soc/codecs/es8375.c b/sound/soc/codecs/es8375.c
index 36b0ebdce514..0b9406e93c0e 100644
--- a/sound/soc/codecs/es8375.c
+++ b/sound/soc/codecs/es8375.c
@@ -397,8 +397,6 @@ static int es8375_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
case SND_SOC_DAIFMT_I2S:
codeciface &= 0xFC;
break;
- case SND_SOC_DAIFMT_RIGHT_J:
- return -EINVAL;
case SND_SOC_DAIFMT_LEFT_J:
codeciface &= 0xFC;
codeciface |= 0x01;
diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c
index a84d79f9d3d1..8d418cae371a 100644
--- a/sound/soc/codecs/es8389.c
+++ b/sound/soc/codecs/es8389.c
@@ -17,6 +17,7 @@
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -31,13 +32,21 @@
struct es8389_private {
struct regmap *regmap;
struct clk *mclk;
+ struct regulator_bulk_data core_supply[2];
unsigned int sysclk;
int mastermode;
u8 mclk_src;
+ u8 vddd;
+ int version;
enum snd_soc_bias_level bias_level;
};
+static const char * const es8389_core_supplies[] = {
+ "vddd",
+ "vdda",
+};
+
static bool es8389_volatile_register(struct device *dev,
unsigned int reg)
{
@@ -367,95 +376,110 @@ struct _coeff_div {
u8 Reg0x16;
u8 Reg0x18;
u8 Reg0x19;
+ u8 dvdd_vol;
+ u8 dmic_sel;
};
/* codec hifi mclk clock divider coefficients */
static const struct _coeff_div coeff_div[] = {
- {32, 256000, 8000, 0x00, 0x57, 0x84, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {36, 288000, 8000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {48, 384000, 8000, 0x02, 0x5F, 0x04, 0xC0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {64, 512000, 8000, 0x00, 0x4D, 0x24, 0xC0, 0x03, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {72, 576000, 8000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {96, 768000, 8000, 0x02, 0x57, 0x84, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {128, 1024000, 8000, 0x00, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {192, 1536000, 8000, 0x02, 0x4D, 0x24, 0xC0, 0x03, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {256, 2048000, 8000, 0x01, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {288, 2304000, 8000, 0x01, 0x51, 0x00, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {384, 3072000, 8000, 0x02, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {512, 4096000, 8000, 0x00, 0x41, 0x04, 0xE0, 0x00, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {768, 6144000, 8000, 0x05, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {1024, 8192000, 8000, 0x01, 0x41, 0x06, 0xE0, 0x00, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {1536, 12288000, 8000, 0x02, 0x41, 0x04, 0xE0, 0x00, 0xD1, 0xB0, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {1625, 13000000, 8000, 0x40, 0x6E, 0x05, 0xC8, 0x01, 0xC2, 0x90, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07},
- {2048, 16384000, 8000, 0x03, 0x44, 0x01, 0xC0, 0x00, 0xD2, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {2304, 18432000, 8000, 0x11, 0x45, 0x25, 0xF0, 0x00, 0xD1, 0xB0, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {3072, 24576000, 8000, 0x05, 0x44, 0x01, 0xC0, 0x00, 0xD2, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {32, 512000, 16000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {36, 576000, 16000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {48, 768000, 16000, 0x02, 0x57, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {50, 800000, 16000, 0x00, 0x7E, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {64, 1024000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {72, 1152000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {96, 1536000, 16000, 0x02, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {128, 2048000, 16000, 0x00, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {144, 2304000, 16000, 0x00, 0x51, 0x00, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {192, 3072000, 16000, 0x02, 0x65, 0x25, 0xE0, 0x00, 0xE1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {256, 4096000, 16000, 0x00, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {300, 4800000, 16000, 0x02, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {384, 6144000, 16000, 0x02, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {512, 8192000, 16000, 0x01, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {750, 12000000, 16000, 0x0E, 0x7E, 0x01, 0xC9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {768, 12288000, 16000, 0x02, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {1024, 16384000, 16000, 0x03, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {1152, 18432000, 16000, 0x08, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {1200, 19200000, 16000, 0x0B, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {1500, 24000000, 16000, 0x0E, 0x26, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {1536, 24576000, 16000, 0x05, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {1625, 26000000, 16000, 0x40, 0x6E, 0x05, 0xC8, 0x01, 0xC2, 0x90, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {800, 19200000, 24000, 0x07, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x1A, 0x49, 0x14},
- {600, 19200000, 32000, 0x05, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x23, 0x61, 0x1B},
- {32, 1411200, 44100, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {64, 2822400, 44100, 0x00, 0x51, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {128, 5644800, 44100, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {256, 11289600, 44100, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {512, 22579200, 44100, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {32, 1536000, 48000, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {48, 2304000, 48000, 0x02, 0x55, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {50, 2400000, 48000, 0x00, 0x76, 0x01, 0xC8, 0x10, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {64, 3072000, 48000, 0x00, 0x51, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {100, 4800000, 48000, 0x00, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {125, 6000000, 48000, 0x04, 0x6E, 0x05, 0xC8, 0x10, 0xC2, 0x80, 0x00, 0x01, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {128, 6144000, 48000, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {200, 9600000, 48000, 0x01, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {250, 12000000, 48000, 0x04, 0x76, 0x01, 0xC8, 0x10, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {256, 12288000, 48000, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {384, 18432000, 48000, 0x02, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {400, 19200000, 48000, 0x03, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {500, 24000000, 48000, 0x04, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {512, 24576000, 48000, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {800, 38400000, 48000, 0x18, 0x45, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {128, 11289600, 88200, 0x00, 0x50, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x32, 0x89, 0x25},
- {64, 6144000, 96000, 0x00, 0x41, 0x00, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28},
- {128, 12288000, 96000, 0x00, 0x50, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28},
- {256, 24576000, 96000, 0x00, 0x40, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28},
- {128, 24576000, 192000, 0x00, 0x50, 0x00, 0xC0, 0x18, 0xC1, 0x81, 0xC0, 0x00, 0x8F, 0x7F, 0xEF, 0xC0, 0x3F, 0x7F, 0x80, 0x12, 0xC0, 0x3F, 0xF9, 0x3F},
-
- {50, 400000, 8000, 0x00, 0x75, 0x05, 0xC8, 0x01, 0xC1, 0x90, 0x10, 0x00, 0x18, 0xC7, 0xD0, 0xC0, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {600, 4800000, 8000, 0x05, 0x65, 0x25, 0xF9, 0x00, 0xD1, 0x90, 0x10, 0x00, 0x18, 0xC7, 0xD0, 0xC0, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {1500, 12000000, 8000, 0x0E, 0x25, 0x25, 0xE8, 0x00, 0xD1, 0x90, 0x40, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {2400, 19200000, 8000, 0x0B, 0x01, 0x00, 0xD0, 0x00, 0xD1, 0x80, 0x90, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0xC7, 0xC7, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07},
- {3000, 24000000, 8000, 0x0E, 0x24, 0x05, 0xD0, 0x00, 0xC2, 0x80, 0xC0, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {3250, 26000000, 8000, 0x40, 0x05, 0xA4, 0xC0, 0x00, 0xD1, 0x80, 0xD0, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0xC7, 0xC7, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {32, 256000, 8000, 0x00, 0x57, 0x84, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {36, 288000, 8000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {48, 384000, 8000, 0x02, 0x5F, 0x04, 0xC0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {50, 400000, 8000, 0x00, 0x75, 0x05, 0xC8, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x18, 0xC7, 0xD0, 0xC0, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 0},
+ {50, 400000, 8000, 0x00, 0x15, 0x85, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 1},
+ {64, 512000, 8000, 0x00, 0x4D, 0x24, 0xC0, 0x03, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {72, 576000, 8000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {96, 768000, 8000, 0x02, 0x57, 0x84, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {128, 1024000, 8000, 0x00, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {192, 1536000, 8000, 0x02, 0x4D, 0x24, 0xC0, 0x03, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {256, 2048000, 8000, 0x01, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {288, 2304000, 8000, 0x01, 0x51, 0x00, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {384, 3072000, 8000, 0x02, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {512, 4096000, 8000, 0x00, 0x41, 0x04, 0xE0, 0x00, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {600, 4800000, 8000, 0x05, 0x65, 0x25, 0xF9, 0x00, 0xD1, 0x90, 0x00, 0x00, 0x18, 0xC7, 0xD0, 0xC0, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 0},
+ {600, 4800000, 8000, 0x02, 0x24, 0x05, 0xD0, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 1},
+ {768, 6144000, 8000, 0x05, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {1024, 8192000, 8000, 0x01, 0x41, 0x06, 0xE0, 0x00, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {1500, 12000000, 8000, 0x0E, 0x25, 0x25, 0xE8, 0x00, 0xD1, 0x90, 0x40, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {1536, 12288000, 8000, 0x02, 0x41, 0x04, 0xE0, 0x00, 0xD1, 0xB0, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {1625, 13000000, 8000, 0x40, 0x6E, 0x05, 0xC8, 0x01, 0xC2, 0x90, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {2048, 16384000, 8000, 0x03, 0x44, 0x01, 0xC0, 0x00, 0xD2, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {2304, 18432000, 8000, 0x11, 0x45, 0x25, 0xF0, 0x00, 0xD1, 0xB0, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {2400, 19200000, 8000, 0x05, 0x14, 0x01, 0xC9, 0x00, 0xD2, 0x80, 0x80, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 1},
+ {2400, 19200000, 8000, 0x0B, 0x01, 0x00, 0xD0, 0x00, 0xD1, 0x80, 0x80, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0xC7, 0xC7, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 0},
+ {3000, 24000000, 8000, 0x0E, 0x24, 0x05, 0xD0, 0x00, 0xC2, 0x80, 0xC0, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {3072, 24576000, 8000, 0x05, 0x44, 0x01, 0xC0, 0x00, 0xD2, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {3250, 26000000, 8000, 0x40, 0x15, 0x85, 0xD0, 0x01, 0xC1, 0x90, 0xC0, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 1},
+ {3250, 26000000, 8000, 0x40, 0x05, 0xA4, 0xC0, 0x00, 0xD1, 0x80, 0xC0, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0xC7, 0xC7, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 0},
+ {32, 512000, 16000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2},
+ {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},
+ {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},
+ {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},
+ {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},
+ {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},
+ {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},
+ {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},
+ {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},
+ {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},
+ {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},
+ {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},
+ {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},
+ {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},
};
-static inline int get_coeff(int mclk, int rate)
+static inline int get_coeff(u8 vddd, u8 dmic, int mclk, int rate)
{
int i;
+ u8 dmic_det, vddd_det;
for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
- if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
- return i;
+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) {
+ vddd_det = ~(coeff_div[i].dvdd_vol ^ vddd) & 0x01;
+ dmic_det = ~(coeff_div[i].dmic_sel ^ dmic) & 0x01;
+ vddd_det |= ~(coeff_div[i].dvdd_vol % 2) & 0x01;
+ dmic_det |= ~(coeff_div[i].dmic_sel % 2) & 0x01;
+
+ if (vddd_det && dmic_det)
+ return i;
+ }
}
+
return -EINVAL;
}
@@ -537,8 +561,9 @@ static int es8389_pcm_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_component *component = dai->component;
struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
- int coeff;
- u8 state = 0;
+ int coeff, ret;
+ u8 dmic_enable, state = 0;
+ unsigned int regv;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
@@ -569,7 +594,23 @@ static int es8389_pcm_hw_params(struct snd_pcm_substream *substream,
es8389->sysclk = params_channels(params) * params_width(params) * params_rate(params);
}
- coeff = get_coeff(es8389->sysclk, params_rate(params));
+ regmap_read(es8389->regmap, ES8389_DMIC_EN, &regv);
+ dmic_enable = regv >> 7 & 0x01;
+
+ ret = regulator_get_voltage(es8389->core_supply[ES8389_SUPPLY_VD].consumer);
+ switch (ret) {
+ case 1800000 ... 2000000:
+ es8389->vddd = ES8389_1V8;
+ break;
+ case 2500000 ... 3300000:
+ es8389->vddd = ES8389_3V3;
+ break;
+ default:
+ es8389->vddd = ES8389_3V3;
+ break;
+ }
+
+ coeff = get_coeff(es8389->vddd, dmic_enable, es8389->sysclk, params_rate(params));
if (coeff >= 0) {
regmap_write(es8389->regmap, ES8389_CLK_DIV1, coeff_div[coeff].Reg0x04);
regmap_write(es8389->regmap, ES8389_CLK_MUL, coeff_div[coeff].Reg0x05);
@@ -621,10 +662,6 @@ static int es8389_set_bias_level(struct snd_soc_component *component,
regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xE4);
regmap_write(es8389->regmap, ES8389_RESET, 0x01);
regmap_write(es8389->regmap, ES8389_CLK_OFF1, 0xC3);
- regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, 0x0a);
- regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, 0x0a);
- usleep_range(70000, 72000);
- regmap_write(es8389->regmap, ES8389_DAC_RESET, 0X00);
break;
case SND_SOC_BIAS_PREPARE:
break;
@@ -655,6 +692,7 @@ static int es8389_mute(struct snd_soc_dai *dai, int mute, int direction)
{
struct snd_soc_component *component = dai->component;
struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+ unsigned int regv;
if (mute) {
if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
@@ -665,10 +703,26 @@ static int es8389_mute(struct snd_soc_dai *dai, int mute, int direction)
0x03, 0x03);
}
} else {
+ regmap_read(es8389->regmap, ES8389_CSM_STATE1, &regv);
+ if (regv != ES8389_STATE_ON) {
+ regmap_update_bits(es8389->regmap, ES8389_HPSW, 0x20, 0x20);
+ regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0xD9);
+ regmap_write(es8389->regmap, ES8389_ADC_EN, 0x8F);
+ regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xE4);
+ regmap_write(es8389->regmap, ES8389_RESET, 0x01);
+ regmap_write(es8389->regmap, ES8389_CLK_OFF1, 0xC3);
+ }
+
if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (!es8389->version) {
+ regmap_write(es8389->regmap, ES8389_DAC_RESET, 0X00);
+ usleep_range(70000, 72000);
+ }
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_FORMAT_MUTE,
0x03, 0x00);
}
@@ -713,7 +767,10 @@ static struct snd_soc_dai_driver es8389_dai = {
static void es8389_init(struct snd_soc_component *component)
{
struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+ unsigned int reg;
+ regmap_read(es8389->regmap, ES8389_MAX_REGISTER, &reg);
+ es8389->version = reg;
regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x00);
regmap_write(es8389->regmap, ES8389_RESET, 0x7E);
regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x38);
@@ -818,7 +875,7 @@ static int es8389_resume(struct snd_soc_component *component)
static int es8389_probe(struct snd_soc_component *component)
{
- int ret;
+ int ret, i;
struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
ret = device_property_read_u8(component->dev, "everest,mclk-src", &es8389->mclk_src);
@@ -827,6 +884,14 @@ static int es8389_probe(struct snd_soc_component *component)
es8389->mclk_src = ES8389_MCLK_SOURCE;
}
+ for (i = 0; i < ARRAY_SIZE(es8389_core_supplies); i++)
+ es8389->core_supply[i].supply = es8389_core_supplies[i];
+ ret = devm_regulator_bulk_get(component->dev, ARRAY_SIZE(es8389_core_supplies), es8389->core_supply);
+ if (ret) {
+ dev_err(component->dev, "Failed to request core supplies %d\n", ret);
+ return ret;
+ }
+
es8389->mclk = devm_clk_get(component->dev, "mclk");
if (IS_ERR(es8389->mclk))
return dev_err_probe(component->dev, PTR_ERR(es8389->mclk),
@@ -841,6 +906,13 @@ static int es8389_probe(struct snd_soc_component *component)
return ret;
}
+ ret = regulator_bulk_enable(ARRAY_SIZE(es8389_core_supplies), es8389->core_supply);
+ if (ret) {
+ dev_err(component->dev, "Failed to enable core supplies: %d\n", ret);
+ clk_disable_unprepare(es8389->mclk);
+ return ret;
+ }
+
es8389_init(component);
es8389_set_bias_level(component, SND_SOC_BIAS_STANDBY);
@@ -907,6 +979,8 @@ static void es8389_i2c_shutdown(struct i2c_client *i2c)
regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0x08);
regmap_write(es8389->regmap, ES8389_ISO_CTL, 0xC1);
regmap_write(es8389->regmap, ES8389_PULL_DOWN, 0x00);
+
+ regulator_bulk_disable(ARRAY_SIZE(es8389_core_supplies), es8389->core_supply);
}
static int es8389_i2c_probe(struct i2c_client *i2c_client)
diff --git a/sound/soc/codecs/es8389.h b/sound/soc/codecs/es8389.h
index 123d1e4b2d53..d21e72f876a6 100644
--- a/sound/soc/codecs/es8389.h
+++ b/sound/soc/codecs/es8389.h
@@ -137,4 +137,12 @@
#define ES8389_STATE_ON (13 << 0)
#define ES8389_STATE_STANDBY (7 << 0)
+enum ES8389_supplies {
+ ES8389_SUPPLY_VD = 0,
+ ES8389_SUPPLY_VA,
+};
+
+#define ES8389_3V3 1
+#define ES8389_1V8 0
+
#endif
diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c
index 3dd4dd94bc37..ff58805e97d1 100644
--- a/sound/soc/codecs/max98390.c
+++ b/sound/soc/codecs/max98390.c
@@ -1067,6 +1067,9 @@ static int max98390_i2c_probe(struct i2c_client *i2c)
reset_gpio = devm_gpiod_get_optional(&i2c->dev,
"reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(reset_gpio))
+ return dev_err_probe(&i2c->dev, PTR_ERR(reset_gpio),
+ "Failed to get reset gpio\n");
/* Power on device */
if (reset_gpio) {
diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c
index 3beb3c44dc2c..ffb526de0021 100644
--- a/sound/soc/codecs/nau8821.c
+++ b/sound/soc/codecs/nau8821.c
@@ -11,10 +11,10 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dmi.h>
-#include <linux/init.h>
#include <linux/i2c.h>
-#include <linux/module.h>
+#include <linux/init.h>
#include <linux/math64.h>
+#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/core.h>
@@ -24,6 +24,7 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
+
#include "nau8821.h"
#define NAU8821_QUIRK_JD_ACTIVE_HIGH BIT(0)
@@ -806,16 +807,20 @@ nau8821_get_osr(struct nau8821 *nau8821, int stream)
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
regmap_read(nau8821->regmap, NAU8821_R2C_DAC_CTRL1, &osr);
osr &= NAU8821_DAC_OVERSAMPLE_MASK;
+
if (osr >= ARRAY_SIZE(osr_dac_sel))
return NULL;
+
return &osr_dac_sel[osr];
- } else {
- regmap_read(nau8821->regmap, NAU8821_R2B_ADC_RATE, &osr);
- osr &= NAU8821_ADC_SYNC_DOWN_MASK;
- if (osr >= ARRAY_SIZE(osr_adc_sel))
- return NULL;
- return &osr_adc_sel[osr];
}
+
+ regmap_read(nau8821->regmap, NAU8821_R2B_ADC_RATE, &osr);
+ osr &= NAU8821_ADC_SYNC_DOWN_MASK;
+
+ if (osr >= ARRAY_SIZE(osr_adc_sel))
+ return NULL;
+
+ return &osr_adc_sel[osr];
}
static int nau8821_dai_startup(struct snd_pcm_substream *substream,
@@ -868,15 +873,16 @@ static int nau8821_hw_params(struct snd_pcm_substream *substream,
if (ctrl_val & NAU8821_I2S_MS_MASTER) {
/* get the bclk and fs ratio */
bclk_fs = snd_soc_params_to_bclk(params) / nau8821->fs;
+
if (bclk_fs <= 32)
clk_div = 3;
else if (bclk_fs <= 64)
clk_div = 2;
else if (bclk_fs <= 128)
clk_div = 1;
- else {
+ else
return -EINVAL;
- }
+
regmap_update_bits(nau8821->regmap, NAU8821_R1D_I2S_PCM_CTRL2,
NAU8821_I2S_LRC_DIV_MASK | NAU8821_I2S_BLK_DIV_MASK,
(clk_div << NAU8821_I2S_LRC_DIV_SFT) | clk_div);
@@ -1264,6 +1270,14 @@ static int nau8821_component_probe(struct snd_soc_component *component)
return 0;
}
+static void nau8821_component_remove(struct snd_soc_component *component)
+{
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+
+ if (nau8821->jdet_active)
+ cancel_delayed_work_sync(&nau8821->jdet_work);
+};
+
/**
* nau8821_calc_fll_param - Calculate FLL parameters.
* @fll_in: external clock provided to codec.
@@ -1597,6 +1611,10 @@ static int __maybe_unused nau8821_suspend(struct snd_soc_component *component)
if (nau8821->irq)
disable_irq(nau8821->irq);
+
+ if (nau8821->jdet_active)
+ cancel_delayed_work_sync(&nau8821->jdet_work);
+
snd_soc_dapm_force_bias_level(nau8821->dapm, SND_SOC_BIAS_OFF);
/* Power down codec power; don't support button wakeup */
snd_soc_dapm_disable_pin(nau8821->dapm, "MICBIAS");
@@ -1621,6 +1639,7 @@ static int __maybe_unused nau8821_resume(struct snd_soc_component *component)
static const struct snd_soc_component_driver nau8821_component_driver = {
.probe = nau8821_component_probe,
+ .remove = nau8821_component_remove,
.set_sysclk = nau8821_set_sysclk,
.set_pll = nau8821_set_fll,
.set_bias_level = nau8821_set_bias_level,
@@ -1655,17 +1674,20 @@ int nau8821_enable_jack_detect(struct snd_soc_component *component,
int ret;
nau8821->jack = jack;
+
+ if (nau8821->jdet_active)
+ return 0;
+
/* Initiate jack detection work queue */
INIT_DELAYED_WORK(&nau8821->jdet_work, nau8821_jdet_work);
+ nau8821->jdet_active = true;
ret = devm_request_threaded_irq(nau8821->dev, nau8821->irq, NULL,
nau8821_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
"nau8821", nau8821);
- if (ret) {
+ if (ret)
dev_err(nau8821->dev, "Cannot request irq %d (%d)\n",
nau8821->irq, ret);
- return ret;
- }
return ret;
}
diff --git a/sound/soc/codecs/nau8821.h b/sound/soc/codecs/nau8821.h
index 88602923780d..f9d7cd8cbd21 100644
--- a/sound/soc/codecs/nau8821.h
+++ b/sound/soc/codecs/nau8821.h
@@ -562,6 +562,7 @@ struct nau8821 {
struct snd_soc_dapm_context *dapm;
struct snd_soc_jack *jack;
struct delayed_work jdet_work;
+ bool jdet_active;
int irq;
int clk_id;
int micbias_voltage;
diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c
index e6142645b903..977881994e60 100644
--- a/sound/soc/codecs/rt1320-sdw.c
+++ b/sound/soc/codecs/rt1320-sdw.c
@@ -502,12 +502,8 @@ static bool rt1320_readable_register(struct device *dev, unsigned int reg)
case 0x2000301c:
case 0x2000900f:
case 0x20009018:
- case 0x3fc29d80 ... 0x3fc29d83:
- case 0x3fe2e000 ... 0x3fe2e003:
- case 0x3fc2ab80 ... 0x3fc2abd4:
- case 0x3fc2bfc0 ... 0x3fc2bfc8:
- case 0x3fc2d300 ... 0x3fc2d354:
- case 0x3fc2dfc0 ... 0x3fc2dfc8:
+ case 0x3fc000c0 ... 0x3fc2dfc8:
+ case 0x3fe00000 ... 0x3fe36fff:
/* 0x40801508/0x40801809/0x4080180a/0x40801909/0x4080190a */
case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_REQ_POWER_STATE, 0):
case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_MUTE, CH_01):
@@ -557,6 +553,8 @@ static bool rt1320_volatile_register(struct device *dev, unsigned int reg)
case 0xc48c ... 0xc48f:
case 0xc560:
case 0xc5b5 ... 0xc5b7:
+ case 0xc5c3:
+ case 0xc5c8:
case 0xc5fc ... 0xc5ff:
case 0xc680 ... 0xc683:
case 0xc820:
@@ -590,6 +588,7 @@ static bool rt1320_volatile_register(struct device *dev, unsigned int reg)
case 0xdd0c ... 0xdd13:
case 0xde02:
case 0xdf14 ... 0xdf1b:
+ case 0xe80b:
case 0xe83c ... 0xe847:
case 0xf01e:
case 0xf717 ... 0xf719:
@@ -602,7 +601,7 @@ static bool rt1320_volatile_register(struct device *dev, unsigned int reg)
case 0x2000301c:
case 0x2000900f:
case 0x20009018:
- case 0x3fc2ab80 ... 0x3fc2abd4:
+ case 0x3fc2ab80 ... 0x3fc2ac4c:
case 0x3fc2b780:
case 0x3fc2bf80 ... 0x3fc2bf83:
case 0x3fc2bfc0 ... 0x3fc2bfc8:
@@ -720,6 +719,13 @@ static int rt1320_read_prop(struct sdw_slave *slave)
j++;
}
+ prop->dp0_prop = devm_kzalloc(&slave->dev, sizeof(*prop->dp0_prop), GFP_KERNEL);
+ if (!prop->dp0_prop)
+ return -ENOMEM;
+
+ prop->dp0_prop->simple_ch_prep_sm = true;
+ prop->dp0_prop->ch_prep_timeout = 10;
+
/* set the timeout values */
prop->clk_stop_timeout = 64;
@@ -754,6 +760,515 @@ static int rt1320_pde_transition_delay(struct rt1320_sdw_priv *rt1320, unsigned
return 0;
}
+static void rt1320_data_rw(struct rt1320_sdw_priv *rt1320, unsigned int start,
+ unsigned char *data, unsigned int size, enum rt1320_rw_type rw)
+{
+ struct device *dev = &rt1320->sdw_slave->dev;
+ unsigned int tmp;
+ int ret = -1;
+ int i, j;
+
+ pm_runtime_set_autosuspend_delay(dev, 20000);
+ pm_runtime_mark_last_busy(dev);
+
+ switch (rw) {
+ case RT1320_BRA_WRITE:
+ case RT1320_BRA_READ:
+ ret = sdw_bpt_send_sync(rt1320->sdw_slave->bus, rt1320->sdw_slave, &rt1320->bra_msg);
+ if (ret < 0)
+ dev_err(dev, "%s: Failed to send BRA message: %d\n", __func__, ret);
+ fallthrough;
+ case RT1320_PARAM_WRITE:
+ case RT1320_PARAM_READ:
+ if (ret < 0) {
+ /* if BRA fails, we try to access by the control word */
+ if (rw == RT1320_BRA_WRITE || rw == RT1320_BRA_READ) {
+ for (i = 0; i < rt1320->bra_msg.sections; i++) {
+ pm_runtime_mark_last_busy(dev);
+ for (j = 0; j < rt1320->bra_msg.sec[i].len; j++) {
+ if (rw == RT1320_BRA_WRITE) {
+ regmap_write(rt1320->regmap,
+ rt1320->bra_msg.sec[i].addr + j, rt1320->bra_msg.sec[i].buf[j]);
+ } else {
+ regmap_read(rt1320->regmap, rt1320->bra_msg.sec[i].addr + j, &tmp);
+ rt1320->bra_msg.sec[i].buf[j] = tmp;
+ }
+ }
+ }
+ } else {
+ for (i = 0; i < size; i++) {
+ if (rw == RT1320_PARAM_WRITE)
+ regmap_write(rt1320->regmap, start + i, data[i]);
+ else {
+ regmap_read(rt1320->regmap, start + i, &tmp);
+ data[i] = tmp;
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_mark_last_busy(dev);
+}
+
+static unsigned long long rt1320_rsgain_to_rsratio(struct rt1320_sdw_priv *rt1320, unsigned int rsgain)
+{
+ unsigned long long base = 1000000000U;
+ unsigned long long step = 1960784U;
+ unsigned long long tmp, result;
+
+ if (rsgain == 0 || rsgain == 0x1ff)
+ result = 1000000000;
+ else if (rsgain & 0x100) {
+ tmp = 0xff - (rsgain & 0xff);
+ tmp = tmp * step;
+ result = base + tmp;
+ } else {
+ tmp = (rsgain & 0xff);
+ tmp = tmp * step;
+ result = base - tmp;
+ }
+
+ return result;
+}
+
+static void rt1320_pr_read(struct rt1320_sdw_priv *rt1320, unsigned int reg, unsigned int *val)
+{
+ unsigned int byte3, byte2, byte1, byte0;
+
+ regmap_write(rt1320->regmap, 0xc483, 0x80);
+ regmap_write(rt1320->regmap, 0xc482, 0x40);
+ regmap_write(rt1320->regmap, 0xc481, 0x0c);
+ regmap_write(rt1320->regmap, 0xc480, 0x10);
+
+ regmap_write(rt1320->regmap, 0xc487, ((reg & 0xff000000) >> 24));
+ regmap_write(rt1320->regmap, 0xc486, ((reg & 0x00ff0000) >> 16));
+ regmap_write(rt1320->regmap, 0xc485, ((reg & 0x0000ff00) >> 8));
+ regmap_write(rt1320->regmap, 0xc484, (reg & 0x000000ff));
+
+ regmap_write(rt1320->regmap, 0xc482, 0xc0);
+
+ regmap_read(rt1320->regmap, 0xc48f, &byte3);
+ regmap_read(rt1320->regmap, 0xc48e, &byte2);
+ regmap_read(rt1320->regmap, 0xc48d, &byte1);
+ regmap_read(rt1320->regmap, 0xc48c, &byte0);
+
+ *val = (byte3 << 24) | (byte2 << 16) | (byte1 << 8) | byte0;
+}
+
+static int rt1320_check_fw_ready(struct rt1320_sdw_priv *rt1320)
+{
+ struct device *dev = &rt1320->sdw_slave->dev;
+ unsigned int tmp, retry = 0;
+ unsigned int cmd_addr;
+
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ cmd_addr = RT1320_CMD_ID;
+ break;
+ case RT1321_DEV_ID:
+ cmd_addr = RT1321_CMD_ID;
+ break;
+ default:
+ dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id);
+ return -EINVAL;
+ }
+
+ pm_runtime_mark_last_busy(dev);
+ /* check the value of cmd_addr becomes to zero */
+ while (retry < 500) {
+ regmap_read(rt1320->regmap, cmd_addr, &tmp);
+ if (tmp == 0)
+ break;
+ usleep_range(1000, 1100);
+ retry++;
+ }
+ if (retry == 500) {
+ dev_warn(dev, "%s FW is NOT ready!", __func__);
+ return -ETIMEDOUT;
+ }
+
+ 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;
+ unsigned int retry = 0, tmp;
+
+ pm_runtime_mark_last_busy(dev);
+ while (retry < 200) {
+ regmap_read(rt1320->regmap, RT1320_POWER_STATE, &tmp);
+ dev_dbg(dev, "%s, RT1320_POWER_STATE=0x%x\n", __func__, tmp);
+ if (tmp >= ps)
+ break;
+ usleep_range(1000, 1500);
+ retry++;
+ }
+ if (retry == 200) {
+ dev_warn(dev, "%s FW Power State is NOT ready!", __func__);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int rt1320_process_fw_param(struct rt1320_sdw_priv *rt1320, unsigned char *buf, unsigned int buf_size)
+{
+ struct device *dev = &rt1320->sdw_slave->dev;
+ struct rt1320_paramcmd *paramhr = (struct rt1320_paramcmd *)buf;
+ unsigned char moudleid = paramhr->moudleid;
+ unsigned char cmdtype = paramhr->commandtype;
+ unsigned int fw_param_addr;
+ unsigned int start_addr;
+ int ret = 0;
+
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ fw_param_addr = RT1320_FW_PARAM_ADDR;
+ start_addr = RT1320_CMD_PARAM_ADDR;
+ break;
+ case RT1321_DEV_ID:
+ fw_param_addr = RT1321_FW_PARAM_ADDR;
+ start_addr = RT1321_CMD_PARAM_ADDR;
+ break;
+ default:
+ dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id);
+ return -EINVAL;
+ }
+
+ ret = rt1320_check_fw_ready(rt1320);
+ if (ret < 0)
+ goto _timeout_;
+
+ /* don't set offset 0x0/0x1, it will be set later*/
+ paramhr->moudleid = 0;
+ paramhr->commandtype = 0;
+ rt1320_data_rw(rt1320, fw_param_addr, buf, buf_size, RT1320_PARAM_WRITE);
+
+ dev_dbg(dev, "%s, moudleid=%d, cmdtype=%d, paramid=%d, paramlength=%d\n", __func__,
+ moudleid, cmdtype, paramhr->paramid, paramhr->paramlength);
+
+ if (cmdtype == RT1320_SET_PARAM) {
+ regmap_write(rt1320->regmap, fw_param_addr, moudleid);
+ regmap_write(rt1320->regmap, fw_param_addr + 1, 0x01);
+ }
+ if (cmdtype == RT1320_GET_PARAM) {
+ regmap_write(rt1320->regmap, fw_param_addr, moudleid);
+ regmap_write(rt1320->regmap, fw_param_addr + 1, 0x02);
+ ret = rt1320_check_fw_ready(rt1320);
+ if (ret < 0)
+ goto _timeout_;
+
+ rt1320_data_rw(rt1320, start_addr, buf + 0x10, paramhr->commandlength, RT1320_PARAM_READ);
+ }
+ return 0;
+
+_timeout_:
+ dev_err(&rt1320->sdw_slave->dev, "%s: FW is NOT ready for SET/GET_PARAM\n", __func__);
+ return ret;
+}
+
+static int rt1320_fw_param_protocol(struct rt1320_sdw_priv *rt1320, enum rt1320_fw_cmdid cmdid,
+ unsigned int paramid, void *parambuf, unsigned int paramsize)
+{
+ struct device *dev = &rt1320->sdw_slave->dev;
+ unsigned char *tempbuf = NULL;
+ struct rt1320_paramcmd paramhr;
+ int ret = 0;
+
+ tempbuf = kzalloc(sizeof(paramhr) + paramsize, GFP_KERNEL);
+ if (!tempbuf)
+ return -ENOMEM;
+
+ paramhr.moudleid = 1;
+ paramhr.commandtype = cmdid;
+ /* 8 is "sizeof(paramid) + sizeof(paramlength)" */
+ paramhr.commandlength = 8 + paramsize;
+ paramhr.paramid = paramid;
+ paramhr.paramlength = paramsize;
+
+ memcpy(tempbuf, &paramhr, sizeof(paramhr));
+ if (cmdid == RT1320_SET_PARAM)
+ memcpy(tempbuf + sizeof(paramhr), parambuf, paramsize);
+
+ ret = rt1320_process_fw_param(rt1320, tempbuf, sizeof(paramhr) + paramsize);
+ if (ret < 0) {
+ dev_err(dev, "%s: process_fw_param failed\n", __func__);
+ goto _finish_;
+ }
+
+ if (cmdid == RT1320_GET_PARAM)
+ memcpy(parambuf, tempbuf + sizeof(paramhr), paramsize);
+
+_finish_:
+ kfree(tempbuf);
+ return ret;
+}
+
+static void rt1320_set_advancemode(struct rt1320_sdw_priv *rt1320)
+{
+ struct device *dev = &rt1320->sdw_slave->dev;
+ struct rt1320_datafixpoint r0_data[2];
+ unsigned short l_advancegain, r_advancegain;
+ int ret;
+
+ /* Get advance gain/r0 */
+ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 6, &r0_data[0], sizeof(struct rt1320_datafixpoint));
+ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 7, &r0_data[1], sizeof(struct rt1320_datafixpoint));
+ l_advancegain = r0_data[0].advancegain;
+ r_advancegain = r0_data[1].advancegain;
+ dev_dbg(dev, "%s, LR advanceGain=0x%x 0x%x\n", __func__, l_advancegain, r_advancegain);
+
+ /* set R0 and enable protection by SetParameter id 6, 7 */
+ r0_data[0].silencedetect = 0;
+ r0_data[0].r0 = rt1320->r0_l_reg;
+ r0_data[1].silencedetect = 0;
+ r0_data[1].r0 = rt1320->r0_r_reg;
+ dev_dbg(dev, "%s, write LR r0=%d, %d\n", __func__, r0_data[0].r0, r0_data[1].r0);
+
+ rt1320_fw_param_protocol(rt1320, RT1320_SET_PARAM, 6, &r0_data[0], sizeof(struct rt1320_datafixpoint));
+ rt1320_fw_param_protocol(rt1320, RT1320_SET_PARAM, 7, &r0_data[1], sizeof(struct rt1320_datafixpoint));
+ ret = rt1320_check_fw_ready(rt1320);
+ if (ret < 0)
+ dev_err(dev, "%s: Failed to set FW param 6,7!\n", __func__);
+
+ if (l_advancegain != 0 && r_advancegain != 0) {
+ regmap_write(rt1320->regmap, 0xdd0b, (l_advancegain & 0xff00) >> 8);
+ regmap_write(rt1320->regmap, 0xdd0a, (l_advancegain & 0xff));
+ regmap_write(rt1320->regmap, 0xdd09, (r_advancegain & 0xff00) >> 8);
+ regmap_write(rt1320->regmap, 0xdd08, (r_advancegain & 0xff));
+ dev_dbg(dev, "%s, set Advance mode gain\n", __func__);
+ }
+}
+
+static int rt1320_invrs_load(struct rt1320_sdw_priv *rt1320)
+{
+ struct device *dev = &rt1320->sdw_slave->dev;
+ unsigned long long l_rsratio, r_rsratio;
+ unsigned int pr_1058, pr_1059, pr_105a;
+ unsigned long long l_invrs, r_invrs;
+ unsigned long long factor = (1 << 28);
+ unsigned int l_rsgain, r_rsgain;
+ struct rt1320_datafixpoint r0_data[2];
+ int ret;
+
+ /* read L/Rch Rs Gain - it uses for compensating the R0 value */
+ rt1320_pr_read(rt1320, 0x1058, &pr_1058);
+ rt1320_pr_read(rt1320, 0x1059, &pr_1059);
+ rt1320_pr_read(rt1320, 0x105a, &pr_105a);
+ l_rsgain = ((pr_1059 & 0x7f) << 2) | ((pr_105a & 0xc0) >> 6);
+ r_rsgain = ((pr_1058 & 0xff) << 1) | ((pr_1059 & 0x80) >> 7);
+ dev_dbg(dev, "%s, LR rsgain=0x%x, 0x%x\n", __func__, l_rsgain, r_rsgain);
+
+ l_rsratio = rt1320_rsgain_to_rsratio(rt1320, l_rsgain);
+ r_rsratio = rt1320_rsgain_to_rsratio(rt1320, r_rsgain);
+ dev_dbg(dev, "%s, LR rsratio=%lld, %lld\n", __func__, l_rsratio, r_rsratio);
+
+ l_invrs = div_u64(l_rsratio * factor, 1000000000U);
+ r_invrs = div_u64(r_rsratio * factor, 1000000000U);
+
+ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 6, &r0_data[0], sizeof(struct rt1320_datafixpoint));
+ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 7, &r0_data[1], sizeof(struct rt1320_datafixpoint));
+
+ r0_data[0].invrs = l_invrs;
+ r0_data[1].invrs = r_invrs;
+ dev_dbg(dev, "%s, write DSP LR invrs=0x%x, 0x%x\n", __func__, r0_data[0].invrs, r0_data[1].invrs);
+
+ rt1320_fw_param_protocol(rt1320, RT1320_SET_PARAM, 6, &r0_data[0], sizeof(struct rt1320_datafixpoint));
+ rt1320_fw_param_protocol(rt1320, RT1320_SET_PARAM, 7, &r0_data[1], sizeof(struct rt1320_datafixpoint));
+ ret = rt1320_check_fw_ready(rt1320);
+ if (ret < 0)
+ dev_err(dev, "%s: Failed to set FW param 6,7!\n", __func__);
+
+ return ret;
+}
+
+static void rt1320_calc_r0(struct rt1320_sdw_priv *rt1320)
+{
+ struct device *dev = &rt1320->sdw_slave->dev;
+ unsigned long long l_calir0, r_calir0, l_calir0_lo, r_calir0_lo;
+
+ l_calir0 = rt1320->r0_l_reg >> 27;
+ r_calir0 = rt1320->r0_r_reg >> 27;
+ l_calir0_lo = ((rt1320->r0_l_reg & ((1ull << 27) - 1)) * 1000) >> 27;
+ r_calir0_lo = ((rt1320->r0_r_reg & ((1ull << 27) - 1)) * 1000) >> 27;
+
+ dev_dbg(dev, "%s, l_calir0=%lld.%03lld ohm, r_calir0=%lld.%03lld ohm\n", __func__,
+ l_calir0, l_calir0_lo, r_calir0, r_calir0_lo);
+}
+
+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 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 */
+
+ 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]);
+ 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, &reg_c5fb);
+ regmap_read(rt1320->regmap, 0xc570, &reg_c570);
+ regmap_read(rt1320->regmap, 0xcd00, &reg_cd00);
+
+ 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);
+ if (ret < 0) {
+ dev_dbg(dev, "%s, PDE=PS0 is NOT ready\n", __func__);
+ goto _finish_;
+ }
+
+ regmap_read(rt1320->regmap, fw_status_addr, &fw_ready);
+ fw_ready &= 0x1;
+ if (!fw_ready) {
+ dev_dbg(dev, "%s, DSP FW is NOT ready. Please load DSP FW first\n", __func__);
+ goto _finish_;
+ }
+
+ ret = rt1320_check_power_state_ready(rt1320, RT1320_NORMAL_STATE);
+ if (ret < 0) {
+ dev_dbg(dev, "%s, DSP FW PS is NOT ready\n", __func__);
+ goto _finish_;
+ }
+
+ if (rt1320->dev_id == RT1320_DEV_ID)
+ regmap_write(rt1320->regmap, 0xc5fb, 0x00);
+ regmap_write(rt1320->regmap, 0xc570, 0x0b);
+ regmap_write(rt1320->regmap, 0xcd00, 0xc5);
+
+ /* disable silence detection */
+ regmap_update_bits(rt1320->regmap, 0xc044, 0xe0, 0x00);
+ dev_dbg(dev, "%s, disable silence detection\n", __func__);
+
+ ret = rt1320_check_power_state_ready(rt1320, RT1320_K_R0_STATE);
+ if (ret < 0) {
+ dev_dbg(dev, "%s, check class D status before k r0\n", __func__);
+ goto _finish_;
+ }
+
+ for (tmp = 0; tmp < delay_s; tmp++) {
+ msleep(1000);
+ pm_runtime_mark_last_busy(dev);
+
+ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 11, &l_re[0], sizeof(l_re));
+ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 12, &r_re[0], sizeof(r_re));
+
+ dev_dbg(dev, "%s, LR re=0x%x, 0x%x\n", __func__, l_re[4], r_re[4]);
+ dev_dbg(dev, "%s, waiting for calibration R0...%d seconds\n", __func__, tmp + 1);
+ }
+
+ /* Get Calibration data */
+ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 11, &l_re[0], sizeof(l_re));
+ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 12, &r_re[0], sizeof(r_re));
+ dev_dbg(dev, "%s, LR re=0x%x, 0x%x\n", __func__, l_re[4], r_re[4]);
+
+ /* Get advance gain/mean r0 */
+ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 6, &audfixpoint[0], sizeof(struct rt1320_datafixpoint));
+ l_meanr0 = audfixpoint[0].meanr0;
+ l_advancegain = audfixpoint[0].advancegain;
+ l_meanr0 = ((l_meanr0 * 1000U) / factor);
+ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 7, &audfixpoint[1], sizeof(struct rt1320_datafixpoint));
+ r_meanr0 = audfixpoint[1].meanr0;
+ r_advancegain = audfixpoint[1].advancegain;
+ r_meanr0 = ((r_meanr0 * 1000U) / factor);
+ dev_dbg(dev, "%s, LR meanr0=%lld, %lld\n", __func__, l_meanr0, r_meanr0);
+ dev_dbg(dev, "%s, LR advanceGain=0x%x, 0x%x\n", __func__, l_advancegain, r_advancegain);
+ dev_dbg(dev, "%s, LR invrs=0x%x, 0x%x\n", __func__, audfixpoint[0].invrs, audfixpoint[1].invrs);
+
+ /* enable silence detection */
+ regmap_update_bits(rt1320->regmap, 0xc044, 0xe0, 0xe0);
+ dev_dbg(dev, "%s, enable silence detection\n", __func__);
+
+ regmap_write(rt1320->regmap, 0xc5fb, reg_c5fb);
+ regmap_write(rt1320->regmap, 0xc570, reg_c570);
+ regmap_write(rt1320->regmap, 0xcd00, reg_cd00);
+
+ rt1320->r0_l_reg = l_re[4];
+ rt1320->r0_r_reg = r_re[4];
+ rt1320->cali_done = true;
+ rt1320_calc_r0(rt1320);
+
+_finish_:
+ regmap_write(rt1320->regmap,
+ 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);
+
+ /* 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]);
+ regmap_write(rt1320->regmap, 0xdd09, vol_reg[1]);
+ regmap_write(rt1320->regmap, 0xdd08, vol_reg[0]);
+}
+
+static int rt1320_r0_cali_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt1320->cali_done;
+ return 0;
+}
+
+static int rt1320_r0_cali_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(rt1320->component);
+ int ret;
+
+ if (!rt1320->hw_init)
+ return 0;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ rt1320->cali_done = false;
+ snd_soc_dapm_mutex_lock(dapm);
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF &&
+ ucontrol->value.integer.value[0]) {
+ rt1320_calibrate(rt1320);
+ }
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return 0;
+}
+
/*
* The 'patch code' is written to the patch code area.
* The patch code area is used for SDCA register expansion flexibility.
@@ -844,6 +1359,431 @@ 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;
+ 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;
+ }
+
+ 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) {
+ dev_warn(dev, "%s, DSP FW is NOT ready\n", __func__);
+ goto _exit_;
+ }
+
+ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 3, &l_t0_data[0], sizeof(l_t0_data));
+ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 4, &r_t0_data[0], sizeof(r_t0_data));
+
+ l_t0_data[37] = l_t0 * factor;
+ r_t0_data[37] = r_t0 * factor;
+
+ dev_dbg(dev, "%s, write LR t0=0x%x, 0x%x\n", __func__, l_t0_data[37], r_t0_data[37]);
+
+ rt1320_fw_param_protocol(rt1320, RT1320_SET_PARAM, 3, &l_t0_data[0], sizeof(l_t0_data));
+ rt1320_fw_param_protocol(rt1320, RT1320_SET_PARAM, 4, &r_t0_data[0], sizeof(r_t0_data));
+ if (rt1320_check_fw_ready(rt1320) < 0)
+ dev_err(dev, "%s: Failed to set FW param 3,4!\n", __func__);
+
+ rt1320->temp_l_calib = l_t0;
+ rt1320->temp_r_calib = r_t0;
+
+ memset(&l_t0_data[0], 0x00, sizeof(l_t0_data));
+ memset(&r_t0_data[0], 0x00, sizeof(r_t0_data));
+ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 3, &l_t0_data[0], sizeof(l_t0_data));
+ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 4, &r_t0_data[0], sizeof(r_t0_data));
+ dev_dbg(dev, "%s, read after writing LR t0=0x%x, 0x%x\n", __func__, l_t0_data[37], r_t0_data[37]);
+
+_exit_:
+ regmap_write(rt1320->regmap,
+ 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);
+}
+
+static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320)
+{
+ struct device *dev = &rt1320->sdw_slave->dev;
+ static const char func_tag[] = "FUNC";
+ static const char xu_tag[] = "XU";
+ const struct firmware *rae_fw = NULL;
+ unsigned int fw_offset;
+ unsigned char *fw_data;
+ unsigned char *param_data;
+ unsigned int addr, size;
+ unsigned int func, value;
+ const char *dmi_vendor, *dmi_product, *dmi_sku;
+ int len_vendor, len_product, len_sku;
+ char rae_filename[512];
+ char tag[5];
+ int ret = 0;
+ int retry = 200;
+
+ 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);
+
+ if (dmi_vendor && dmi_product && dmi_sku) {
+ len_vendor = strchrnul(dmi_vendor, ' ') - dmi_vendor;
+ len_product = strchrnul(dmi_product, ' ') - dmi_product;
+ len_sku = strchrnul(dmi_sku, ' ') - dmi_sku;
+
+ snprintf(rae_filename, sizeof(rae_filename),
+ "realtek/rt1320/rt1320_RAE_%.*s_%.*s_%.*s.dat",
+ len_vendor, dmi_vendor, len_product, dmi_product, len_sku, dmi_sku);
+ dev_dbg(dev, "%s: try to load RAE file %s\n", __func__, rae_filename);
+ } else {
+ dev_warn(dev, "%s: Can't find proper RAE file name\n", __func__);
+ return -EINVAL;
+ }
+
+ 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);
+
+ 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__);
+ return -ETIMEDOUT;
+ }
+
+ dev_dbg(dev, "%s, rae_fw size=0x%zx\n", __func__, rae_fw->size);
+ regcache_cache_bypass(rt1320->regmap, true);
+ for (fw_offset = 0; fw_offset < rae_fw->size;) {
+
+ dev_dbg(dev, "%s, fw_offset=0x%x\n", __func__, fw_offset);
+
+ fw_data = (unsigned char *)&rae_fw->data[fw_offset];
+
+ memcpy(tag, fw_data, 4);
+ tag[4] = '\0';
+ dev_dbg(dev, "%s, tag=%s\n", __func__, tag);
+ if (strcmp(tag, xu_tag) == 0) {
+ dev_dbg(dev, "%s: This is a XU tag", __func__);
+ memcpy(&addr, (fw_data + 4), 4);
+ memcpy(&size, (fw_data + 8), 4);
+ param_data = (unsigned char *)(fw_data + 12);
+
+ dev_dbg(dev, "%s: addr=0x%x, size=0x%x\n", __func__, addr, size);
+
+ /*
+ * UI register ranges from 0x1000d000 to 0x1000d7ff
+ * UI registers should be accessed by tuning tool.
+ * So, there registers should be cached.
+ */
+ if (addr <= 0x1000d7ff && addr >= 0x1000d000)
+ regcache_cache_bypass(rt1320->regmap, false);
+
+ rt1320_data_rw(rt1320, addr, param_data, size, RT1320_PARAM_WRITE);
+
+ regcache_cache_bypass(rt1320->regmap, true);
+
+ fw_offset += (size + 12);
+ } else if (strcmp(tag, func_tag) == 0) {
+ dev_err(dev, "%s: This is a FUNC tag", __func__);
+
+ memcpy(&func, (fw_data + 4), 4);
+ memcpy(&value, (fw_data + 8), 4);
+
+ dev_dbg(dev, "%s: func=0x%x, value=0x%x\n", __func__, func, value);
+ if (func == 1) //DelayMs
+ msleep(value);
+
+ fw_offset += 12;
+ } else {
+ dev_err(dev, "%s: This is NOT a XU file (wrong tag)", __func__);
+ break;
+ }
+ }
+
+ regcache_cache_bypass(rt1320->regmap, false);
+ release_firmware(rae_fw);
+
+ } else {
+ dev_err(dev, "%s: Failed to load %s firmware\n", __func__, rae_filename);
+ ret = -EINVAL;
+ 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);
+
+ rt1320->rae_update_done = true;
+
+_exit_:
+ regmap_write(rt1320->regmap,
+ 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);
+
+ return ret;
+}
+
+static void rt1320_dspfw_load_code(struct rt1320_sdw_priv *rt1320)
+{
+struct rt1320_imageinfo {
+ unsigned int addr;
+ unsigned int size;
+};
+
+struct rt1320_dspfwheader {
+ unsigned int sync;
+ short num;
+ short crc;
+};
+
+ 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;
+ struct rt1320_dspfwheader *fwheader;
+ struct rt1320_imageinfo *ptr_img;
+ struct sdw_bpt_section sec[10];
+ const struct firmware *fw = NULL;
+ unsigned char *fw_data;
+ bool dev_fw_match = false;
+ static const char hdr_sig[] = "AFX";
+ unsigned int hdr_size = 0;
+ const char *dmi_vendor, *dmi_product, *dmi_sku;
+ 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);
+
+ if (dmi_vendor && dmi_product && dmi_sku) {
+ len_vendor = strchrnul(dmi_vendor, ' ') - dmi_vendor;
+ len_product = strchrnul(dmi_product, ' ') - dmi_product;
+ len_sku = strchrnul(dmi_sku, ' ') - dmi_sku;
+
+ snprintf(filename, sizeof(filename),
+ "realtek/rt1320/rt1320_%.*s_%.*s_%.*s.dat",
+ len_vendor, dmi_vendor, len_product, dmi_product, len_sku, dmi_sku);
+
+ dev_dbg(dev, "%s: try to load FW file %s\n", __func__, filename);
+ } else if (rt1320->dspfw_name) {
+ snprintf(filename, sizeof(filename), "rt1320_%s.dat",
+ rt1320->dspfw_name);
+ dev_dbg(dev, "%s: try to load FW file %s\n", __func__, filename);
+ } else {
+ dev_warn(dev, "%s: Can't find proper FW file name\n", __func__);
+ return;
+ }
+
+ snd_soc_dapm_mutex_lock(dapm);
+ 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) {
+ dev_dbg(dev, "%s, DSP FW was already\n", __func__);
+ rt1320->fw_load_done = true;
+ 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;
+ dev_dbg(dev, "%s, fw sync = 0x%x, num=%d, crc=0x%x\n", __func__,
+ fwheader->sync, fwheader->num, fwheader->crc);
+
+ if (fwheader->sync != 0x0a1c5679) {
+ dev_err(dev, "%s: FW sync error\n", __func__);
+ release_firmware(fw);
+ goto _exit_;
+ }
+
+ fw_offset = sizeof(struct rt1320_dspfwheader) + (sizeof(struct rt1320_imageinfo) * fwheader->num);
+ dev_dbg(dev, "%s, fw_offset = 0x%x\n", __func__, fw_offset);
+
+ regcache_cache_bypass(rt1320->regmap, true);
+
+ for (i = 0; i < fwheader->num; i++) {
+ ptr_img = (struct rt1320_imageinfo *)&fw->data[sizeof(struct rt1320_dspfwheader) + (sizeof(struct rt1320_imageinfo) * i)];
+
+ dev_dbg(dev, "%s, fw_offset=0x%x, load fw addr=0x%x, size=%d\n", __func__,
+ fw_offset, ptr_img->addr, ptr_img->size);
+
+ fw_data = (unsigned char *)&fw->data[fw_offset];
+
+ /* The binary file has a header of 64 bytes */
+ if (memcmp(fw_data, hdr_sig, sizeof(hdr_sig)) == 0)
+ hdr_size = 64;
+ else
+ hdr_size = 0;
+
+ sec[i].addr = ptr_img->addr;
+ sec[i].len = ptr_img->size - hdr_size;
+ sec[i].buf = fw_data + hdr_size;
+
+ dev_dbg(dev, "%s, hdr_size=%d, sec[%d].buf[0]=0x%x\n",
+ __func__, hdr_size, i, sec[i].buf[0]);
+
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ if (ptr_img->addr == 0x3fc29d80)
+ if (fw_data[9] == '0')
+ dev_fw_match = true;
+ break;
+ case RT1321_DEV_ID:
+ if (ptr_img->addr == 0x3fc00000)
+ if (fw_data[9] == '1')
+ dev_fw_match = true;
+ break;
+ default:
+ dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id);
+ goto _exit_;
+ }
+
+ fw_offset += ptr_img->size;
+ }
+
+ if (dev_fw_match) {
+ dev_dbg(dev, "%s, starting BRA downloading FW..\n", __func__);
+ rt1320->bra_msg.dev_num = rt1320->sdw_slave->dev_num;
+ rt1320->bra_msg.flags = SDW_MSG_FLAG_WRITE;
+ rt1320->bra_msg.sections = fwheader->num;
+ rt1320->bra_msg.sec = &sec[0];
+ rt1320_data_rw(rt1320, 0, NULL, 0, RT1320_BRA_WRITE);
+ dev_dbg(dev, "%s, BRA downloading FW done..\n", __func__);
+ }
+
+ regcache_cache_bypass(rt1320->regmap, false);
+ release_firmware(fw);
+
+ if (!dev_fw_match) {
+ dev_err(dev, "%s: FW file doesn't match to device\n", __func__);
+ goto _exit_;
+ }
+ } else {
+ dev_err(dev, "%s: Failed to load %s firmware\n", __func__, filename);
+ 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:
+ 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:
+ regmap_write(rt1320->regmap, 0x3fc2dfcb, 0x00);
+ regmap_write(rt1320->regmap, 0x3fc2dfca, 0x00);
+ regmap_write(rt1320->regmap, 0x3fc2dfc9, 0x00);
+ regmap_write(rt1320->regmap, 0x3fc2dfc8, 0x00);
+ break;
+ }
+
+ /* enable DSP FW */
+ regmap_write(rt1320->regmap, 0xc081, 0xfc);
+ regmap_update_bits(rt1320->regmap, 0xf01e, 0x1, 0x0);
+
+ /* RsRatio should restore into DSP FW when FW was ready */
+ rt1320_invrs_load(rt1320);
+
+ /* DSP clock switches to PLL */
+ regmap_write(rt1320->regmap, 0xc081, 0xfc);
+ /* pass DSP settings */
+ regmap_write(rt1320->regmap, 0xc5c3, 0xf3);
+ regmap_write(rt1320->regmap, 0xc5c8, 0x05);
+
+ rt1320->fw_load_done = true;
+
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_mark_last_busy(dev);
+
+_exit_:
+ regmap_write(rt1320->regmap,
+ 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);
+
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+
+static void rt1320_load_dspfw_work(struct work_struct *work)
+{
+ struct rt1320_sdw_priv *rt1320 =
+ container_of(work, struct rt1320_sdw_priv, load_dspfw_work);
+ int ret;
+
+ ret = pm_runtime_resume(rt1320->component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return;
+
+ dev_dbg(&rt1320->sdw_slave->dev, "%s, Starting to reload DSP FW", __func__);
+ rt1320_dspfw_load_code(rt1320);
+}
+
static void rt1320_vc_preset(struct rt1320_sdw_priv *rt1320)
{
struct sdw_slave *slave = rt1320->sdw_slave;
@@ -956,6 +1896,10 @@ static int rt1320_io_init(struct device *dev, struct sdw_slave *slave)
regmap_write(rt1320->regmap,
SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT0, RT1320_SDCA_CTL_FUNC_STATUS, 0),
FUNCTION_NEEDS_INITIALIZATION);
+
+ /* reload DSP FW */
+ if (rt1320->fw_load_done)
+ schedule_work(&rt1320->load_dspfw_work);
}
if (!rt1320->first_hw_init && rt1320->version_id == RT1320_VA && rt1320->dev_id == RT1320_DEV_ID) {
regmap_write(rt1320->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23,
@@ -1358,6 +2302,225 @@ static SOC_ENUM_SINGLE_DECL(rt1320_rx_data_ch_enum,
static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0);
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 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);
+ if (ret < 0) {
+ dev_dbg(dev, "%s, PDE=PS0 is NOT ready\n", __func__);
+ goto _timeout_;
+ }
+
+ regmap_read(rt1320->regmap, fw_status_addr, &fw_ready);
+ fw_ready &= 0x1;
+ if (!fw_ready) {
+ dev_dbg(dev, "%s, DSP FW is NOT ready\n", __func__);
+ goto _timeout_;
+ }
+
+ ret = rt1320_check_power_state_ready(rt1320, RT1320_NORMAL_STATE);
+ if (ret < 0) {
+ dev_dbg(dev, "%s, DSP FW PS is NOT ready\n", __func__);
+ goto _timeout_;
+ }
+
+ rt1320_set_advancemode(rt1320);
+
+_timeout_:
+ regmap_write(rt1320->regmap,
+ 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);
+
+ return ret;
+}
+
+static int rt1320_r0_load_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt1320->r0_l_reg;
+ ucontrol->value.integer.value[1] = rt1320->r0_r_reg;
+
+ return 0;
+}
+
+static int rt1320_r0_load_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(rt1320->component);
+ int ret;
+
+ if (!rt1320->hw_init)
+ return 0;
+
+ if (ucontrol->value.integer.value[0] == 0 ||
+ ucontrol->value.integer.value[1] == 0)
+ return -EINVAL;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ snd_soc_dapm_mutex_lock(dapm);
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
+ rt1320->r0_l_reg = ucontrol->value.integer.value[0];
+ rt1320->r0_r_reg = ucontrol->value.integer.value[1];
+ rt1320_calc_r0(rt1320);
+ rt1320_r0_load(rt1320);
+ }
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return 0;
+}
+
+static int rt1320_t0_r0_load_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.max = kcontrol->private_value;
+
+ return 0;
+}
+
+#define RT1320_T0_R0_LOAD(xname, xmax, xhandler_get, xhandler_put) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = rt1320_t0_r0_load_info, \
+ .get = xhandler_get, \
+ .put = xhandler_put, \
+ .private_value = xmax, \
+}
+
+static int rt1320_dspfw_load_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt1320->fw_load_done;
+ return 0;
+}
+
+static int rt1320_dspfw_load_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ int ret;
+
+ if (!rt1320->hw_init)
+ return 0;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF &&
+ ucontrol->value.integer.value[0])
+ rt1320_dspfw_load_code(rt1320);
+
+ if (!ucontrol->value.integer.value[0])
+ rt1320->fw_load_done = false;
+
+ return 0;
+}
+
+static int rt1320_rae_update_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt1320->rae_update_done;
+ return 0;
+}
+
+static int rt1320_rae_update_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ int ret;
+
+ if (!rt1320->hw_init)
+ return 0;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF &&
+ ucontrol->value.integer.value[0] && rt1320->fw_load_done)
+ rt1320_rae_load(rt1320);
+
+ if (!ucontrol->value.integer.value[0])
+ rt1320->rae_update_done = false;
+
+ return 0;
+}
+
+static int rt1320_r0_temperature_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt1320->temp_l_calib;
+ ucontrol->value.integer.value[1] = rt1320->temp_r_calib;
+ return 0;
+}
+
+static int rt1320_r0_temperature_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(rt1320->component);
+ int ret;
+
+ if (!rt1320->hw_init)
+ return 0;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ snd_soc_dapm_mutex_lock(dapm);
+ if ((snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) &&
+ ucontrol->value.integer.value[0] && ucontrol->value.integer.value[1])
+ rt1320_t0_load(rt1320, ucontrol->value.integer.value[0], ucontrol->value.integer.value[1]);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return 0;
+}
+
static const struct snd_kcontrol_new rt1320_snd_controls[] = {
SOC_DOUBLE_R_EXT_TLV("FU21 Playback Volume",
SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_01),
@@ -1371,6 +2534,17 @@ static const struct snd_kcontrol_new rt1320_snd_controls[] = {
RT_SDCA_EXT_TLV("FU Capture Volume",
SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01),
rt1320_set_gain_get, rt1320_set_gain_put, 4, 0x3f, in_vol_tlv, rt1320_dmic_fu_info),
+
+ SOC_SINGLE_EXT("R0 Calibration", SND_SOC_NOPM, 0, 1, 0,
+ rt1320_r0_cali_get, rt1320_r0_cali_put),
+ SOC_SINGLE_EXT("DSP FW Update", SND_SOC_NOPM, 0, 1, 0,
+ rt1320_dspfw_load_get, rt1320_dspfw_load_put),
+ RT1320_T0_R0_LOAD("R0 Load Mode", 0xffffffff,
+ rt1320_r0_load_mode_get, rt1320_r0_load_mode_put),
+ RT1320_T0_R0_LOAD("R0 Temperature", 0xff,
+ rt1320_r0_temperature_get, rt1320_r0_temperature_put),
+ SOC_SINGLE_EXT("RAE Update", SND_SOC_NOPM, 0, 1, 0,
+ rt1320_rae_update_get, rt1320_rae_update_put),
};
static const struct snd_kcontrol_new rt1320_spk_l_dac =
@@ -1606,6 +2780,18 @@ static int rt1320_sdw_component_probe(struct snd_soc_component *component)
if (ret < 0 && ret != -EACCES)
return ret;
+ /* Apply temperature and calibration data from device property */
+ if ((rt1320->temp_l_calib <= 0xff) && (rt1320->temp_l_calib > 0) &&
+ (rt1320->temp_r_calib <= 0xff) && (rt1320->temp_r_calib > 0))
+ rt1320_t0_load(rt1320, rt1320->temp_l_calib, rt1320->temp_r_calib);
+
+ if (rt1320->r0_l_calib && rt1320->r0_r_calib) {
+ rt1320->r0_l_reg = rt1320->r0_l_calib;
+ rt1320->r0_r_reg = rt1320->r0_r_calib;
+ rt1320_calc_r0(rt1320);
+ rt1320_r0_load(rt1320);
+ }
+
return 0;
}
@@ -1667,6 +2853,26 @@ static struct snd_soc_dai_driver rt1320_sdw_dai[] = {
},
};
+static int rt1320_parse_dp(struct rt1320_sdw_priv *rt1320, struct device *dev)
+{
+ device_property_read_u32(dev, "realtek,temperature_l_calib",
+ &rt1320->temp_l_calib);
+ device_property_read_u32(dev, "realtek,temperature_r_calib",
+ &rt1320->temp_r_calib);
+ device_property_read_u32(dev, "realtek,r0_l_calib",
+ &rt1320->r0_l_calib);
+ device_property_read_u32(dev, "realtek,r0_r_calib",
+ &rt1320->r0_r_calib);
+ device_property_read_string(dev, "realtek,dspfw-name",
+ &rt1320->dspfw_name);
+
+ dev_dbg(dev, "%s: temp_l_calib: %d temp_r_calib: %d r0_l_calib: %d, r0_r_calib: %d",
+ __func__, rt1320->temp_l_calib, rt1320->temp_r_calib, rt1320->r0_l_calib, rt1320->r0_r_calib);
+ dev_dbg(dev, "%s: dspfw_name: %s", __func__, rt1320->dspfw_name);
+
+ return 0;
+}
+
static int rt1320_sdw_init(struct device *dev, struct regmap *regmap,
struct regmap *mbq_regmap, struct sdw_slave *slave)
{
@@ -1685,6 +2891,8 @@ static int rt1320_sdw_init(struct device *dev, struct regmap *regmap,
regcache_cache_only(rt1320->regmap, true);
regcache_cache_only(rt1320->mbq_regmap, true);
+ rt1320_parse_dp(rt1320, dev);
+
/*
* Mark hw_init to false
* HW init will be performed when device reports present
@@ -1696,6 +2904,8 @@ static int rt1320_sdw_init(struct device *dev, struct regmap *regmap,
rt1320->fu_mixer_mute[0] = rt1320->fu_mixer_mute[1] =
rt1320->fu_mixer_mute[2] = rt1320->fu_mixer_mute[3] = true;
+ INIT_WORK(&rt1320->load_dspfw_work, rt1320_load_dspfw_work);
+
ret = devm_snd_soc_register_component(dev,
&soc_component_sdw_rt1320,
rt1320_sdw_dai,
@@ -1742,6 +2952,9 @@ static int rt1320_sdw_probe(struct sdw_slave *slave,
static int rt1320_sdw_remove(struct sdw_slave *slave)
{
+ struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(&slave->dev);
+
+ cancel_work_sync(&rt1320->load_dspfw_work);
pm_runtime_disable(&slave->dev);
return 0;
diff --git a/sound/soc/codecs/rt1320-sdw.h b/sound/soc/codecs/rt1320-sdw.h
index a6d90e259dc9..5a9f496dd848 100644
--- a/sound/soc/codecs/rt1320-sdw.h
+++ b/sound/soc/codecs/rt1320-sdw.h
@@ -13,6 +13,7 @@
#include <linux/soundwire/sdw_type.h>
#include <linux/soundwire/sdw_registers.h>
#include <sound/soc.h>
+#include "../../../drivers/soundwire/bus.h"
#define RT1320_DEV_ID 0x6981
#define RT1321_DEV_ID 0x7045
@@ -22,6 +23,8 @@
#define RT1320_DEV_ID_1 0xc405
#define RT1320_DEV_ID_0 0xc406
+#define RT1320_POWER_STATE 0xc560
+
#define RT1321_PATCH_MAIN_VER 0x1000cffe
#define RT1321_PATCH_BETA_VER 0x1000cfff
@@ -96,6 +99,57 @@ enum rt1320_version_id {
#define RT1320_VC_MCU_PATCH "realtek/rt1320/rt1320-patch-code-vc.bin"
#define RT1321_VA_MCU_PATCH "realtek/rt1320/rt1321-patch-code-va.bin"
+#define RT1320_FW_PARAM_ADDR 0x3fc2ab80
+#define RT1320_CMD_ID 0x3fc2ab81
+#define RT1320_CMD_PARAM_ADDR 0x3fc2ab90
+#define RT1320_DSPFW_STATUS_ADDR 0x3fc2bfc4
+
+#define RT1321_FW_PARAM_ADDR 0x3fc2d300
+#define RT1321_CMD_ID 0x3fc2d301
+#define RT1321_CMD_PARAM_ADDR 0x3fc2d310
+#define RT1321_DSPFW_STATUS_ADDR 0x3fc2dfc4
+
+/* FW parameter id 6, 7 */
+struct rt1320_datafixpoint {
+ int silencedetect;
+ int r0;
+ int meanr0;
+ int advancegain;
+ int ts;
+ int re;
+ int t;
+ int invrs;
+};
+
+struct rt1320_paramcmd {
+ unsigned char moudleid;
+ unsigned char commandtype;
+ unsigned short reserved1;
+ unsigned int commandlength;
+ long long reserved2;
+ unsigned int paramid;
+ unsigned int paramlength;
+};
+
+enum rt1320_fw_cmdid {
+ RT1320_FW_READY,
+ RT1320_SET_PARAM,
+ RT1320_GET_PARAM,
+ RT1320_GET_POOLSIZE,
+};
+
+enum rt1320_power_state {
+ RT1320_NORMAL_STATE = 0x18,
+ RT1320_K_R0_STATE = 0x1b,
+};
+
+enum rt1320_rw_type {
+ RT1320_BRA_WRITE = 0,
+ RT1320_BRA_READ = 1,
+ RT1320_PARAM_WRITE = 2,
+ RT1320_PARAM_READ = 3,
+};
+
struct rt1320_sdw_priv {
struct snd_soc_component *component;
struct regmap *regmap;
@@ -108,6 +162,18 @@ struct rt1320_sdw_priv {
unsigned int dev_id;
bool fu_dapm_mute;
bool fu_mixer_mute[4];
+ unsigned long long r0_l_reg;
+ unsigned long long r0_r_reg;
+ unsigned int r0_l_calib;
+ unsigned int r0_r_calib;
+ unsigned int temp_l_calib;
+ unsigned int temp_r_calib;
+ const char *dspfw_name;
+ bool cali_done;
+ bool fw_load_done;
+ bool rae_update_done;
+ struct work_struct load_dspfw_work;
+ struct sdw_bpt_msg bra_msg;
};
#endif /* __RT1320_SDW_H__ */
diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c
index 5c33aeaced2f..bba714020c70 100644
--- a/sound/soc/codecs/rt274.c
+++ b/sound/soc/codecs/rt274.c
@@ -1189,7 +1189,7 @@ static int rt274_i2c_probe(struct i2c_client *i2c)
regmap_write(rt274->regmap, RT274_UNSOLICITED_MIC, 0x82);
if (rt274->i2c->irq) {
- ret = request_threaded_irq(rt274->i2c->irq, NULL, rt274_irq,
+ ret = devm_request_threaded_irq(&rt274->i2c->dev, rt274->i2c->irq, NULL, rt274_irq,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt274", rt274);
if (ret != 0) {
dev_err(&i2c->dev,
@@ -1205,15 +1205,6 @@ static int rt274_i2c_probe(struct i2c_client *i2c)
return ret;
}
-static void rt274_i2c_remove(struct i2c_client *i2c)
-{
- struct rt274_priv *rt274 = i2c_get_clientdata(i2c);
-
- if (i2c->irq)
- free_irq(i2c->irq, rt274);
-}
-
-
static struct i2c_driver rt274_i2c_driver = {
.driver = {
.name = "rt274",
@@ -1223,7 +1214,6 @@ static struct i2c_driver rt274_i2c_driver = {
#endif
},
.probe = rt274_i2c_probe,
- .remove = rt274_i2c_remove,
.id_table = rt274_i2c_id,
};
diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c
index 2fbb5860c421..195658f626cc 100644
--- a/sound/soc/codecs/rt286.c
+++ b/sound/soc/codecs/rt286.c
@@ -1236,7 +1236,7 @@ static int rt286_i2c_probe(struct i2c_client *i2c)
}
if (rt286->i2c->irq) {
- ret = request_threaded_irq(rt286->i2c->irq, NULL, rt286_irq,
+ ret = devm_request_threaded_irq(&rt286->i2c->dev, rt286->i2c->irq, NULL, rt286_irq,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt286", rt286);
if (ret != 0) {
dev_err(&i2c->dev,
@@ -1252,22 +1252,12 @@ static int rt286_i2c_probe(struct i2c_client *i2c)
return ret;
}
-static void rt286_i2c_remove(struct i2c_client *i2c)
-{
- struct rt286_priv *rt286 = i2c_get_clientdata(i2c);
-
- if (i2c->irq)
- free_irq(i2c->irq, rt286);
-}
-
-
static struct i2c_driver rt286_i2c_driver = {
.driver = {
.name = "rt286",
.acpi_match_table = ACPI_PTR(rt286_acpi_match),
},
.probe = rt286_i2c_probe,
- .remove = rt286_i2c_remove,
.id_table = rt286_i2c_id,
};
diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c
index 02247593513a..7d532a5a5f73 100644
--- a/sound/soc/codecs/rt298.c
+++ b/sound/soc/codecs/rt298.c
@@ -1282,7 +1282,7 @@ static int rt298_i2c_probe(struct i2c_client *i2c)
rt298->is_hp_in = -1;
if (rt298->i2c->irq) {
- ret = request_threaded_irq(rt298->i2c->irq, NULL, rt298_irq,
+ ret = devm_request_threaded_irq(&rt298->i2c->dev, rt298->i2c->irq, NULL, rt298_irq,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt298", rt298);
if (ret != 0) {
dev_err(&i2c->dev,
@@ -1298,22 +1298,12 @@ static int rt298_i2c_probe(struct i2c_client *i2c)
return ret;
}
-static void rt298_i2c_remove(struct i2c_client *i2c)
-{
- struct rt298_priv *rt298 = i2c_get_clientdata(i2c);
-
- if (i2c->irq)
- free_irq(i2c->irq, rt298);
-}
-
-
static struct i2c_driver rt298_i2c_driver = {
.driver = {
.name = "rt298",
.acpi_match_table = ACPI_PTR(rt298_acpi_match),
},
.probe = rt298_i2c_probe,
- .remove = rt298_i2c_remove,
.id_table = rt298_i2c_id,
};
diff --git a/sound/soc/codecs/rt5575-spi.c b/sound/soc/codecs/rt5575-spi.c
new file mode 100644
index 000000000000..9a349965435b
--- /dev/null
+++ b/sound/soc/codecs/rt5575-spi.c
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * rt5575-spi.c -- ALC5575 SPI driver
+ *
+ * Copyright(c) 2025 Realtek Semiconductor Corp.
+ *
+ */
+
+#include <linux/firmware.h>
+#include <linux/of.h>
+#include <linux/spi/spi.h>
+
+#include "rt5575-spi.h"
+
+#define RT5575_SPI_CMD_BURST_WRITE 5
+#define RT5575_SPI_BUF_LEN 240
+
+struct rt5575_spi_burst_write {
+ u8 cmd;
+ u32 addr;
+ u8 data[RT5575_SPI_BUF_LEN];
+ u8 dummy;
+} __packed;
+
+struct spi_device *rt5575_spi_get_device(struct device *dev)
+{
+ struct spi_device *spi;
+ struct spi_controller *ctlr;
+ struct device_node *spi_np;
+ u32 cs;
+
+ spi_np = of_parse_phandle(dev->of_node, "spi-parent", 0);
+ if (!spi_np) {
+ dev_err(dev, "Failed to get spi-parent phandle\n");
+ return NULL;
+ }
+
+ if (of_property_read_u32_index(dev->of_node, "spi-parent", 1, &cs))
+ cs = 0;
+
+ ctlr = of_find_spi_controller_by_node(spi_np);
+ of_node_put(spi_np);
+ if (!ctlr) {
+ dev_err(dev, "Failed to get spi_controller\n");
+ return NULL;
+ }
+
+ if (cs >= ctlr->num_chipselect) {
+ dev_err(dev, "Chip select has wrong number %d\n", cs);
+ spi_controller_put(ctlr);
+ return NULL;
+ }
+
+ spi = spi_new_device(ctlr, &(struct spi_board_info){
+ .modalias = "rt5575",
+ .chip_select = cs,
+ .max_speed_hz = 10000000,
+ });
+
+ spi_controller_put(ctlr);
+ return spi;
+}
+
+/**
+ * rt5575_spi_burst_write - Write data to SPI by rt5575 address.
+ * @spi: SPI device.
+ * @addr: Start address.
+ * @txbuf: Data buffer for writing.
+ * @len: Data length.
+ *
+ */
+static void rt5575_spi_burst_write(struct spi_device *spi, u32 addr, const u8 *txbuf, size_t len)
+{
+ struct rt5575_spi_burst_write buf = {
+ .cmd = RT5575_SPI_CMD_BURST_WRITE,
+ };
+ unsigned int end, offset = 0;
+
+ while (offset < len) {
+ if (offset + RT5575_SPI_BUF_LEN <= len)
+ end = RT5575_SPI_BUF_LEN;
+ else
+ end = len % RT5575_SPI_BUF_LEN;
+
+ buf.addr = cpu_to_le32(addr + offset);
+ memcpy(&buf.data, &txbuf[offset], end);
+ spi_write(spi, &buf, sizeof(buf));
+
+ offset += RT5575_SPI_BUF_LEN;
+ }
+}
+
+int rt5575_spi_fw_load(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ const struct firmware *firmware;
+ int i, ret;
+ static const char * const fw_path[] = {
+ "realtek/rt5575/rt5575_fw1.bin",
+ "realtek/rt5575/rt5575_fw2.bin",
+ "realtek/rt5575/rt5575_fw3.bin",
+ "realtek/rt5575/rt5575_fw4.bin",
+ };
+ static const u32 fw_addr[] = { 0x5f400000, 0x5f600000, 0x5f7fe000, 0x5f7ff000 };
+
+ for (i = 0; i < ARRAY_SIZE(fw_addr); i++) {
+ ret = request_firmware(&firmware, fw_path[i], dev);
+ if (ret) {
+ dev_err(dev, "Request firmware failure: %d\n", ret);
+ return ret;
+ }
+
+ rt5575_spi_burst_write(spi, fw_addr[i], firmware->data, firmware->size);
+ release_firmware(firmware);
+ }
+
+ return 0;
+}
diff --git a/sound/soc/codecs/rt5575-spi.h b/sound/soc/codecs/rt5575-spi.h
new file mode 100644
index 000000000000..084638820b6f
--- /dev/null
+++ b/sound/soc/codecs/rt5575-spi.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt5575-spi.h -- ALC5575 SPI driver
+ *
+ * Copyright(c) 2025 Realtek Semiconductor Corp.
+ *
+ */
+
+#ifndef __RT5575_SPI_H__
+#define __RT5575_SPI_H__
+
+#if IS_ENABLED(CONFIG_SND_SOC_RT5575_SPI)
+struct spi_device *rt5575_spi_get_device(struct device *dev);
+int rt5575_spi_fw_load(struct spi_device *spi);
+#else
+static inline struct spi_device *rt5575_spi_get_device(struct device *dev)
+{
+ return NULL;
+}
+
+static inline int rt5575_spi_fw_load(struct spi_device *spi)
+{
+ return -EINVAL;
+}
+#endif
+
+#endif /* __RT5575_SPI_H__ */
diff --git a/sound/soc/codecs/rt5575.c b/sound/soc/codecs/rt5575.c
new file mode 100644
index 000000000000..c5525ad195ee
--- /dev/null
+++ b/sound/soc/codecs/rt5575.c
@@ -0,0 +1,352 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * rt5575.c -- ALC5575 ALSA SoC audio component driver
+ *
+ * Copyright(c) 2025 Realtek Semiconductor Corp.
+ *
+ */
+
+#include <linux/i2c.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "rt5575.h"
+#include "rt5575-spi.h"
+
+static bool rt5575_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5575_BOOT:
+ case RT5575_ID:
+ case RT5575_ID_1:
+ case RT5575_MIXL_VOL:
+ case RT5575_MIXR_VOL:
+ case RT5575_PROMPT_VOL:
+ case RT5575_SPK01_VOL:
+ case RT5575_SPK23_VOL:
+ case RT5575_MIC1_VOL:
+ case RT5575_MIC2_VOL:
+ case RT5575_WNC_CTRL:
+ case RT5575_MODE_CTRL:
+ case RT5575_I2S_RATE_CTRL:
+ case RT5575_SLEEP_CTRL:
+ case RT5575_ALG_BYPASS_CTRL:
+ case RT5575_PINMUX_CTRL_2:
+ case RT5575_GPIO_CTRL_1:
+ case RT5575_DSP_BUS_CTRL:
+ case RT5575_SW_INT:
+ case RT5575_DSP_BOOT_ERR:
+ case RT5575_DSP_READY:
+ case RT5575_DSP_CMD_ADDR:
+ case RT5575_EFUSE_DATA_2:
+ case RT5575_EFUSE_DATA_3:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const DECLARE_TLV_DB_SCALE(ob_tlv, -9525, 75, 0);
+
+static const struct snd_kcontrol_new rt5575_snd_controls[] = {
+ SOC_DOUBLE("Speaker CH-01 Playback Switch", RT5575_SPK01_VOL, 31, 15, 1, 1),
+ SOC_DOUBLE_TLV("Speaker CH-01 Playback Volume", RT5575_SPK01_VOL, 17, 1, 167, 0, ob_tlv),
+ SOC_DOUBLE("Speaker CH-23 Playback Switch", RT5575_SPK23_VOL, 31, 15, 1, 1),
+ SOC_DOUBLE_TLV("Speaker CH-23 Playback Volume", RT5575_SPK23_VOL, 17, 1, 167, 0, ob_tlv),
+ SOC_DOUBLE("Mic1 Capture Switch", RT5575_MIC1_VOL, 31, 15, 1, 1),
+ SOC_DOUBLE_TLV("Mic1 Capture Volume", RT5575_MIC1_VOL, 17, 1, 167, 0, ob_tlv),
+ SOC_DOUBLE("Mic2 Capture Switch", RT5575_MIC2_VOL, 31, 15, 1, 1),
+ SOC_DOUBLE_TLV("Mic2 Capture Volume", RT5575_MIC2_VOL, 17, 1, 167, 0, ob_tlv),
+ SOC_DOUBLE_R("Mix Playback Switch", RT5575_MIXL_VOL, RT5575_MIXR_VOL, 31, 1, 1),
+ SOC_DOUBLE_R_TLV("Mix Playback Volume", RT5575_MIXL_VOL, RT5575_MIXR_VOL, 1, 127, 0,
+ ob_tlv),
+ SOC_DOUBLE("Prompt Playback Switch", RT5575_PROMPT_VOL, 31, 15, 1, 1),
+ SOC_DOUBLE_TLV("Prompt Playback Volume", RT5575_PROMPT_VOL, 17, 1, 167, 0, ob_tlv),
+};
+
+static const struct snd_soc_dapm_widget rt5575_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF3RX", "AIF3 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF3TX", "AIF3 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF4RX", "AIF4 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF4TX", "AIF4 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_INPUT("INPUT"),
+ SND_SOC_DAPM_OUTPUT("OUTPUT"),
+};
+
+static const struct snd_soc_dapm_route rt5575_dapm_routes[] = {
+ { "AIF1TX", NULL, "INPUT" },
+ { "AIF2TX", NULL, "INPUT" },
+ { "AIF3TX", NULL, "INPUT" },
+ { "AIF4TX", NULL, "INPUT" },
+ { "OUTPUT", NULL, "AIF1RX" },
+ { "OUTPUT", NULL, "AIF2RX" },
+ { "OUTPUT", NULL, "AIF3RX" },
+ { "OUTPUT", NULL, "AIF4RX" },
+};
+
+static long long rt5575_get_priv_id(struct rt5575_priv *rt5575)
+{
+ int priv_id_low, priv_id_high;
+
+ regmap_write(rt5575->regmap, RT5575_EFUSE_PID, 0xa0000000);
+ regmap_read(rt5575->regmap, RT5575_EFUSE_DATA_2, &priv_id_low);
+ regmap_read(rt5575->regmap, RT5575_EFUSE_DATA_3, &priv_id_high);
+ regmap_write(rt5575->regmap, RT5575_EFUSE_PID, 0);
+
+ return ((long long)priv_id_high << 32) | (long long)priv_id_low;
+}
+
+static int rt5575_probe(struct snd_soc_component *component)
+{
+ struct rt5575_priv *rt5575 = snd_soc_component_get_drvdata(component);
+ struct device *dev = component->dev;
+
+ rt5575->component = component;
+
+ dev_info(dev, "Private ID: %llx\n", rt5575_get_priv_id(rt5575));
+
+ return 0;
+}
+
+#define RT5575_STEREO_RATES SNDRV_PCM_RATE_8000_192000
+#define RT5575_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver rt5575_dai[] = {
+ {
+ .name = "rt5575-aif1",
+ .id = RT5575_AIF1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = RT5575_STEREO_RATES,
+ .formats = RT5575_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = RT5575_STEREO_RATES,
+ .formats = RT5575_FORMATS,
+ },
+ },
+ {
+ .name = "rt5575-aif2",
+ .id = RT5575_AIF2,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = RT5575_STEREO_RATES,
+ .formats = RT5575_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = RT5575_STEREO_RATES,
+ .formats = RT5575_FORMATS,
+ },
+ },
+ {
+ .name = "rt5575-aif3",
+ .id = RT5575_AIF3,
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = RT5575_STEREO_RATES,
+ .formats = RT5575_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF3 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = RT5575_STEREO_RATES,
+ .formats = RT5575_FORMATS,
+ },
+ },
+ {
+ .name = "rt5575-aif4",
+ .id = RT5575_AIF4,
+ .playback = {
+ .stream_name = "AIF4 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = RT5575_STEREO_RATES,
+ .formats = RT5575_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF4 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = RT5575_STEREO_RATES,
+ .formats = RT5575_FORMATS,
+ },
+ },
+};
+
+static const struct snd_soc_component_driver rt5575_soc_component_dev = {
+ .probe = rt5575_probe,
+ .controls = rt5575_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5575_snd_controls),
+ .dapm_widgets = rt5575_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5575_dapm_widgets),
+ .dapm_routes = rt5575_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5575_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config rt5575_dsp_regmap = {
+ .name = "dsp",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 2,
+};
+
+static int rt5575_i2c_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct i2c_client *client = context;
+ struct rt5575_priv *rt5575 = i2c_get_clientdata(client);
+
+ return regmap_read(rt5575->dsp_regmap, reg | RT5575_DSP_MAPPING, val);
+}
+
+static int rt5575_i2c_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct i2c_client *client = context;
+ struct rt5575_priv *rt5575 = i2c_get_clientdata(client);
+
+ return regmap_write(rt5575->dsp_regmap, reg | RT5575_DSP_MAPPING, val);
+}
+
+static const struct regmap_config rt5575_regmap = {
+ .reg_bits = 16,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0xfffc,
+ .readable_reg = rt5575_readable_register,
+ .reg_read = rt5575_i2c_read,
+ .reg_write = rt5575_i2c_write,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt5575_fw_load_by_spi(struct rt5575_priv *rt5575)
+{
+ struct i2c_client *i2c = rt5575->i2c;
+ struct spi_device *spi;
+ struct device *dev = &i2c->dev;
+ int ret;
+
+ spi = rt5575_spi_get_device(dev);
+ if (!spi) {
+ dev_err(dev, "Failed to get spi_device\n");
+ return -ENODEV;
+ }
+
+ regmap_write(rt5575->dsp_regmap, 0xfafafafa, 0x00000004);
+ regmap_write(rt5575->dsp_regmap, 0x18008064, 0x00000000);
+ regmap_write(rt5575->dsp_regmap, 0x18008068, 0x0002ffff);
+
+ ret = rt5575_spi_fw_load(spi);
+ if (ret) {
+ dev_err(dev, "Load firmware failure: %d\n", ret);
+ return -ENODEV;
+ }
+
+ regmap_write(rt5575->dsp_regmap, 0x18000000, 0x00000000);
+ regmap_update_bits(rt5575->regmap, RT5575_SW_INT, 1, 1);
+
+ regmap_read_poll_timeout(rt5575->regmap, RT5575_SW_INT, ret, !ret, 100000, 10000000);
+ if (ret) {
+ dev_err(dev, "Run firmware failure: %d\n", ret);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int rt5575_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt5575_priv *rt5575;
+ int ret, val, boot;
+ struct device *dev = &i2c->dev;
+
+ rt5575 = devm_kzalloc(dev, sizeof(struct rt5575_priv), GFP_KERNEL);
+ if (!rt5575)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt5575);
+
+ rt5575->i2c = i2c;
+
+ rt5575->dsp_regmap = devm_regmap_init_i2c(i2c, &rt5575_dsp_regmap);
+ if (IS_ERR(rt5575->dsp_regmap)) {
+ ret = PTR_ERR(rt5575->dsp_regmap);
+ dev_err(dev, "Failed to allocate DSP register map: %d\n", ret);
+ return ret;
+ }
+
+ rt5575->regmap = devm_regmap_init(dev, NULL, i2c, &rt5575_regmap);
+ if (IS_ERR(rt5575->regmap)) {
+ ret = PTR_ERR(rt5575->regmap);
+ dev_err(dev, "Failed to allocate register map: %d\n", ret);
+ return ret;
+ }
+
+ regmap_read(rt5575->regmap, RT5575_ID, &val);
+ if (val != RT5575_DEVICE_ID) {
+ dev_err(dev, "Device with ID register %08x is not rt5575\n", val);
+ return -ENODEV;
+ }
+
+ regmap_read(rt5575->regmap, RT5575_BOOT, &boot);
+ if ((boot & RT5575_BOOT_MASK) == RT5575_BOOT_SPI) {
+ if (!IS_ENABLED(CONFIG_SND_SOC_RT5575_SPI)) {
+ dev_err(dev, "Please enable CONFIG_SND_SOC_RT5575_SPI\n");
+ return -ENODEV;
+ }
+
+ if (rt5575_fw_load_by_spi(rt5575))
+ return -ENODEV;
+ }
+
+ return devm_snd_soc_register_component(dev, &rt5575_soc_component_dev, rt5575_dai,
+ ARRAY_SIZE(rt5575_dai));
+}
+
+static const struct i2c_device_id rt5575_i2c_id[] = {
+ { "rt5575" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt5575_i2c_id);
+
+static const struct of_device_id rt5575_of_match[] = {
+ { .compatible = "realtek,rt5575" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rt5575_of_match);
+
+static struct i2c_driver rt5575_i2c_driver = {
+ .driver = {
+ .name = "rt5575",
+ .owner = THIS_MODULE,
+ .of_match_table = rt5575_of_match,
+ },
+ .probe = rt5575_i2c_probe,
+ .id_table = rt5575_i2c_id,
+};
+module_i2c_driver(rt5575_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ALC5575 driver");
+MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt5575.h b/sound/soc/codecs/rt5575.h
new file mode 100644
index 000000000000..752a3c8f5aa9
--- /dev/null
+++ b/sound/soc/codecs/rt5575.h
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt5575.h -- ALC5575 ALSA SoC audio driver
+ *
+ * Copyright(c) 2025 Realtek Semiconductor Corp.
+ *
+ */
+
+#ifndef __RT5575_H__
+#define __RT5575_H__
+
+#define RT5575_DEVICE_ID 0x10ec5575
+#define RT5575_DSP_MAPPING 0x18000000
+
+#define RT5575_BOOT 0x8004
+#define RT5575_ID 0x8008
+#define RT5575_ID_1 0x800c
+#define RT5575_MIXL_VOL 0x8a14
+#define RT5575_MIXR_VOL 0x8a18
+#define RT5575_PROMPT_VOL 0x8a84
+#define RT5575_SPK01_VOL 0x8a88
+#define RT5575_SPK23_VOL 0x8a8c
+#define RT5575_MIC1_VOL 0x8a98
+#define RT5575_MIC2_VOL 0x8a9c
+#define RT5575_WNC_CTRL 0x80ec
+#define RT5575_MODE_CTRL 0x80f0
+#define RT5575_I2S_RATE_CTRL 0x80f4
+#define RT5575_SLEEP_CTRL 0x80f8
+#define RT5575_ALG_BYPASS_CTRL 0x80fc
+#define RT5575_PINMUX_CTRL_2 0x81a4
+#define RT5575_GPIO_CTRL_1 0x8208
+#define RT5575_DSP_BUS_CTRL 0x880c
+#define RT5575_SW_INT 0x0018
+#define RT5575_DSP_BOOT_ERR 0x8e14
+#define RT5575_DSP_READY 0x8e24
+#define RT5575_DSP_CMD_ADDR 0x8e28
+#define RT5575_EFUSE_DATA_2 0xc638
+#define RT5575_EFUSE_DATA_3 0xc63c
+#define RT5575_EFUSE_PID 0xc660
+
+#define RT5575_BOOT_MASK 0x3
+#define RT5575_BOOT_SPI 0x0
+
+enum {
+ RT5575_AIF1,
+ RT5575_AIF2,
+ RT5575_AIF3,
+ RT5575_AIF4,
+ RT5575_AIFS,
+};
+
+struct rt5575_priv {
+ struct i2c_client *i2c;
+ struct snd_soc_component *component;
+ struct regmap *dsp_regmap, *regmap;
+};
+
+#endif /* __RT5575_H__ */
diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c
index 4c08c274f50e..db2222e6f2e7 100644
--- a/sound/soc/codecs/rt5640.c
+++ b/sound/soc/codecs/rt5640.c
@@ -2564,7 +2564,7 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component,
rt5640->use_platform_clock = jack_data->use_platform_clock;
ret = request_irq(rt5640->irq, rt5640_irq,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
"rt5640", rt5640);
if (ret) {
dev_warn(component->dev, "Failed to request IRQ %d: %d\n", rt5640->irq, ret);
@@ -2618,7 +2618,7 @@ static void rt5640_enable_hda_jack_detect(
rt5640->jack = jack;
ret = request_irq(rt5640->irq, rt5640_irq,
- IRQF_TRIGGER_RISING | IRQF_ONESHOT, "rt5640", rt5640);
+ IRQF_TRIGGER_RISING, "rt5640", rt5640);
if (ret) {
dev_warn(component->dev, "Failed to request IRQ %d: %d\n", rt5640->irq, ret);
rt5640->jack = NULL;
diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c
index 9af65a38f0ee..23c4bf3da298 100644
--- a/sound/soc/codecs/rt5651.c
+++ b/sound/soc/codecs/rt5651.c
@@ -2262,7 +2262,7 @@ static int rt5651_i2c_probe(struct i2c_client *i2c)
ret = devm_request_irq(&i2c->dev, rt5651->irq, rt5651_irq,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
- | IRQF_ONESHOT | IRQF_NO_AUTOEN, "rt5651", rt5651);
+ | IRQF_NO_AUTOEN, "rt5651", rt5651);
if (ret) {
dev_warn(&i2c->dev, "Failed to request IRQ %d: %d\n",
rt5651->irq, ret);
diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c
index f5957470652c..3590ebd41c27 100644
--- a/sound/soc/codecs/rt5659.c
+++ b/sound/soc/codecs/rt5659.c
@@ -4118,7 +4118,7 @@ static int rt5659_i2c_probe(struct i2c_client *i2c)
rt5659->gpiod_reset = devm_gpiod_get_optional(&i2c->dev, "reset",
GPIOD_OUT_HIGH);
- /* Sleep for 300 ms miniumum */
+ /* Sleep for 300 ms minimum */
msleep(300);
rt5659->regmap = devm_regmap_init_i2c(i2c, &rt5659_regmap);
diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c
index e4d8785e64c1..eee1c98cc4aa 100644
--- a/sound/soc/codecs/rt5663.c
+++ b/sound/soc/codecs/rt5663.c
@@ -3689,8 +3689,8 @@ static int rt5663_i2c_probe(struct i2c_client *i2c)
if (i2c->irq) {
ret = request_irq(i2c->irq, rt5663_irq,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
- | IRQF_ONESHOT, "rt5663", rt5663);
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "rt5663", rt5663);
if (ret) {
dev_err(&i2c->dev, "%s Failed to request IRQ: %d\n",
__func__, ret);
diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c
index c7beccd54b16..38fb3a277e26 100644
--- a/sound/soc/codecs/rt5665.c
+++ b/sound/soc/codecs/rt5665.c
@@ -4688,7 +4688,7 @@ static int rt5665_i2c_probe(struct i2c_client *i2c)
return PTR_ERR(rt5665->gpiod_ldo1_en);
}
- /* Sleep for 300 ms miniumum */
+ /* Sleep for 300 ms minimum */
usleep_range(300000, 350000);
rt5665->regmap = devm_regmap_init_i2c(i2c, &rt5665_regmap);
diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c
index 5fcdb50d5184..c551696ae11a 100644
--- a/sound/soc/codecs/rt5668.c
+++ b/sound/soc/codecs/rt5668.c
@@ -2458,7 +2458,7 @@ static int rt5668_i2c_probe(struct i2c_client *i2c)
return PTR_ERR(rt5668->ldo1_en);
}
- /* Sleep for 300 ms miniumum */
+ /* Sleep for 300 ms minimum */
usleep_range(300000, 350000);
regmap_write(rt5668->regmap, RT5668_I2C_MODE, 0x1);
diff --git a/sound/soc/codecs/rt5682-i2c.c b/sound/soc/codecs/rt5682-i2c.c
index bba987308e15..e556a365adc8 100644
--- a/sound/soc/codecs/rt5682-i2c.c
+++ b/sound/soc/codecs/rt5682-i2c.c
@@ -173,7 +173,7 @@ static int rt5682_i2c_probe(struct i2c_client *i2c)
if (ret)
return ret;
- /* Sleep for 300 ms miniumum */
+ /* Sleep for 300 ms minimum */
usleep_range(300000, 350000);
regmap_write(rt5682->regmap, RT5682_I2C_MODE, 0x1);
diff --git a/sound/soc/codecs/rtq9128.c b/sound/soc/codecs/rtq9128.c
index 391cc03d687f..14a2c0723d33 100644
--- a/sound/soc/codecs/rtq9128.c
+++ b/sound/soc/codecs/rtq9128.c
@@ -40,6 +40,12 @@
#define RTQ9128_REG_EFUSE_DATA 0xE0
#define RTQ9128_REG_VENDOR_ID 0xF9
+#define RTQ9154_REG_CH1_VOL 0x34
+#define RTQ9154_REG_CH2_VOL 0x33
+#define RTQ9154_REG_CH3_VOL 0x32
+#define RTQ9154_REG_CH4_VOL 0x31
+#define RTQ9154_REG_AUTOULQM 0xAD
+
#define RTQ9128_CHSTAT_VAL_MASK GENMASK(1, 0)
#define RTQ9128_DOLEN_MASK GENMASK(7, 6)
#define RTQ9128_TDMSRCIN_MASK GENMASK(5, 4)
@@ -48,6 +54,7 @@
#define RTQ9128_MSMUTE_MASK BIT(0)
#define RTQ9128_DIE_CHECK_MASK GENMASK(4, 0)
#define RTQ9128_VENDOR_ID_MASK GENMASK(19, 8)
+#define RTQ9128_MODEL_ID_MASK GENMASK(7, 4)
#define RTQ9128_SOFT_RESET_VAL 0x80
#define RTQ9128_VENDOR_ID_VAL 0x470
@@ -56,6 +63,15 @@
#define RTQ9128_TKA470B_VAL 0
#define RTQ9128_RTQ9128DH_VAL 0x0F
#define RTQ9128_RTQ9128DL_VAL 0x10
+#define RTQ9154_MODEL_ID 0x08
+
+#define RTQ9154_AUTOULQM_VAL 0x82
+
+enum rtq9128_chip_model {
+ CHIP_MODEL_RTQ9128 = 0,
+ CHIP_MODEL_RTQ9154,
+ CHIP_MODEL_MAX
+};
struct rtq9128_data {
struct gpio_desc *enable;
@@ -63,6 +79,7 @@ struct rtq9128_data {
int tdm_slots;
int tdm_slot_width;
bool tdm_input_data2_select;
+ enum rtq9128_chip_model chip_model;
};
struct rtq9128_init_reg {
@@ -251,6 +268,28 @@ static const struct soc_enum rtq9128_out4_phase_enum =
SOC_ENUM_SINGLE(RTQ9128_REG_PLLTRI_GEN2, 0, ARRAY_SIZE(phase_select_text),
phase_select_text);
+static const struct soc_enum rtq9154_ch1_si_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_SDI_SEL, 0, ARRAY_SIZE(source_select_text),
+ source_select_text);
+static const struct soc_enum rtq9154_ch2_si_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_SDI_SEL, 2, ARRAY_SIZE(source_select_text),
+ source_select_text);
+static const struct soc_enum rtq9154_ch3_si_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_SDI_SEL, 4, ARRAY_SIZE(source_select_text),
+ source_select_text);
+static const struct soc_enum rtq9154_ch4_si_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_SDI_SEL, 6, ARRAY_SIZE(source_select_text),
+ source_select_text);
+static const struct soc_enum rtq9154_out1_phase_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_PLLTRI_GEN2, 0, ARRAY_SIZE(phase_select_text),
+ phase_select_text);
+static const struct soc_enum rtq9154_out2_phase_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_PLLTRI_GEN2, 4, ARRAY_SIZE(phase_select_text),
+ phase_select_text);
+static const struct soc_enum rtq9154_out3_phase_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_PLLTRI_GEN1, 0, ARRAY_SIZE(phase_select_text),
+ phase_select_text);
+
/*
* In general usage, DVDD could be 1P8V, 3P0V or 3P3V.
* This DVDD undervoltage protection is to prevent from the abnormal power
@@ -283,10 +322,33 @@ static const struct snd_kcontrol_new rtq9128_snd_ctrls[] = {
SOC_ENUM("DVDD UV Threshold Select", rtq9128_dvdduv_select_enum),
};
+static const struct snd_kcontrol_new rtq9154_snd_ctrls[] = {
+ SOC_SINGLE_TLV("MS Volume", RTQ9128_REG_MS_VOL, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_TLV("CH1 Volume", RTQ9154_REG_CH1_VOL, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_TLV("CH2 Volume", RTQ9154_REG_CH2_VOL, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_TLV("CH3 Volume", RTQ9154_REG_CH3_VOL, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_TLV("CH4 Volume", RTQ9154_REG_CH4_VOL, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_TLV("SPK Gain Volume", RTQ9128_REG_MISC, 0, 5, 0, spkgain_tlv),
+ SOC_SINGLE("PBTL12 Switch", RTQ9128_REG_MISC, 4, 1, 0),
+ SOC_SINGLE("PBTL34 Switch", RTQ9128_REG_MISC, 5, 1, 0),
+ SOC_SINGLE("Spread Spectrum Switch", RTQ9128_REG_PWM_SS_OPT, 7, 1, 0),
+ SOC_SINGLE("SDO Select", RTQ9128_REG_SDO_SEL, 0, 15, 0),
+ SOC_ENUM("CH1 SI Select", rtq9154_ch1_si_enum),
+ SOC_ENUM("CH2 SI Select", rtq9154_ch2_si_enum),
+ SOC_ENUM("CH3 SI Select", rtq9154_ch3_si_enum),
+ SOC_ENUM("CH4 SI Select", rtq9154_ch4_si_enum),
+ SOC_ENUM("PWM FREQ Select", rtq9128_pwm_freq_enum),
+ SOC_ENUM("OUT1 Phase Select", rtq9154_out1_phase_enum),
+ SOC_ENUM("OUT2 Phase Select", rtq9154_out2_phase_enum),
+ SOC_ENUM("OUT3 Phase Select", rtq9154_out3_phase_enum),
+ SOC_ENUM("DVDD UV Threshold Select", rtq9128_dvdduv_select_enum),
+};
+
static int rtq9128_dac_power_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
int event)
{
struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct rtq9128_data *data = snd_soc_component_get_drvdata(comp);
unsigned int shift, mask;
int ret;
@@ -301,6 +363,10 @@ static int rtq9128_dac_power_event(struct snd_soc_dapm_widget *w, struct snd_kco
else
shift = 0;
+ /* Compared to RTQ9128, RTQ9154 use the reverse order for DACx bitfield location */
+ if (data->chip_model == CHIP_MODEL_RTQ9154)
+ shift = 6 - shift;
+
mask = RTQ9128_CHSTAT_VAL_MASK << shift;
/* Turn channel state to Normal or HiZ */
@@ -352,7 +418,7 @@ static const struct snd_soc_dapm_route rtq9128_dapm_routes[] = {
static const struct rtq9128_init_reg rtq9128_tka470b_tables[] = {
{ 0xA0, 0xEF },
{ 0x0D, 0x00 },
- { 0x03, 0x05 },
+ { 0x03, 0x45 },
{ 0x05, 0x31 },
{ 0x06, 0x23 },
{ 0x70, 0x11 },
@@ -367,7 +433,7 @@ static const struct rtq9128_init_reg rtq9128_tka470b_tables[] = {
static const struct rtq9128_init_reg rtq9128_dh_tables[] = {
{ 0x0F, 0x00 },
- { 0x03, 0x0D },
+ { 0x03, 0x4D },
{ 0xB2, 0xFF },
{ 0xB3, 0xFF },
{ 0x30, 0x180 },
@@ -378,7 +444,7 @@ static const struct rtq9128_init_reg rtq9128_dh_tables[] = {
static const struct rtq9128_init_reg rtq9128_dl_tables[] = {
{ 0x0F, 0x00 },
- { 0x03, 0x0D },
+ { 0x03, 0x4D },
{ 0x30, 0x180 },
{ 0x8A, 0x55 },
{ 0x72, 0x00 },
@@ -387,6 +453,7 @@ static const struct rtq9128_init_reg rtq9128_dl_tables[] = {
static int rtq9128_component_probe(struct snd_soc_component *comp)
{
+ struct rtq9128_data *data = snd_soc_component_get_drvdata(comp);
const struct rtq9128_init_reg *table, *curr;
size_t table_size;
unsigned int val;
@@ -421,6 +488,14 @@ static int rtq9128_component_probe(struct snd_soc_component *comp)
return ret;
}
+
+ if (data->chip_model == CHIP_MODEL_RTQ9154) {
+ /* Enable RTQ9154 Specific AUTO ULQM feature */
+ ret = snd_soc_component_write(comp, RTQ9154_REG_AUTOULQM, RTQ9154_AUTOULQM_VAL);
+ if (ret < 0)
+ return ret;
+ }
+
pm_runtime_mark_last_busy(comp->dev);
pm_runtime_put(comp->dev);
@@ -439,6 +514,18 @@ static const struct snd_soc_component_driver rtq9128_comp_driver = {
.endianness = 1,
};
+static const struct snd_soc_component_driver rtq9154_comp_driver = {
+ .probe = rtq9128_component_probe,
+ .controls = rtq9154_snd_ctrls,
+ .num_controls = ARRAY_SIZE(rtq9154_snd_ctrls),
+ .dapm_widgets = rtq9128_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rtq9128_dapm_widgets),
+ .dapm_routes = rtq9128_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rtq9128_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
static int rtq9128_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct rtq9128_data *data = snd_soc_dai_get_drvdata(dai);
@@ -679,7 +766,8 @@ static int rtq9128_probe(struct i2c_client *i2c)
struct device *dev = &i2c->dev;
struct rtq9128_data *data;
struct regmap *regmap;
- unsigned int venid;
+ unsigned int veninfo, venid, chip_model;
+ const struct snd_soc_component_driver *comp_drv;
int ret;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
@@ -712,21 +800,33 @@ static int rtq9128_probe(struct i2c_client *i2c)
if (IS_ERR(regmap))
return dev_err_probe(dev, PTR_ERR(regmap), "Failed to init regmap\n");
- ret = regmap_read(regmap, RTQ9128_REG_VENDOR_ID, &venid);
+ ret = regmap_read(regmap, RTQ9128_REG_VENDOR_ID, &veninfo);
if (ret)
return dev_err_probe(dev, ret, "Failed to get vendor id\n");
- venid = FIELD_GET(RTQ9128_VENDOR_ID_MASK, venid);
+ venid = FIELD_GET(RTQ9128_VENDOR_ID_MASK, veninfo);
if (venid != RTQ9128_VENDOR_ID_VAL)
return dev_err_probe(dev, -ENODEV, "Vendor ID not match (0x%x)\n", venid);
+ chip_model = FIELD_GET(RTQ9128_MODEL_ID_MASK, veninfo);
+ switch (chip_model) {
+ case RTQ9154_MODEL_ID:
+ data->chip_model = CHIP_MODEL_RTQ9154;
+ comp_drv = &rtq9154_comp_driver;
+ break;
+ default:
+ data->chip_model = CHIP_MODEL_RTQ9128;
+ comp_drv = &rtq9128_comp_driver;
+ break;
+ }
+
pm_runtime_set_active(dev);
pm_runtime_mark_last_busy(dev);
ret = devm_pm_runtime_enable(dev);
if (ret)
return dev_err_probe(dev, ret, "Failed to enable pm runtime\n");
- return devm_snd_soc_register_component(dev, &rtq9128_comp_driver, &rtq9128_dai, 1);
+ return devm_snd_soc_register_component(dev, comp_drv, &rtq9128_dai, 1);
}
static int rtq9128_pm_runtime_suspend(struct device *dev)
diff --git a/sound/soc/codecs/sdw-mockup.c b/sound/soc/codecs/sdw-mockup.c
index 574c08b14f0c..b7e6546f1b5a 100644
--- a/sound/soc/codecs/sdw-mockup.c
+++ b/sound/soc/codecs/sdw-mockup.c
@@ -237,11 +237,6 @@ static int sdw_mockup_sdw_probe(struct sdw_slave *slave,
return ret;
}
-static int sdw_mockup_sdw_remove(struct sdw_slave *slave)
-{
- return 0;
-}
-
/*
* Intel reserved parts ID with the following mapping expected:
* 0xAAAA: generic full-duplex codec
@@ -264,7 +259,6 @@ static struct sdw_driver sdw_mockup_sdw_driver = {
.name = "sdw-mockup",
},
.probe = sdw_mockup_sdw_probe,
- .remove = sdw_mockup_sdw_remove,
.ops = &sdw_mockup_slave_ops,
.id_table = sdw_mockup_id,
};
diff --git a/sound/soc/codecs/tas2780.c b/sound/soc/codecs/tas2780.c
index a1963415c931..cf3f6abd7e7b 100644
--- a/sound/soc/codecs/tas2780.c
+++ b/sound/soc/codecs/tas2780.c
@@ -319,25 +319,22 @@ static int tas2780_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
goto err;
}
- if (((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S)
- || ((fmt & SND_SOC_DAIFMT_FORMAT_MASK)
- == SND_SOC_DAIFMT_DSP_A)){
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_DSP_A:
iface = TAS2780_TDM_CFG2_SCFG_I2S;
tdm_rx_start_slot = 1;
- } else {
- if (((fmt & SND_SOC_DAIFMT_FORMAT_MASK)
- == SND_SOC_DAIFMT_DSP_B)
- || ((fmt & SND_SOC_DAIFMT_FORMAT_MASK)
- == SND_SOC_DAIFMT_LEFT_J)) {
- iface = TAS2780_TDM_CFG2_SCFG_LEFT_J;
- tdm_rx_start_slot = 0;
- } else {
- dev_err(tas2780->dev,
- "%s:DAI Format is not found, fmt=0x%x\n",
- __func__, fmt);
- ret = -EINVAL;
- goto err;
- }
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_DSP_B:
+ iface = TAS2780_TDM_CFG2_SCFG_LEFT_J;
+ tdm_rx_start_slot = 0;
+ break;
+ default:
+ dev_err(tas2780->dev,
+ "%s:DAI Format is not found, fmt=0x%x\n", __func__, fmt);
+ ret = -EINVAL;
+ goto err;
}
ret = snd_soc_component_update_bits(component, TAS2780_TDM_CFG1,
TAS2780_TDM_CFG1_MASK,
diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c
index 78fd0a5dc6f2..0e084c3a162d 100644
--- a/sound/soc/codecs/tas2781-fmwlib.c
+++ b/sound/soc/codecs/tas2781-fmwlib.c
@@ -2,7 +2,7 @@
//
// tas2781-fmwlib.c -- TASDEVICE firmware support
//
-// Copyright 2023 - 2025 Texas Instruments, Inc.
+// Copyright 2023 - 2026 Texas Instruments, Inc.
//
// Author: Shenghao Ding <shenghao-ding@ti.com>
// Author: Baojun Xu <baojun.xu@ti.com>
@@ -80,6 +80,14 @@
#define POST_SOFTWARE_RESET_DEVICE_C 0x47
#define POST_SOFTWARE_RESET_DEVICE_D 0x48
+#define COPY_CAL_DATA(i) \
+ do { \
+ calbin_data[i + 1] = data[7]; \
+ calbin_data[i + 2] = data[8]; \
+ calbin_data[i + 3] = data[9]; \
+ calbin_data[i + 4] = data[10]; \
+ } while (0)
+
struct tas_crc {
unsigned char offset;
unsigned char len;
@@ -1952,23 +1960,6 @@ static int dspfw_default_callback(struct tasdevice_priv *tas_priv,
return rc;
}
-static int load_calib_data(struct tasdevice_priv *tas_priv,
- struct tasdevice_data *dev_data)
-{
- struct tasdev_blk *block;
- unsigned int i;
- int ret = 0;
-
- for (i = 0; i < dev_data->nr_blk; i++) {
- block = &(dev_data->dev_blks[i]);
- ret = tasdevice_load_block(tas_priv, block);
- if (ret < 0)
- break;
- }
-
- return ret;
-}
-
static int fw_parse_header(struct tasdevice_priv *tas_priv,
struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
{
@@ -2029,6 +2020,103 @@ out:
return offset;
}
+static inline int check_cal_bin_data(struct device *dev,
+ const unsigned char *data, const char *name)
+{
+ if (data[2] != 0x85 || data[1] != 4) {
+ dev_err(dev, "Invalid cal bin file in %s\n", name);
+ return -1;
+ }
+ return 0;
+}
+
+static void calbin_conversion(struct tasdevice_priv *priv,
+ struct tasdevice_fw *tas_fmw)
+{
+ struct calidata *cali_data = &priv->cali_data;
+ unsigned char *calbin_data = cali_data->data;
+ struct cali_reg *p = &cali_data->cali_reg_array;
+ struct tasdevice_calibration *calibration;
+ struct tasdevice_data *img_data;
+ struct tasdev_blk *blk;
+ unsigned char *data;
+ int chn, k;
+
+ if (cali_data->total_sz != priv->ndev *
+ (cali_data->cali_dat_sz_per_dev + 1)) {
+ dev_err(priv->dev, "%s: cali_data size err\n",
+ __func__);
+ return;
+ }
+ calibration = &(tas_fmw->calibrations[0]);
+ img_data = &(calibration->dev_data);
+
+ if (img_data->nr_blk != 1) {
+ dev_err(priv->dev, "%s: Invalid nr_blk, wrong cal bin\n",
+ __func__);
+ return;
+ }
+
+ blk = &(img_data->dev_blks[0]);
+ if (blk->nr_cmds != 15) {
+ dev_err(priv->dev, "%s: Invalid nr_cmds, wrong cal bin\n",
+ __func__);
+ return;
+ }
+
+ switch (blk->type) {
+ case COEFF_DEVICE_A:
+ chn = 0;
+ break;
+ case COEFF_DEVICE_B:
+ chn = 1;
+ break;
+ case COEFF_DEVICE_C:
+ chn = 2;
+ break;
+ case COEFF_DEVICE_D:
+ chn = 3;
+ break;
+ default:
+ dev_err(priv->dev, "%s: Other Type = 0x%02x\n",
+ __func__, blk->type);
+ return;
+ }
+ k = chn * (cali_data->cali_dat_sz_per_dev + 1);
+
+ data = blk->data;
+ if (check_cal_bin_data(priv->dev, data, "r0_reg") < 0)
+ return;
+ p->r0_reg = TASDEVICE_REG(data[4], data[5], data[6]);
+ COPY_CAL_DATA(k);
+
+ data = blk->data + 12;
+ if (check_cal_bin_data(priv->dev, data, "r0_low_reg") < 0)
+ return;
+ p->r0_low_reg = TASDEVICE_REG(data[4], data[5], data[6]);
+ COPY_CAL_DATA(k + 4);
+
+ data = blk->data + 24;
+ if (check_cal_bin_data(priv->dev, data, "invr0_reg") < 0)
+ return;
+ p->invr0_reg = TASDEVICE_REG(data[4], data[5], data[6]);
+ COPY_CAL_DATA(k + 8);
+
+ data = blk->data + 36;
+ if (check_cal_bin_data(priv->dev, data, "pow_reg") < 0)
+ return;
+ p->pow_reg = TASDEVICE_REG(data[4], data[5], data[6]);
+ COPY_CAL_DATA(k + 12);
+
+ data = blk->data + 48;
+ if (check_cal_bin_data(priv->dev, data, "tlimit_reg") < 0)
+ return;
+ p->tlimit_reg = TASDEVICE_REG(data[4], data[5], data[6]);
+ COPY_CAL_DATA(k + 16);
+
+ calbin_data[k] = chn;
+}
+
/* When calibrated data parsing error occurs, DSP can still work with default
* calibrated data, memory resource related to calibrated data will be
* released in the tasdevice_codec_remove.
@@ -2086,6 +2174,7 @@ static int fw_parse_calibration_data(struct tasdevice_priv *tas_priv,
goto out;
}
+ calbin_conversion(tas_priv, tas_fmw);
out:
return offset;
}
@@ -2371,25 +2460,12 @@ static int tasdevice_load_data(struct tasdevice_priv *tas_priv,
static void tasdev_load_calibrated_data(struct tasdevice_priv *priv, int i)
{
- struct tasdevice_fw *cal_fmw = priv->tasdevice[i].cali_data_fmw;
struct calidata *cali_data = &priv->cali_data;
struct cali_reg *p = &cali_data->cali_reg_array;
unsigned char *data = cali_data->data;
- struct tasdevice_calibration *cal;
int k = i * (cali_data->cali_dat_sz_per_dev + 1);
int rc;
- /* Load the calibrated data from cal bin file */
- if (!priv->is_user_space_calidata && cal_fmw) {
- cal = cal_fmw->calibrations;
-
- if (cal)
- load_calib_data(priv, &cal->dev_data);
- return;
- }
- if (!priv->is_user_space_calidata)
- return;
- /* load calibrated data from user space */
if (data[k] != i) {
dev_err(priv->dev, "%s: no cal-data for dev %d from usr-spc\n",
__func__, i);
diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c
index d1c76ab0144d..41b89fcc69c3 100644
--- a/sound/soc/codecs/tas2781-i2c.c
+++ b/sound/soc/codecs/tas2781-i2c.c
@@ -2,7 +2,7 @@
//
// ALSA SoC Texas Instruments TAS2563/TAS2781 Audio Smart Amplifier
//
-// Copyright (C) 2022 - 2025 Texas Instruments Incorporated
+// Copyright (C) 2022 - 2026 Texas Instruments Incorporated
// https://www.ti.com
//
// The TAS2563/TAS2781 driver implements a flexible and configurable
@@ -255,8 +255,6 @@ static int tasdev_cali_data_get(struct snd_kcontrol *kcontrol,
int rc;
guard(mutex)(&priv->codec_lock);
- if (!priv->is_user_space_calidata)
- return -1;
if (!p->r0_reg)
return -1;
@@ -654,7 +652,6 @@ static int tasdev_cali_data_put(struct snd_kcontrol *kcontrol,
}
}
i += 2;
- priv->is_user_space_calidata = true;
if (priv->dspbin_typ == TASDEV_BASIC) {
p->r0_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]);
@@ -1444,7 +1441,11 @@ static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv)
GFP_KERNEL);
if (!cali_data->data)
return -ENOMEM;
-
+ /*
+ * Set to an invalid value before the calibrated data is stored into
+ * it, for the default value is 0, which means the first device.
+ */
+ cali_data->data[0] = 0xff;
if (priv->chip_id == TAS2781) {
struct soc_bytes_ext *ext_cali_start;
char *cali_start_name;
diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c
index 43b779873b93..3c1fbf523529 100644
--- a/sound/soc/codecs/tas2783-sdw.c
+++ b/sound/soc/codecs/tas2783-sdw.c
@@ -27,16 +27,21 @@
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_registers.h>
#include <linux/soundwire/sdw_type.h>
+#if IS_ENABLED(CONFIG_PCI)
+#include <linux/pci.h>
+#endif
#include <sound/sdw.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include <sound/tas2781-tlv.h>
+#include <sound/sdca_function.h>
+#include <sound/sdca_regmap.h>
#include "tas2783.h"
#define TIMEOUT_FW_DL_MS (3000)
-#define FW_DL_OFFSET 36
-#define FW_FL_HDR 12
+#define FW_DL_OFFSET 84 /* binary file information */
+#define FW_FL_HDR 20 /* minimum number of bytes in one chunk */
#define TAS2783_PROBE_TIMEOUT 5000
#define TAS2783_CALI_GUID EFI_GUID(0x1f52d2a1, 0xbb3a, 0x457d, 0xbc, \
0x09, 0x43, 0xa3, 0xf4, 0x31, 0x0a, 0x92)
@@ -49,11 +54,22 @@ static const u32 tas2783_cali_reg[] = {
TAS2783_CAL_TLIM,
};
-struct bin_header_t {
- u16 vendor_id;
- u16 version;
+struct tas_fw_hdr {
+ u32 size;
+ u32 version_offset;
+ u32 plt_id;
+ u32 ppc3_ver;
+ u32 timestamp;
+ u8 ddc_name[64];
+};
+
+struct tas_fw_file {
+ u32 vendor_id;
u32 file_id;
+ u32 version;
u32 length;
+ u32 dest_addr;
+ u8 *fw_data;
};
struct calibration_data {
@@ -66,6 +82,7 @@ struct tas2783_prv {
struct snd_soc_component *component;
struct calibration_data cali_data;
struct sdw_slave *sdw_peripheral;
+ struct sdca_function_data *sa_func_data;
enum sdw_slave_status status;
/* calibration */
struct mutex calib_lock;
@@ -286,7 +303,7 @@ static const struct reg_default tas2783_reg_default[] = {
};
static const struct reg_sequence tas2783_init_seq[] = {
- REG_SEQ0(SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x10, 0x00), 0x04),
+ REG_SEQ0(SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x10, 0x00), 0x01),
REG_SEQ0(0x00800418, 0x00),
REG_SEQ0(0x00800419, 0x00),
REG_SEQ0(0x0080041a, 0x00),
@@ -296,60 +313,19 @@ static const struct reg_sequence tas2783_init_seq[] = {
REG_SEQ0(0x0080042a, 0x00),
REG_SEQ0(0x0080042b, 0x00),
REG_SEQ0(SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x1, 0x00), 0x00),
- REG_SEQ0(0x0080005c, 0xD9),
- REG_SEQ0(0x00800082, 0x20),
- REG_SEQ0(0x008000a1, 0x00),
- REG_SEQ0(0x00800097, 0xc8),
- REG_SEQ0(0x00800099, 0x20),
- REG_SEQ0(0x008000c7, 0xaa),
- REG_SEQ0(0x008000b5, 0x74),
- REG_SEQ0(0x00800082, 0x20),
- REG_SEQ0(0x00807e8d, 0x0d),
- REG_SEQ0(0x00807eb9, 0x53),
- REG_SEQ0(0x00807ebe, 0x42),
- REG_SEQ0(0x00807ec5, 0x37),
- REG_SEQ0(0x00800066, 0x92),
- REG_SEQ0(0x00800003, 0x28),
REG_SEQ0(0x00800004, 0x21),
REG_SEQ0(0x00800005, 0x41),
REG_SEQ0(0x00800006, 0x00),
REG_SEQ0(0x00800007, 0x20),
- REG_SEQ0(0x0080000c, 0x10),
- REG_SEQ0(0x00800013, 0x08),
REG_SEQ0(0x00800015, 0x00),
- REG_SEQ0(0x00800017, 0x80),
- REG_SEQ0(0x0080001a, 0x00),
- REG_SEQ0(0x0080001b, 0x22),
- REG_SEQ0(0x0080001c, 0x36),
- REG_SEQ0(0x0080001d, 0x01),
- REG_SEQ0(0x0080001f, 0x00),
- REG_SEQ0(0x00800020, 0x2e),
- REG_SEQ0(0x00800034, 0x06),
- REG_SEQ0(0x00800035, 0xb9),
REG_SEQ0(0x00800036, 0xad),
REG_SEQ0(0x00800037, 0xa8),
- REG_SEQ0(0x00800038, 0x00),
- REG_SEQ0(0x0080003b, 0xfc),
- REG_SEQ0(0x0080003d, 0xdd),
- REG_SEQ0(0x00800040, 0xf6),
- REG_SEQ0(0x00800041, 0x14),
- REG_SEQ0(0x0080005c, 0x19),
- REG_SEQ0(0x0080005d, 0x80),
- REG_SEQ0(0x00800063, 0x48),
- REG_SEQ0(0x00800065, 0x08),
- REG_SEQ0(0x00800067, 0x00),
- REG_SEQ0(0x0080006a, 0x12),
REG_SEQ0(0x0080006b, 0x7b),
REG_SEQ0(0x0080006c, 0x00),
REG_SEQ0(0x0080006d, 0x00),
REG_SEQ0(0x0080006e, 0x1a),
REG_SEQ0(0x0080006f, 0x00),
- REG_SEQ0(0x00800070, 0x96),
REG_SEQ0(0x00800071, 0x02),
- REG_SEQ0(0x00800073, 0x08),
- REG_SEQ0(0x00800075, 0xe0),
- REG_SEQ0(0x0080007a, 0x60),
- REG_SEQ0(0x008000bd, 0x00),
REG_SEQ0(0x008000be, 0x00),
REG_SEQ0(0x008000bf, 0x00),
REG_SEQ0(0x008000c0, 0x00),
@@ -357,17 +333,6 @@ static const struct reg_sequence tas2783_init_seq[] = {
REG_SEQ0(0x008000c2, 0x00),
REG_SEQ0(0x008000c3, 0x00),
REG_SEQ0(0x008000c4, 0x00),
- REG_SEQ0(0x008000c5, 0x00),
- REG_SEQ0(0x00800008, 0x49),
- REG_SEQ0(0x00800009, 0x02),
- REG_SEQ0(0x0080000a, 0x1a),
- REG_SEQ0(0x0080000d, 0x93),
- REG_SEQ0(0x0080000e, 0x82),
- REG_SEQ0(0x0080000f, 0x42),
- REG_SEQ0(0x00800010, 0x84),
- REG_SEQ0(0x00800014, 0x0a),
- REG_SEQ0(0x00800016, 0x00),
- REG_SEQ0(0x00800060, 0x21),
};
static int tas2783_sdca_mbq_size(struct device *dev, u32 reg)
@@ -676,7 +641,8 @@ static void tas2783_set_calib_params_to_device(struct tas2783_prv *tas_dev, u32
}
if (device_num == dev_count)
- dev_err(tas_dev->dev, "device not found\n");
+ dev_err(tas_dev->dev,
+ "unique id not found in the calib data\n");
else
dev_dbg(tas_dev->dev, "calib data update done\n");
}
@@ -735,13 +701,28 @@ static s32 tas2783_update_calibdata(struct tas2783_prv *tas_dev)
return ret;
}
-static s32 read_header(const u8 *data, struct bin_header_t *hdr)
+static s32 tas_fw_read_hdr(const u8 *data, struct tas_fw_hdr *hdr)
{
- hdr->vendor_id = get_unaligned_le16(&data[0]);
- hdr->file_id = get_unaligned_le32(&data[2]);
- hdr->version = get_unaligned_le16(&data[6]);
- hdr->length = get_unaligned_le32(&data[8]);
- return 12;
+ hdr->size = get_unaligned_le32(data);
+ hdr->version_offset = get_unaligned_le32(&data[4]);
+ hdr->plt_id = get_unaligned_le32(&data[8]);
+ hdr->ppc3_ver = get_unaligned_le32(&data[12]);
+ memcpy(hdr->ddc_name, &data[16], 64);
+ hdr->timestamp = get_unaligned_le32(&data[80]);
+
+ return 84;
+}
+
+static s32 tas_fw_get_next_file(const u8 *data, struct tas_fw_file *file)
+{
+ file->vendor_id = get_unaligned_le32(&data[0]);
+ file->file_id = get_unaligned_le32(&data[4]);
+ file->version = get_unaligned_le32(&data[8]);
+ file->length = get_unaligned_le32(&data[12]);
+ file->dest_addr = get_unaligned_le32(&data[16]);
+ file->fw_data = (u8 *)&data[20];
+
+ return file->length + sizeof(u32) * 5;
}
static void tas2783_fw_ready(const struct firmware *fmw, void *context)
@@ -749,13 +730,20 @@ static void tas2783_fw_ready(const struct firmware *fmw, void *context)
struct tas2783_prv *tas_dev =
(struct tas2783_prv *)context;
const u8 *buf = NULL;
- s32 offset = 0, img_sz, file_blk_size, ret;
- struct bin_header_t hdr;
+ s32 img_sz, ret = 0, cur_file = 0;
+ s32 offset = 0;
+
+ struct tas_fw_hdr *hdr __free(kfree) = kzalloc(sizeof(*hdr), GFP_KERNEL);
+ struct tas_fw_file *file __free(kfree) = kzalloc(sizeof(*file), GFP_KERNEL);
+ if (!file || !hdr) {
+ ret = -ENOMEM;
+ goto out;
+ }
if (!fmw || !fmw->data) {
- /* No firmware binary, devices will work in ROM mode. */
+ /* firmware binary not found*/
dev_err(tas_dev->dev,
- "Failed to read %s, no side-effect on driver running\n",
+ "Failed to read fw binary %s\n",
tas_dev->rca_binaryname);
ret = -EINVAL;
goto out;
@@ -763,67 +751,47 @@ static void tas2783_fw_ready(const struct firmware *fmw, void *context)
img_sz = fmw->size;
buf = fmw->data;
- offset += FW_DL_OFFSET;
- if (offset >= (img_sz - FW_FL_HDR)) {
- dev_err(tas_dev->dev,
- "firmware is too small");
+ offset += tas_fw_read_hdr(buf, hdr);
+ if (hdr->size != img_sz) {
+ ret = -EINVAL;
+ dev_err(tas_dev->dev, "firmware size mismatch with header");
+ goto out;
+ }
+
+ if (img_sz < FW_DL_OFFSET) {
ret = -EINVAL;
+ dev_err(tas_dev->dev, "unexpected size, size is too small");
goto out;
}
mutex_lock(&tas_dev->pde_lock);
while (offset < (img_sz - FW_FL_HDR)) {
- memset(&hdr, 0, sizeof(hdr));
- offset += read_header(&buf[offset], &hdr);
+ offset += tas_fw_get_next_file(&buf[offset], file);
dev_dbg(tas_dev->dev,
- "vndr=%d, file=%d, version=%d, len=%d, off=%d\n",
- hdr.vendor_id, hdr.file_id, hdr.version,
- hdr.length, offset);
- /* size also includes the header */
- file_blk_size = hdr.length - FW_FL_HDR;
-
- /* make sure that enough data is there */
- if (offset + file_blk_size > img_sz) {
- ret = -EINVAL;
+ "v=%d, fid=%d, ver=%d, len=%d, daddr=0x%x, fw=%p",
+ file->vendor_id, file->file_id,
+ file->version, file->length,
+ file->dest_addr, file->fw_data);
+
+ ret = sdw_nwrite_no_pm(tas_dev->sdw_peripheral,
+ file->dest_addr,
+ file->length,
+ file->fw_data);
+ if (ret < 0) {
dev_err(tas_dev->dev,
- "corrupt firmware file");
- break;
- }
-
- switch (hdr.file_id) {
- case 0:
- ret = sdw_nwrite_no_pm(tas_dev->sdw_peripheral,
- PRAM_ADDR_START, file_blk_size,
- &buf[offset]);
- if (ret < 0)
- dev_err(tas_dev->dev,
- "PRAM update failed: %d", ret);
- break;
-
- case 1:
- ret = sdw_nwrite_no_pm(tas_dev->sdw_peripheral,
- YRAM_ADDR_START, file_blk_size,
- &buf[offset]);
- if (ret < 0)
- dev_err(tas_dev->dev,
- "YRAM update failed: %d", ret);
-
- break;
-
- default:
- ret = -EINVAL;
- dev_err(tas_dev->dev, "Unsupported file");
+ "FW download failed: %d", ret);
break;
}
-
- if (ret == 0)
- offset += file_blk_size;
- else
- break;
+ cur_file++;
}
mutex_unlock(&tas_dev->pde_lock);
- if (!ret)
+
+ if (cur_file == 0) {
+ dev_err(tas_dev->dev, "fw with no files");
+ ret = -EINVAL;
+ } else {
tas2783_update_calibdata(tas_dev);
+ }
out:
if (!ret)
@@ -1094,66 +1062,6 @@ static s32 tas_init(struct tas2783_prv *tas_dev)
return ret;
}
-static s32 tas_read_prop(struct sdw_slave *slave)
-{
- struct sdw_slave_prop *prop = &slave->prop;
- s32 nval;
- s32 i, j;
- u32 bit;
- unsigned long addr;
- struct sdw_dpn_prop *dpn;
-
- prop->scp_int1_mask =
- SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
- prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
-
- prop->paging_support = true;
-
- /* first we need to allocate memory for set bits in port lists */
- prop->source_ports = 0x04; /* BITMAP: 00000100 */
- prop->sink_ports = 0x2; /* BITMAP: 00000010 */
-
- nval = hweight32(prop->source_ports);
- prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
- sizeof(*prop->src_dpn_prop), GFP_KERNEL);
- if (!prop->src_dpn_prop)
- return -ENOMEM;
-
- i = 0;
- dpn = prop->src_dpn_prop;
- addr = prop->source_ports;
- for_each_set_bit(bit, &addr, 32) {
- dpn[i].num = bit;
- dpn[i].type = SDW_DPN_FULL;
- dpn[i].simple_ch_prep_sm = false;
- dpn[i].ch_prep_timeout = 10;
- i++;
- }
-
- /* do this again for sink now */
- nval = hweight32(prop->sink_ports);
- prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
- sizeof(*prop->sink_dpn_prop), GFP_KERNEL);
- if (!prop->sink_dpn_prop)
- return -ENOMEM;
-
- j = 0;
- dpn = prop->sink_dpn_prop;
- addr = prop->sink_ports;
- for_each_set_bit(bit, &addr, 32) {
- dpn[j].num = bit;
- dpn[j].type = SDW_DPN_FULL;
- dpn[j].simple_ch_prep_sm = false;
- dpn[j].ch_prep_timeout = 10;
- j++;
- }
-
- /* set the timeout values */
- prop->clk_stop_timeout = 200;
-
- return 0;
-}
-
static s32 tas2783_sdca_dev_suspend(struct device *dev)
{
struct tas2783_prv *tas_dev = dev_get_drvdata(dev);
@@ -1200,6 +1108,31 @@ static const struct dev_pm_ops tas2783_sdca_pm = {
RUNTIME_PM_OPS(tas2783_sdca_dev_suspend, tas2783_sdca_dev_resume, NULL)
};
+static void tas_generate_fw_name(struct sdw_slave *slave, char *name, size_t size)
+{
+ struct sdw_bus *bus = slave->bus;
+ u8 unique_id = slave->id.unique_id;
+ bool pci_found = false;
+#if IS_ENABLED(CONFIG_PCI)
+ struct device *dev = &slave->dev;
+ struct pci_dev *pci = NULL;
+
+ for (; dev; dev = dev->parent) {
+ if (dev->bus == &pci_bus_type) {
+ pci = to_pci_dev(dev);
+ scnprintf(name, size, "%04X-%1X-%1X.bin",
+ pci->subsystem_device, bus->link_id, unique_id);
+ pci_found = true;
+ break;
+ }
+ }
+#endif
+
+ if (!pci_found)
+ scnprintf(name, size, "tas2783-%1X-%1X.bin",
+ bus->link_id, unique_id);
+}
+
static s32 tas_io_init(struct device *dev, struct sdw_slave *slave)
{
struct tas2783_prv *tas_dev = dev_get_drvdata(dev);
@@ -1211,8 +1144,16 @@ static s32 tas_io_init(struct device *dev, struct sdw_slave *slave)
tas_dev->fw_dl_task_done = false;
tas_dev->fw_dl_success = false;
- scnprintf(tas_dev->rca_binaryname, sizeof(tas_dev->rca_binaryname),
- "tas2783-%01x.bin", unique_id);
+
+ ret = regmap_write(tas_dev->regmap, TAS2783_SW_RESET, 0x1);
+ if (ret) {
+ dev_err(dev, "sw reset failed, err=%d", ret);
+ return ret;
+ }
+ usleep_range(2000, 2200);
+
+ tas_generate_fw_name(slave, tas_dev->rca_binaryname,
+ sizeof(tas_dev->rca_binaryname));
ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,
tas_dev->rca_binaryname, tas_dev->dev,
@@ -1230,9 +1171,18 @@ static s32 tas_io_init(struct device *dev, struct sdw_slave *slave)
dev_err(tas_dev->dev, "fw request, wait_event timeout\n");
ret = -EAGAIN;
} else {
- ret = regmap_multi_reg_write(tas_dev->regmap, tas2783_init_seq,
- ARRAY_SIZE(tas2783_init_seq));
- tas_dev->hw_init = true;
+ if (tas_dev->sa_func_data)
+ ret = sdca_regmap_write_init(dev, tas_dev->regmap,
+ tas_dev->sa_func_data);
+ else
+ ret = regmap_multi_reg_write(tas_dev->regmap, tas2783_init_seq,
+ ARRAY_SIZE(tas2783_init_seq));
+
+ if (ret)
+ dev_err(tas_dev->dev,
+ "init writes failed, err=%d", ret);
+ else
+ tas_dev->hw_init = true;
}
return ret;
@@ -1267,7 +1217,6 @@ static s32 tas_update_status(struct sdw_slave *slave,
}
static const struct sdw_slave_ops tas_sdw_ops = {
- .read_prop = tas_read_prop,
.update_status = tas_update_status,
};
@@ -1282,12 +1231,52 @@ static s32 tas_sdw_probe(struct sdw_slave *peripheral,
struct regmap *regmap;
struct device *dev = &peripheral->dev;
struct tas2783_prv *tas_dev;
+ struct sdca_function_data *function_data = NULL;
+ int ret, i;
+
+ ret = sdw_slave_read_prop(peripheral);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "slave property read failed");
tas_dev = devm_kzalloc(dev, sizeof(*tas_dev), GFP_KERNEL);
if (!tas_dev)
return dev_err_probe(dev, -ENOMEM,
"Failed devm_kzalloc");
+ i = -1;
+ /* check if we have any SDCA function data available */
+ if (peripheral->sdca_data.num_functions > 0) {
+ dev_dbg(dev, "SDCA functions found: %d", peripheral->sdca_data.num_functions);
+
+ /* Look for Smart Amp function type */
+ for (i = 0; i < peripheral->sdca_data.num_functions; i++) {
+ if (peripheral->sdca_data.function[i].type ==
+ SDCA_FUNCTION_TYPE_SMART_AMP) {
+ dev_info(dev, "Found Smart Amp function at index %d", i);
+ break;
+ }
+ }
+ }
+
+ if (i >= 0 && i < peripheral->sdca_data.num_functions) {
+ /* Allocate memory for function data */
+ function_data = devm_kzalloc(dev, sizeof(*function_data),
+ GFP_KERNEL);
+ if (!function_data)
+ return dev_err_probe(dev, -ENOMEM,
+ "failed to parse sdca functions");
+
+ /* Parse the function */
+ ret = sdca_parse_function(dev, peripheral,
+ &peripheral->sdca_data.function[i],
+ function_data);
+ if (!ret)
+ tas_dev->sa_func_data = function_data;
+ else
+ dev_warn(dev, "smartamp function parse failed:err%d, using defaults", ret);
+ }
+
tas_dev->dev = dev;
tas_dev->sdw_peripheral = peripheral;
tas_dev->hw_init = false;
@@ -1342,6 +1331,7 @@ static struct sdw_driver tas_sdw_driver = {
};
module_sdw_driver(tas_sdw_driver);
+MODULE_IMPORT_NS("SND_SOC_SDCA");
MODULE_AUTHOR("Texas Instruments Inc.");
MODULE_DESCRIPTION("ASoC TAS2783 SoundWire Driver");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tas2783.h b/sound/soc/codecs/tas2783.h
index 794333e0a350..bf34319c9a9f 100644
--- a/sound/soc/codecs/tas2783.h
+++ b/sound/soc/codecs/tas2783.h
@@ -28,6 +28,7 @@
#define TASDEV_REG_SDW(book, page, reg) (((book) * 256 * 128) + \
0x800000 + ((page) * 128) + (reg))
+#define TAS2783_SW_RESET TASDEV_REG_SDW(0x0, 0x00, 0x01)
/* Volume control */
#define TAS2783_DVC_LVL TASDEV_REG_SDW(0x0, 0x00, 0x1A)
#define TAS2783_AMP_LEVEL TASDEV_REG_SDW(0x0, 0x00, 0x03)
diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c
index fdf4a9add852..e4f27a734501 100644
--- a/sound/soc/codecs/tlv320adcx140.c
+++ b/sound/soc/codecs/tlv320adcx140.c
@@ -22,8 +22,16 @@
#include "tlv320adcx140.h"
+static const char *const adcx140_supply_names[] = {
+ "avdd",
+ "iovdd",
+};
+
+#define ADCX140_NUM_SUPPLIES ARRAY_SIZE(adcx140_supply_names)
+
struct adcx140_priv {
struct regulator *supply_areg;
+ struct regulator_bulk_data supplies[ADCX140_NUM_SUPPLIES];
struct gpio_desc *gpio_reset;
struct regmap *regmap;
struct device *dev;
@@ -121,6 +129,34 @@ static const struct reg_default adcx140_reg_defaults[] = {
{ ADCX140_DEV_STS1, 0x80 },
};
+static const struct regmap_range adcx140_wr_ranges[] = {
+ regmap_reg_range(ADCX140_PAGE_SELECT, ADCX140_SLEEP_CFG),
+ regmap_reg_range(ADCX140_SHDN_CFG, ADCX140_SHDN_CFG),
+ regmap_reg_range(ADCX140_ASI_CFG0, ADCX140_ASI_CFG2),
+ regmap_reg_range(ADCX140_ASI_CH1, ADCX140_MST_CFG1),
+ regmap_reg_range(ADCX140_CLK_SRC, ADCX140_CLK_SRC),
+ regmap_reg_range(ADCX140_PDMCLK_CFG, ADCX140_GPO_CFG3),
+ regmap_reg_range(ADCX140_GPO_VAL, ADCX140_GPO_VAL),
+ regmap_reg_range(ADCX140_GPI_CFG0, ADCX140_GPI_CFG1),
+ regmap_reg_range(ADCX140_GPI_MON, ADCX140_GPI_MON),
+ regmap_reg_range(ADCX140_INT_CFG, ADCX140_INT_MASK0),
+ regmap_reg_range(ADCX140_BIAS_CFG, ADCX140_CH4_CFG4),
+ regmap_reg_range(ADCX140_CH5_CFG2, ADCX140_CH5_CFG4),
+ regmap_reg_range(ADCX140_CH6_CFG2, ADCX140_CH6_CFG4),
+ regmap_reg_range(ADCX140_CH7_CFG2, ADCX140_CH7_CFG4),
+ regmap_reg_range(ADCX140_CH8_CFG2, ADCX140_CH8_CFG4),
+ regmap_reg_range(ADCX140_DSP_CFG0, ADCX140_DRE_CFG0),
+ regmap_reg_range(ADCX140_AGC_CFG0, ADCX140_AGC_CFG0),
+ regmap_reg_range(ADCX140_IN_CH_EN, ADCX140_PWR_CFG),
+ regmap_reg_range(ADCX140_PHASE_CALIB, ADCX140_PHASE_CALIB),
+ regmap_reg_range(0x7e, 0x7e),
+};
+
+static const struct regmap_access_table adcx140_wr_table = {
+ .yes_ranges = adcx140_wr_ranges,
+ .n_yes_ranges = ARRAY_SIZE(adcx140_wr_ranges),
+};
+
static const struct regmap_range_cfg adcx140_ranges[] = {
{
.range_min = 0,
@@ -156,6 +192,7 @@ static const struct regmap_config adcx140_i2c_regmap = {
.num_ranges = ARRAY_SIZE(adcx140_ranges),
.max_register = 12 * 128,
.volatile_reg = adcx140_volatile,
+ .wr_table = &adcx140_wr_table,
};
/* Digital Volume control. From -100 to 27 dB in 0.5 dB steps */
@@ -185,6 +222,13 @@ static const struct snd_kcontrol_new decimation_filter_controls[] = {
SOC_DAPM_ENUM("Decimation Filter", decimation_filter_enum),
};
+static const char * const channel_summation_text[] = {
+ "Disabled", "2 Channel", "4 Channel"
+};
+
+static SOC_ENUM_SINGLE_DECL(channel_summation_enum, ADCX140_DSP_CFG0, 2,
+ channel_summation_text);
+
static const char * const pdmclk_text[] = {
"2.8224 MHz", "1.4112 MHz", "705.6 kHz", "5.6448 MHz"
};
@@ -672,6 +716,10 @@ static const struct snd_kcontrol_new adcx140_snd_controls[] = {
SOC_SINGLE_TLV("Digital CH8 Out Volume", ADCX140_CH8_CFG2,
0, 0xff, 0, dig_vol_tlv),
ADCX140_PHASE_CALIB_SWITCH("Phase Calibration Switch"),
+
+ SOC_SINGLE("Biquads Per Channel", ADCX140_DSP_CFG1, 5, 3, 0),
+
+ SOC_ENUM("Channel Summation", channel_summation_enum),
};
static int adcx140_reset(struct adcx140_priv *adcx140)
@@ -1073,19 +1121,91 @@ out:
return ret;
}
+static int adcx140_pwr_off(struct adcx140_priv *adcx140)
+{
+ int ret;
+
+ regcache_cache_only(adcx140->regmap, true);
+ regcache_mark_dirty(adcx140->regmap);
+
+ /* Assert the reset GPIO */
+ gpiod_set_value_cansleep(adcx140->gpio_reset, 0);
+
+ /*
+ * Datasheet - TLV320ADC3140 Rev. B, TLV320ADC5140 Rev. A,
+ * TLV320ADC6140 Rev. A 8.4.1:
+ * wait for hw shutdown (25ms) + >= 1ms
+ */
+ usleep_range(30000, 100000);
+
+ /* Power off the regulators, `avdd` and `iovdd` */
+ ret = regulator_bulk_disable(ARRAY_SIZE(adcx140->supplies),
+ adcx140->supplies);
+ if (ret) {
+ dev_err(adcx140->dev, "Failed to disable supplies: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int adcx140_pwr_on(struct adcx140_priv *adcx140)
+{
+ int ret;
+
+ /* Power on the regulators, `avdd` and `iovdd` */
+ ret = regulator_bulk_enable(ARRAY_SIZE(adcx140->supplies),
+ adcx140->supplies);
+ if (ret) {
+ dev_err(adcx140->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* De-assert the reset GPIO */
+ gpiod_set_value_cansleep(adcx140->gpio_reset, 1);
+
+ /*
+ * Datasheet - TLV320ADC3140 Rev. B, TLV320ADC5140 Rev. A,
+ * TLV320ADC6140 Rev. A 8.4.2:
+ * wait >= 10 ms after entering sleep mode.
+ */
+ usleep_range(10000, 100000);
+
+ regcache_cache_only(adcx140->regmap, false);
+
+ /* Flush the regcache */
+ ret = regcache_sync(adcx140->regmap);
+ if (ret) {
+ dev_err(adcx140->dev, "Failed to restore register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
static int adcx140_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ enum snd_soc_bias_level prev_level = snd_soc_dapm_get_bias_level(dapm);
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
+ if (prev_level == SND_SOC_BIAS_STANDBY)
+ adcx140_pwr_ctrl(adcx140, true);
+ break;
case SND_SOC_BIAS_STANDBY:
- adcx140_pwr_ctrl(adcx140, true);
+ if (prev_level == SND_SOC_BIAS_PREPARE)
+ adcx140_pwr_ctrl(adcx140, false);
+ if (prev_level == SND_SOC_BIAS_OFF)
+ return adcx140_pwr_on(adcx140);
break;
case SND_SOC_BIAS_OFF:
- adcx140_pwr_ctrl(adcx140, false);
+ if (prev_level == SND_SOC_BIAS_STANDBY)
+ return adcx140_pwr_off(adcx140);
break;
}
@@ -1151,6 +1271,16 @@ static int adcx140_i2c_probe(struct i2c_client *i2c)
adcx140->phase_calib_on = false;
adcx140->dev = &i2c->dev;
+ for (int i = 0; i < ADCX140_NUM_SUPPLIES; i++)
+ adcx140->supplies[i].supply = adcx140_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, ADCX140_NUM_SUPPLIES,
+ adcx140->supplies);
+ if (ret) {
+ dev_err_probe(&i2c->dev, ret, "Failed to request supplies\n");
+ return ret;
+ }
+
adcx140->gpio_reset = devm_gpiod_get_optional(adcx140->dev,
"reset", GPIOD_OUT_LOW);
if (IS_ERR(adcx140->gpio_reset))
@@ -1186,6 +1316,8 @@ static int adcx140_i2c_probe(struct i2c_client *i2c)
return ret;
}
+ regcache_cache_only(adcx140->regmap, true);
+
i2c_set_clientdata(i2c, adcx140);
return devm_snd_soc_register_component(&i2c->dev,
diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c
index 26ebcdadeb7d..0c842aaa7eec 100644
--- a/sound/soc/codecs/wcd-mbhc-v2.c
+++ b/sound/soc/codecs/wcd-mbhc-v2.c
@@ -1631,17 +1631,5 @@ void wcd_mbhc_deinit(struct wcd_mbhc *mbhc)
}
EXPORT_SYMBOL(wcd_mbhc_deinit);
-static int __init mbhc_init(void)
-{
- return 0;
-}
-
-static void __exit mbhc_exit(void)
-{
-}
-
-module_init(mbhc_init);
-module_exit(mbhc_exit);
-
MODULE_DESCRIPTION("wcd MBHC v2 module");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wcd939x-sdw.c b/sound/soc/codecs/wcd939x-sdw.c
index da342a0c95a5..399dfba79aa2 100644
--- a/sound/soc/codecs/wcd939x-sdw.c
+++ b/sound/soc/codecs/wcd939x-sdw.c
@@ -1384,12 +1384,7 @@ static int wcd9390_probe(struct sdw_slave *pdev, const struct sdw_device_id *id)
}
if (wcd->is_tx) {
- /*
- * Do not use devres here since devres_release_group() could
- * be called by component_unbind() id the aggregate device
- * fails to bind.
- */
- wcd->regmap = regmap_init_sdw(pdev, &wcd939x_regmap_config);
+ wcd->regmap = devm_regmap_init_sdw(pdev, &wcd939x_regmap_config);
if (IS_ERR(wcd->regmap))
return dev_err_probe(dev, PTR_ERR(wcd->regmap),
"Regmap init failed\n");
@@ -1400,30 +1395,20 @@ static int wcd9390_probe(struct sdw_slave *pdev, const struct sdw_device_id *id)
ret = component_add(dev, &wcd_sdw_component_ops);
if (ret)
- goto err_free_regmap;
+ return ret;
/* Set suspended until aggregate device is bind */
pm_runtime_set_suspended(dev);
return 0;
-
-err_free_regmap:
- if (wcd->regmap)
- regmap_exit(wcd->regmap);
-
- return ret;
}
static int wcd9390_remove(struct sdw_slave *pdev)
{
struct device *dev = &pdev->dev;
- struct wcd939x_sdw_priv *wcd = dev_get_drvdata(dev);
component_del(dev, &wcd_sdw_component_ops);
- if (wcd->regmap)
- regmap_exit(wcd->regmap);
-
return 0;
}
diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c
index 7511c71695c6..6e097d8ed288 100644
--- a/sound/soc/codecs/wm0010.c
+++ b/sound/soc/codecs/wm0010.c
@@ -326,7 +326,7 @@ static void byte_swap_64(u64 *data_in, u64 *data_out, u32 len)
int i;
for (i = 0; i < len / 8; i++)
- data_out[i] = cpu_to_be64(le64_to_cpu(data_in[i]));
+ data_out[i] = swab64(data_in[i]);
}
static int wm0010_firmware_load(const char *name, struct snd_soc_component *component)
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index a03bbde5d852..a2f0e2f5c407 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -471,11 +471,9 @@ static int wm8731_set_bias_level(struct snd_soc_component *component,
switch (level) {
case SND_SOC_BIAS_ON:
- if (wm8731->mclk) {
- ret = clk_prepare_enable(wm8731->mclk);
- if (ret)
- return ret;
- }
+ ret = clk_prepare_enable(wm8731->mclk);
+ if (ret)
+ return ret;
break;
case SND_SOC_BIAS_PREPARE:
break;
@@ -494,8 +492,7 @@ static int wm8731_set_bias_level(struct snd_soc_component *component,
snd_soc_component_write(component, WM8731_PWR, reg | 0x0040);
break;
case SND_SOC_BIAS_OFF:
- if (wm8731->mclk)
- clk_disable_unprepare(wm8731->mclk);
+ clk_disable_unprepare(wm8731->mclk);
snd_soc_component_write(component, WM8731_PWR, 0xffff);
regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies),
wm8731->supplies);
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index e9e317ce6898..8d2435bf44ea 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -67,6 +67,8 @@ struct wm8962_priv {
struct mutex dsp2_ena_lock;
u16 dsp2_ena;
+ int mic_status;
+
struct delayed_work mic_work;
struct snd_soc_jack *jack;
@@ -83,6 +85,8 @@ struct wm8962_priv {
int irq;
bool master_flag;
+ int tdm_width;
+ int tdm_slots;
};
/* We can't use the same notifier block for more than one supply and
@@ -1760,7 +1764,7 @@ SND_SOC_BYTES("EQR Coefficients", WM8962_EQ24, 18),
SOC_SINGLE("3D Switch", WM8962_THREED1, 0, 1, 0),
-SND_SOC_BYTES_MASK("3D Coefficients", WM8962_THREED1, 4, WM8962_THREED_ENA),
+SND_SOC_BYTES_MASK("3D Coefficients", WM8962_THREED1, 4, WM8962_THREED_ENA | WM8962_ADC_MONOMIX),
SOC_SINGLE("DF1 Switch", WM8962_DF1, 0, 1, 0),
SND_SOC_BYTES_MASK("DF1 Coefficients", WM8962_DF1, 7, WM8962_DF1_ENA),
@@ -2610,6 +2614,19 @@ static int wm8962_set_bias_level(struct snd_soc_component *component,
return 0;
}
+static int wm8962_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component);
+
+ wm8962->tdm_width = slot_width;
+ /* External is one slot one channel, but internal is one slot two channels */
+ wm8962->tdm_slots = slots / 2;
+
+ return 0;
+}
+
static const struct {
int rate;
int reg;
@@ -2637,10 +2654,21 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream,
int i;
int aif0 = 0;
int adctl3 = 0;
+ int width;
+
+ if (wm8962->tdm_width && wm8962->tdm_slots) {
+ wm8962->bclk = snd_soc_calc_bclk(params_rate(params),
+ wm8962->tdm_width,
+ params_channels(params),
+ wm8962->tdm_slots);
+ width = wm8962->tdm_width;
+ } else {
+ wm8962->bclk = snd_soc_params_to_bclk(params);
+ width = params_width(params);
- wm8962->bclk = snd_soc_params_to_bclk(params);
- if (params_channels(params) == 1)
- wm8962->bclk *= 2;
+ if (params_channels(params) == 1)
+ wm8962->bclk *= 2;
+ }
wm8962->lrclk = params_rate(params);
@@ -2658,7 +2686,7 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream,
if (wm8962->lrclk % 8000 == 0)
adctl3 |= WM8962_SAMPLE_RATE_INT_MODE;
- switch (params_width(params)) {
+ switch (width) {
case 16:
break;
case 20:
@@ -3037,6 +3065,7 @@ static const struct snd_soc_dai_ops wm8962_dai_ops = {
.hw_params = wm8962_hw_params,
.set_sysclk = wm8962_set_dai_sysclk,
.set_fmt = wm8962_set_dai_fmt,
+ .set_tdm_slot = wm8962_set_tdm_slot,
.mute_stream = wm8962_mute,
.no_capture_mute = 1,
};
@@ -3081,8 +3110,16 @@ static void wm8962_mic_work(struct work_struct *work)
if (reg & WM8962_MICSHORT_STS) {
status |= SND_JACK_BTN_0;
irq_pol |= WM8962_MICSCD_IRQ_POL;
+
+ /* Don't report a microphone if it's shorted right after
+ * plugging in, as this may be a TRS plug in a TRRS socket.
+ */
+ if (!(wm8962->mic_status & WM8962_MICDET_STS))
+ status = 0;
}
+ wm8962->mic_status = status;
+
snd_soc_jack_report(wm8962->jack, status,
SND_JACK_MICROPHONE | SND_JACK_BTN_0);
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index c4a00b22bc2a..828524c90f17 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -19,6 +19,7 @@ config SND_SOC_FSL_ASRC
config SND_SOC_FSL_SAI
tristate "Synchronous Audio Interface (SAI) module support"
+ depends on IMX_SCMI_MISC_DRV || !IMX_SCMI_MISC_DRV
select REGMAP_MMIO
select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n
select SND_SOC_GENERIC_DMAENGINE_PCM
@@ -32,7 +33,6 @@ config SND_SOC_FSL_SAI
config SND_SOC_FSL_MQS
tristate "Medium Quality Sound (MQS) module support"
depends on SND_SOC_FSL_SAI
- depends on IMX_SCMI_MISC_DRV || !IMX_SCMI_MISC_DRV
select REGMAP_MMIO
help
Say Y if you want to add Medium Quality Sound (MQS)
@@ -309,6 +309,7 @@ config SND_SOC_IMX_SGTL5000
config SND_SOC_FSL_ASOC_CARD
tristate "Generic ASoC Sound Card with ASRC support"
+ depends on IMX_SCMI_MISC_DRV || !IMX_SCMI_MISC_DRV
depends on OF && I2C
# enforce SND_SOC_FSL_ASOC_CARD=m if SND_AC97_CODEC=m:
depends on SND_AC97_CODEC || SND_AC97_CODEC=n
@@ -330,6 +331,7 @@ config SND_SOC_FSL_ASOC_CARD
config SND_SOC_IMX_AUDMIX
tristate "SoC Audio support for i.MX boards with AUDMIX"
+ depends on IMX_SCMI_MISC_DRV || !IMX_SCMI_MISC_DRV
select SND_SOC_FSL_AUDMIX
select SND_SOC_FSL_SAI
help
@@ -339,6 +341,7 @@ config SND_SOC_IMX_AUDMIX
config SND_SOC_IMX_HDMI
tristate "SoC Audio support for i.MX boards with HDMI port"
+ depends on IMX_SCMI_MISC_DRV || !IMX_SCMI_MISC_DRV
select SND_SOC_FSL_SAI
select SND_SOC_FSL_AUD2HTX
select SND_SOC_HDMI_CODEC
@@ -364,6 +367,7 @@ config SND_SOC_IMX_RPMSG
config SND_SOC_IMX_CARD
tristate "SoC Audio Graph Sound Card support for i.MX boards"
depends on OF && I2C
+ depends on IMX_SCMI_MISC_DRV || !IMX_SCMI_MISC_DRV
select SND_SOC_AK4458
select SND_SOC_AK5558
select SND_SOC_IMX_PCM_DMA
diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c
index 92fb16f7be45..5fda9b647c70 100644
--- a/sound/soc/fsl/fsl_asrc.c
+++ b/sound/soc/fsl/fsl_asrc.c
@@ -106,6 +106,12 @@ static unsigned char clk_map_imx8qxp[2][ASRC_CLK_MAP_LEN] = {
},
};
+static unsigned char clk_map_imx952[ASRC_CLK_MAP_LEN] = {
+ 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
+ 0x0, 0x1, 0x2, 0x3, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x4, 0x5, 0x6, 0x8, 0xf, 0xf,
+ 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x7, 0x9, 0xa, 0xb, 0xc, 0xd, 0xf, 0xf, 0xf, 0xf,
+};
+
/*
* According to RM, the divider range is 1 ~ 8,
* prescaler is power of 2 from 1 ~ 128.
@@ -1078,6 +1084,26 @@ static unsigned int fsl_asrc_get_output_fifo_size(struct fsl_asrc_pair *pair)
return val >> ASRFSTi_OUTPUT_FIFO_SHIFT;
}
+static bool fsl_asrc_m2m_output_ready(struct fsl_asrc_pair *pair)
+{
+ struct fsl_asrc *asrc = pair->asrc;
+ enum asrc_pair_index index = pair->index;
+ u32 val;
+ int ret;
+
+ /* Check output fifo status if it exceeds the watermark. */
+ ret = regmap_read_poll_timeout(asrc->regmap, REG_ASRFST(index), val,
+ (ASRFSTi_OUTPUT_FIFO_FILL(val) >= ASRC_M2M_OUTPUTFIFO_WML),
+ 1, 1000);
+
+ if (ret) {
+ pair_warn("output is not ready\n");
+ return false;
+ }
+
+ return true;
+}
+
static int fsl_asrc_m2m_prepare(struct fsl_asrc_pair *pair)
{
struct fsl_asrc_pair_priv *pair_priv = pair->private;
@@ -1275,6 +1301,7 @@ static int fsl_asrc_probe(struct platform_device *pdev)
asrc_priv->soc = of_device_get_match_data(&pdev->dev);
asrc->use_edma = asrc_priv->soc->use_edma;
+ asrc->start_before_dma = asrc_priv->soc->start_before_dma;
asrc->get_dma_channel = fsl_asrc_get_dma_channel;
asrc->request_pair = fsl_asrc_request_pair;
asrc->release_pair = fsl_asrc_release_pair;
@@ -1289,6 +1316,7 @@ static int fsl_asrc_probe(struct platform_device *pdev)
asrc->m2m_get_maxburst = fsl_asrc_m2m_get_maxburst;
asrc->m2m_pair_resume = fsl_asrc_m2m_pair_resume;
asrc->m2m_get_cap = fsl_asrc_m2m_get_cap;
+ asrc->m2m_output_ready = fsl_asrc_m2m_output_ready;
if (of_device_is_compatible(np, "fsl,imx35-asrc")) {
asrc_priv->clk_map[IN] = input_clk_map_imx35;
@@ -1315,6 +1343,9 @@ static int fsl_asrc_probe(struct platform_device *pdev)
asrc_priv->clk_map[IN] = clk_map_imx8qxp[map_idx];
asrc_priv->clk_map[OUT] = clk_map_imx8qxp[map_idx];
}
+ } else if (of_device_is_compatible(np, "fsl,imx952-asrc")) {
+ asrc_priv->clk_map[IN] = clk_map_imx952;
+ asrc_priv->clk_map[OUT] = clk_map_imx952;
}
asrc->channel_avail = 10;
@@ -1553,11 +1584,18 @@ static const struct fsl_asrc_soc_data fsl_asrc_imx8qxp_data = {
.channel_bits = 4,
};
+static const struct fsl_asrc_soc_data fsl_asrc_imx952_data = {
+ .use_edma = true,
+ .channel_bits = 4,
+ .start_before_dma = true,
+};
+
static const struct of_device_id fsl_asrc_ids[] = {
{ .compatible = "fsl,imx35-asrc", .data = &fsl_asrc_imx35_data },
{ .compatible = "fsl,imx53-asrc", .data = &fsl_asrc_imx53_data },
{ .compatible = "fsl,imx8qm-asrc", .data = &fsl_asrc_imx8qm_data },
{ .compatible = "fsl,imx8qxp-asrc", .data = &fsl_asrc_imx8qxp_data },
+ { .compatible = "fsl,imx952-asrc", .data = &fsl_asrc_imx952_data },
{}
};
MODULE_DEVICE_TABLE(of, fsl_asrc_ids);
diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h
index 1c492eb237f5..7a81366a0ee4 100644
--- a/sound/soc/fsl/fsl_asrc.h
+++ b/sound/soc/fsl/fsl_asrc.h
@@ -257,6 +257,8 @@
#define ASRFSTi_OUTPUT_FIFO_WIDTH 7
#define ASRFSTi_OUTPUT_FIFO_SHIFT 12
#define ASRFSTi_OUTPUT_FIFO_MASK (((1 << ASRFSTi_OUTPUT_FIFO_WIDTH) - 1) << ASRFSTi_OUTPUT_FIFO_SHIFT)
+#define ASRFSTi_OUTPUT_FIFO_FILL(v) \
+ (((v) & ASRFSTi_OUTPUT_FIFO_MASK) >> ASRFSTi_OUTPUT_FIFO_SHIFT)
#define ASRFSTi_IAEi_SHIFT 11
#define ASRFSTi_IAEi_MASK (1 << ASRFSTi_IAEi_SHIFT)
#define ASRFSTi_IAEi (1 << ASRFSTi_IAEi_SHIFT)
@@ -324,6 +326,13 @@ enum asrc_inclk {
INCLK_SAI6_TX_BCLK = 0x22,
INCLK_HDMI_RX_SAI0_RX_BCLK = 0x24,
INCLK_HDMI_TX_SAI0_TX_BCLK = 0x25,
+
+ INCLK_SAI2_TX_BCLK = 0x26,
+ INCLK_SAI3_TX_BCLK = 0x27,
+ INCLK_SAI4_RX_BCLK = 0x28,
+ INCLK_SAI4_TX_BCLK = 0x29,
+ INCLK_SAI5_RX_BCLK = 0x2a,
+ INCLK_SAI5_TX_BCLK = 0x2b,
};
enum asrc_outclk {
@@ -364,6 +373,13 @@ enum asrc_outclk {
OUTCLK_SAI6_TX_BCLK = 0x22,
OUTCLK_HDMI_RX_SAI0_RX_BCLK = 0x24,
OUTCLK_HDMI_TX_SAI0_TX_BCLK = 0x25,
+
+ OUTCLK_SAI2_TX_BCLK = 0x26,
+ OUTCLK_SAI3_TX_BCLK = 0x27,
+ OUTCLK_SAI4_RX_BCLK = 0x28,
+ OUTCLK_SAI4_TX_BCLK = 0x29,
+ OUTCLK_SAI5_RX_BCLK = 0x2a,
+ OUTCLK_SAI5_TX_BCLK = 0x2b,
};
#define ASRC_CLK_MAX_NUM 16
@@ -432,10 +448,12 @@ struct dma_block {
*
* @use_edma: using edma as dma device or not
* @channel_bits: width of ASRCNCR register for each pair
+ * @start_before_dma: start asrc before dma
*/
struct fsl_asrc_soc_data {
bool use_edma;
unsigned int channel_bits;
+ bool start_before_dma;
};
/**
diff --git a/sound/soc/fsl/fsl_asrc_common.h b/sound/soc/fsl/fsl_asrc_common.h
index 0cd595b0f629..c8a1a2b5915d 100644
--- a/sound/soc/fsl/fsl_asrc_common.h
+++ b/sound/soc/fsl/fsl_asrc_common.h
@@ -107,6 +107,7 @@ struct fsl_asrc_pair {
* @asrc_rate: default sample rate for ASoC Back-Ends
* @asrc_format: default sample format for ASoC Back-Ends
* @use_edma: edma is used
+ * @start_before_dma: start asrc before dma
* @get_dma_channel: function pointer
* @request_pair: function pointer
* @release_pair: function pointer
@@ -116,6 +117,7 @@ struct fsl_asrc_pair {
* @m2m_start: function pointer
* @m2m_unprepare: function pointer
* @m2m_stop: function pointer
+ * @m2m_output_ready: function pointer, check output fifo ready or not
* @m2m_calc_out_len: function pointer
* @m2m_get_maxburst: function pointer
* @m2m_pair_suspend: function pointer
@@ -143,6 +145,7 @@ struct fsl_asrc {
int asrc_rate;
snd_pcm_format_t asrc_format;
bool use_edma;
+ bool start_before_dma;
struct dma_chan *(*get_dma_channel)(struct fsl_asrc_pair *pair, bool dir);
int (*request_pair)(int channels, struct fsl_asrc_pair *pair);
@@ -154,6 +157,7 @@ struct fsl_asrc {
int (*m2m_start)(struct fsl_asrc_pair *pair);
int (*m2m_unprepare)(struct fsl_asrc_pair *pair);
int (*m2m_stop)(struct fsl_asrc_pair *pair);
+ bool (*m2m_output_ready)(struct fsl_asrc_pair *pair);
int (*m2m_calc_out_len)(struct fsl_asrc_pair *pair, int input_buffer_length);
int (*m2m_get_maxburst)(u8 dir, struct fsl_asrc_pair *pair);
diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c
index 7dacc06b2f02..348b0aabfa68 100644
--- a/sound/soc/fsl/fsl_asrc_dma.c
+++ b/sound/soc/fsl/fsl_asrc_dma.c
@@ -449,18 +449,52 @@ fsl_asrc_dma_pcm_pointer(struct snd_soc_component *component,
static int fsl_asrc_dma_pcm_new(struct snd_soc_component *component,
struct snd_soc_pcm_runtime *rtd)
{
- struct snd_card *card = rtd->card->snd_card;
+ struct device *dev = component->dev;
+ struct fsl_asrc *asrc = dev_get_drvdata(dev);
+ struct fsl_asrc_pair *pair;
struct snd_pcm *pcm = rtd->pcm;
+ struct dma_chan *chan;
int ret;
- ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
- if (ret) {
- dev_err(card->dev, "failed to set DMA mask\n");
- return ret;
+ pair = kzalloc(size_add(sizeof(*pair), asrc->pair_priv_size), GFP_KERNEL);
+ if (!pair)
+ return -ENOMEM;
+
+ pair->asrc = asrc;
+ pair->private = (void *)pair + sizeof(struct fsl_asrc_pair);
+
+ /* Request a pair, which will be released later.
+ * Request pair function needs channel num as input, for this
+ * pair, we just request "1" channel temporarily.
+ */
+ ret = asrc->request_pair(1, pair);
+ if (ret < 0) {
+ dev_err(dev, "failed to request asrc pair\n");
+ goto req_pair_err;
+ }
+
+ /* Request a dma channel, which will be released later. */
+ chan = asrc->get_dma_channel(pair, IN);
+ if (!chan) {
+ dev_err(dev, "failed to get dma channel\n");
+ ret = -EINVAL;
+ goto dma_chan_err;
}
- return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
- card->dev, FSL_ASRC_DMABUF_SIZE);
+ ret = snd_pcm_set_fixed_buffer_all(pcm,
+ SNDRV_DMA_TYPE_DEV,
+ chan->device->dev,
+ FSL_ASRC_DMABUF_SIZE);
+
+ dma_release_channel(chan);
+
+dma_chan_err:
+ asrc->release_pair(pair);
+
+req_pair_err:
+ kfree(pair);
+
+ return ret;
}
struct snd_soc_component_driver fsl_asrc_component = {
diff --git a/sound/soc/fsl/fsl_asrc_m2m.c b/sound/soc/fsl/fsl_asrc_m2m.c
index f46881f71e43..77999526dd9e 100644
--- a/sound/soc/fsl/fsl_asrc_m2m.c
+++ b/sound/soc/fsl/fsl_asrc_m2m.c
@@ -253,15 +253,21 @@ static int asrc_m2m_device_run(struct fsl_asrc_pair *pair, struct snd_compr_task
reinit_completion(&pair->complete[IN]);
reinit_completion(&pair->complete[OUT]);
+ if (asrc->start_before_dma)
+ asrc->m2m_start(pair);
+
/* Submit DMA request */
dmaengine_submit(pair->desc[IN]);
dma_async_issue_pending(pair->desc[IN]->chan);
if (out_dma_len > 0) {
+ if (asrc->start_before_dma && asrc->m2m_output_ready)
+ asrc->m2m_output_ready(pair);
dmaengine_submit(pair->desc[OUT]);
dma_async_issue_pending(pair->desc[OUT]->chan);
}
- asrc->m2m_start(pair);
+ if (!asrc->start_before_dma)
+ asrc->m2m_start(pair);
if (!wait_for_completion_interruptible_timeout(&pair->complete[IN], 10 * HZ)) {
dev_err(dev, "out DMA task timeout\n");
diff --git a/sound/soc/fsl/fsl_audmix.c b/sound/soc/fsl/fsl_audmix.c
index 7981d598ba13..40a3b7432174 100644
--- a/sound/soc/fsl/fsl_audmix.c
+++ b/sound/soc/fsl/fsl_audmix.c
@@ -444,6 +444,9 @@ static const struct of_device_id fsl_audmix_ids[] = {
{
.compatible = "fsl,imx8qm-audmix",
},
+ {
+ .compatible = "fsl,imx952-audmix",
+ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsl_audmix_ids);
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index 2fa14fbdfe1a..148e09e58dfa 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -7,6 +7,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dmaengine.h>
+#include <linux/firmware/imx/sm.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
@@ -1425,10 +1426,12 @@ static int fsl_sai_probe(struct platform_device *pdev)
struct fsl_sai *sai;
struct regmap *gpr;
void __iomem *base;
+ const char *str = NULL;
char tmp[8];
int irq, ret, i;
int index;
u32 dmas[4];
+ u32 val;
sai = devm_kzalloc(dev, sizeof(*sai), GFP_KERNEL);
if (!sai)
@@ -1598,6 +1601,24 @@ static int fsl_sai_probe(struct platform_device *pdev)
if (ret < 0 && ret != -ENOSYS)
goto err_pm_get_sync;
+ if (of_device_is_compatible(np, "fsl,imx952-sai") &&
+ !of_property_read_string(np, "fsl,sai-amix-mode", &str)) {
+ if (!strcmp(str, "bypass"))
+ val = FSL_SAI_AMIX_BYPASS;
+ else if (!strcmp(str, "audmix"))
+ val = FSL_SAI_AMIX_AUDMIX;
+ else
+ val = FSL_SAI_AMIX_NONE;
+
+ if (val < FSL_SAI_AMIX_NONE) {
+ ret = scmi_imx_misc_ctrl_set(SCMI_IMX952_CTRL_BYPASS_AUDMIX, val);
+ if (ret) {
+ dev_err_probe(dev, ret, "Error setting audmix mode\n");
+ goto err_pm_get_sync;
+ }
+ }
+ }
+
/*
* Register platform component before registering cpu dai for there
* is not defer probe for platform component in snd_soc_add_pcm_runtime().
diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h
index 6c917f79c6b0..7605cbaca3d8 100644
--- a/sound/soc/fsl/fsl_sai.h
+++ b/sound/soc/fsl/fsl_sai.h
@@ -230,6 +230,10 @@
#define FSL_SAI_DL_I2S BIT(0)
#define FSL_SAI_DL_PDM BIT(1)
+#define FSL_SAI_AMIX_BYPASS 0
+#define FSL_SAI_AMIX_AUDMIX 1
+#define FSL_SAI_AMIX_NONE 2
+
struct fsl_sai_soc_data {
bool use_imx_pcm;
bool use_edma;
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 320108bebf30..b2e1da1781ae 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -1447,7 +1447,7 @@ static int fsl_ssi_probe_from_dt(struct fsl_ssi *ssi)
dev_err(dev, "failed to get SSI index property\n");
return -EINVAL;
}
- strcpy(ssi->card_name, "ac97-codec");
+ strscpy(ssi->card_name, "ac97-codec");
} else if (!of_property_read_bool(np, "fsl,ssi-asynchronous")) {
/*
* In synchronous mode, STCK and STFS ports are used by RX
diff --git a/sound/soc/fsl/imx-rpmsg.c b/sound/soc/fsl/imx-rpmsg.c
index 53f04d1f3280..76a8e68c1b62 100644
--- a/sound/soc/fsl/imx-rpmsg.c
+++ b/sound/soc/fsl/imx-rpmsg.c
@@ -145,7 +145,7 @@ static int imx_rpmsg_probe(struct platform_device *pdev)
data->dai.ignore_pmdown_time = 1;
data->dai.cpus->dai_name = pdev->dev.platform_data;
- cpu_dai = snd_soc_find_dai(data->dai.cpus);
+ cpu_dai = snd_soc_find_dai_with_mutex(data->dai.cpus);
if (!cpu_dai) {
ret = -EPROBE_DEFER;
goto fail;
diff --git a/sound/soc/intel/avs/board_selection.c b/sound/soc/intel/avs/board_selection.c
index 52e6266a7cb8..8a46285181fa 100644
--- a/sound/soc/intel/avs/board_selection.c
+++ b/sound/soc/intel/avs/board_selection.c
@@ -367,7 +367,7 @@ static const struct avs_acpi_boards i2s_boards[] = {
AVS_MACH_ENTRY(HDA_SKL_LP, avs_skl_i2s_machines),
AVS_MACH_ENTRY(HDA_KBL_LP, avs_kbl_i2s_machines),
AVS_MACH_ENTRY(HDA_APL, avs_apl_i2s_machines),
- AVS_MACH_ENTRY(HDA_GML, avs_gml_i2s_machines),
+ AVS_MACH_ENTRY(HDA_GLK, avs_gml_i2s_machines),
AVS_MACH_ENTRY(HDA_CNL_LP, avs_cnl_i2s_machines),
AVS_MACH_ENTRY(HDA_CNL_H, avs_cnl_i2s_machines),
AVS_MACH_ENTRY(HDA_CML_LP, avs_cnl_i2s_machines),
diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c
index 6e0e65584c7f..1a53856c2ffb 100644
--- a/sound/soc/intel/avs/core.c
+++ b/sound/soc/intel/avs/core.c
@@ -897,7 +897,7 @@ static const struct pci_device_id avs_ids[] = {
{ PCI_DEVICE_DATA(INTEL, HDA_KBL_H, &skl_desc) },
{ PCI_DEVICE_DATA(INTEL, HDA_CML_S, &skl_desc) },
{ PCI_DEVICE_DATA(INTEL, HDA_APL, &apl_desc) },
- { PCI_DEVICE_DATA(INTEL, HDA_GML, &apl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_GLK, &apl_desc) },
{ PCI_DEVICE_DATA(INTEL, HDA_CNL_LP, &cnl_desc) },
{ PCI_DEVICE_DATA(INTEL, HDA_CNL_H, &cnl_desc) },
{ PCI_DEVICE_DATA(INTEL, HDA_CML_LP, &cnl_desc) },
diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c
index c8b586aced20..899906e0bcb4 100644
--- a/sound/soc/intel/avs/path.c
+++ b/sound/soc/intel/avs/path.c
@@ -134,7 +134,7 @@ static struct avs_tplg_path *avs_condpath_find_variant(struct avs_dev *adev,
static bool avs_tplg_path_template_id_equal(struct avs_tplg_path_template_id *id,
struct avs_tplg_path_template_id *id2)
{
- return id->id == id2->id && !strcmp(id->tplg_name, id2->tplg_name);
+ return id->id == id2->id && !sysfs_streq(id->tplg_name, id2->tplg_name);
}
static struct avs_path *avs_condpath_find_match(struct avs_dev *adev,
diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig
index c23fdb6aad4c..c5942b5655d3 100644
--- a/sound/soc/intel/boards/Kconfig
+++ b/sound/soc/intel/boards/Kconfig
@@ -41,6 +41,9 @@ config SND_SOC_INTEL_SOF_CIRRUS_COMMON
config SND_SOC_INTEL_SOF_NUVOTON_COMMON
tristate
+config SND_SOC_INTEL_SOF_TI_COMMON
+ tristate
+
config SND_SOC_INTEL_SOF_BOARD_HELPERS
select SND_SOC_ACPI_INTEL_MATCH
tristate
@@ -327,11 +330,13 @@ config SND_SOC_INTEL_SOF_RT5682_MACH
select SND_SOC_RT5645
select SND_SOC_RT5682_I2C
select SND_SOC_RT5682S
+ select SND_SOC_TAS2781_I2C
select SND_SOC_DMIC
select SND_SOC_INTEL_HDA_DSP_COMMON
select SND_SOC_INTEL_SOF_BOARD_HELPERS
select SND_SOC_INTEL_SOF_MAXIM_COMMON
select SND_SOC_INTEL_SOF_REALTEK_COMMON
+ select SND_SOC_INTEL_SOF_TI_COMMON
select SND_SOC_ACPI_INTEL_MATCH
help
This adds support for ASoC machine driver for SOF platforms
diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile
index fcd517d6c279..25a1a9066cbf 100644
--- a/sound/soc/intel/boards/Makefile
+++ b/sound/soc/intel/boards/Makefile
@@ -69,5 +69,8 @@ obj-$(CONFIG_SND_SOC_INTEL_SOF_CIRRUS_COMMON) += snd-soc-intel-sof-cirrus-common
snd-soc-intel-sof-nuvoton-common-y += sof_nuvoton_common.o
obj-$(CONFIG_SND_SOC_INTEL_SOF_NUVOTON_COMMON) += snd-soc-intel-sof-nuvoton-common.o
+snd-soc-intel-sof-ti-common-y += sof_ti_common.o
+obj-$(CONFIG_SND_SOC_INTEL_SOF_TI_COMMON) += snd-soc-intel-sof-ti-common.o
+
snd-soc-intel-sof-board-helpers-y += sof_board_helpers.o
obj-$(CONFIG_SND_SOC_INTEL_SOF_BOARD_HELPERS) += snd-soc-intel-sof-board-helpers.o
diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c
index 3d9d8a97d153..649378957b20 100644
--- a/sound/soc/intel/boards/sof_rt5682.c
+++ b/sound/soc/intel/boards/sof_rt5682.c
@@ -27,6 +27,7 @@
#include "sof_board_helpers.h"
#include "sof_maxim_common.h"
#include "sof_realtek_common.h"
+#include "sof_ti_common.h"
/* Driver-specific board quirks: from bit 0 to 7 */
#define SOF_RT5682_MCLK_EN BIT(0)
@@ -620,6 +621,9 @@ sof_card_dai_links_create(struct device *dev, struct snd_soc_card *card,
ctx->amp_link->init = rt5650_spk_init;
ctx->amp_link->ops = &sof_rt5682_ops;
break;
+ case CODEC_TAS2563:
+ sof_tas2563_dai_link(ctx->amp_link);
+ break;
default:
dev_err(dev, "invalid amp type %d\n", ctx->amp_type);
return -EINVAL;
@@ -767,6 +771,7 @@ static int sof_audio_probe(struct platform_device *pdev)
case CODEC_MAX98360A:
case CODEC_RT1019P:
case CODEC_RT5650:
+ case CODEC_TAS2563:
case CODEC_NONE:
/* no codec conf required */
break;
@@ -934,3 +939,4 @@ MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS("SND_SOC_INTEL_SOF_BOARD_HELPERS");
MODULE_IMPORT_NS("SND_SOC_INTEL_SOF_MAXIM_COMMON");
MODULE_IMPORT_NS("SND_SOC_INTEL_SOF_REALTEK_COMMON");
+MODULE_IMPORT_NS("SND_SOC_INTEL_SOF_TI_COMMON");
diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c
index 50b838be24e9..ee34282828e4 100644
--- a/sound/soc/intel/boards/sof_sdw.c
+++ b/sound/soc/intel/boards/sof_sdw.c
@@ -1186,6 +1186,34 @@ static int create_bt_dailinks(struct snd_soc_card *card,
return 0;
}
+static int create_echoref_dailink(struct snd_soc_card *card,
+ struct snd_soc_dai_link **dai_links, int *be_id)
+{
+ struct device *dev = card->dev;
+ int ret;
+ char *name = devm_kasprintf(dev, GFP_KERNEL, "Loopback_Virtual");
+
+ if (!name)
+ return -ENOMEM;
+
+ /*
+ * use dummy DAI names as this won't be connected to an actual DAI but just to establish a
+ * fe <-> be connection for loopback capture for echo reference
+ */
+ ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, name,
+ 0, 1, "Loopback Virtual Pin", "dummy",
+ snd_soc_dummy_dlc.name, snd_soc_dummy_dlc.dai_name,
+ 1, NULL, NULL);
+ if (ret)
+ return ret;
+
+ (*dai_links)++;
+
+ dev_dbg(dev, "Added echo reference DAI link\n");
+
+ return 0;
+}
+
static int sof_card_dai_links_create(struct snd_soc_card *card)
{
struct device *dev = card->dev;
@@ -1294,8 +1322,12 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)
goto err_end;
}
- /* allocate BE dailinks */
- num_links = sdw_be_num + ssp_num + dmic_num + hdmi_num + bt_num;
+ /*
+ * allocate BE dailinks, add an extra DAI link for echo reference capture.
+ * This should be the last DAI link and it is expected both for monolithic
+ * and functional SOF topologies to support echo reference.
+ */
+ num_links = sdw_be_num + ssp_num + dmic_num + hdmi_num + bt_num + 1;
dai_links = devm_kcalloc(dev, num_links, sizeof(*dai_links), GFP_KERNEL);
if (!dai_links) {
ret = -ENOMEM;
@@ -1344,6 +1376,13 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)
goto err_end;
}
+ /* dummy echo ref link. keep this as the last DAI link. The DAI link ID does not matter */
+ ret = create_echoref_dailink(card, &dai_links, &be_id);
+ if (ret) {
+ dev_err(dev, "failed to create echo ref dai link: %d\n", ret);
+ goto err_end;
+ }
+
WARN_ON(codec_conf != card->codec_conf + card->num_configs);
WARN_ON(dai_links != card->dai_link + card->num_links);
diff --git a/sound/soc/intel/boards/sof_ti_common.c b/sound/soc/intel/boards/sof_ti_common.c
new file mode 100644
index 000000000000..e527bdeb787e
--- /dev/null
+++ b/sound/soc/intel/boards/sof_ti_common.c
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2025 Intel Corporation
+#include <linux/module.h>
+#include <linux/string.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+#include <sound/sof.h>
+#include <uapi/sound/asound.h>
+#include "../common/soc-intel-quirks.h"
+#include "sof_ti_common.h"
+
+/*
+ * Texas Instruments TAS2563 just mount one device to manage multiple devices,
+ * so the kcontrols, widgets and routes just keep one item, respectively.
+ */
+static const struct snd_kcontrol_new tas2563_spk_kcontrols[] = {
+ SOC_DAPM_PIN_SWITCH("Spk"),
+};
+
+static const struct snd_soc_dapm_widget tas2563_spk_dapm_widgets[] = {
+ SND_SOC_DAPM_SPK("Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route tas2563_spk_dapm_routes[] = {
+ { "Spk", NULL, "OUT" },
+};
+
+static struct snd_soc_dai_link_component tas2563_dai_link_components[] = {
+ {
+ .name = TAS2563_DEV0_NAME,
+ .dai_name = TAS2563_CODEC_DAI,
+ },
+};
+
+static int tas2563_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, tas2563_spk_dapm_widgets,
+ ARRAY_SIZE(tas2563_spk_dapm_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add dapm widgets, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, tas2563_spk_kcontrols,
+ ARRAY_SIZE(tas2563_spk_kcontrols));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(dapm, tas2563_spk_dapm_routes,
+ ARRAY_SIZE(tas2563_spk_dapm_routes));
+ if (ret)
+ dev_err(rtd->dev, "unable to add dapm routes, ret %d\n", ret);
+
+ return ret;
+}
+
+void sof_tas2563_dai_link(struct snd_soc_dai_link *link)
+{
+ link->codecs = tas2563_dai_link_components;
+ link->num_codecs = ARRAY_SIZE(tas2563_dai_link_components);
+ link->init = tas2563_init;
+}
+EXPORT_SYMBOL_NS(sof_tas2563_dai_link, "SND_SOC_INTEL_SOF_TI_COMMON");
+
+MODULE_DESCRIPTION("ASoC Intel SOF Texas Instruments helpers");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/boards/sof_ti_common.h b/sound/soc/intel/boards/sof_ti_common.h
new file mode 100644
index 000000000000..de15845aff0c
--- /dev/null
+++ b/sound/soc/intel/boards/sof_ti_common.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2025 Intel Corporation.
+ */
+
+/*
+ * This file defines data structures used in Machine Driver for Intel
+ * platforms with Texas Instruments Codecs.
+ */
+#ifndef __SOF_TI_COMMON_H
+#define __SOF_TI_COMMON_H
+
+#include <sound/soc.h>
+#include <sound/soc-acpi-intel-ssp-common.h>
+
+/*
+ * Texas Instruments TAS2563
+ */
+#define TAS2563_CODEC_DAI "tasdev_codec"
+#define TAS2563_DEV0_NAME "i2c-" TAS2563_ACPI_HID ":00"
+
+void sof_tas2563_dai_link(struct snd_soc_dai_link *link);
+
+#endif /* __SOF_TI_COMMON_H */
diff --git a/sound/soc/intel/catpt/core.h b/sound/soc/intel/catpt/core.h
index c01d27e9fd88..df8a5fd95e13 100644
--- a/sound/soc/intel/catpt/core.h
+++ b/sound/soc/intel/catpt/core.h
@@ -62,6 +62,7 @@ struct catpt_module_type {
struct catpt_spec {
struct snd_soc_acpi_mach *machines;
u8 core_id;
+ const char *fw_name;
u32 host_dram_offset;
u32 host_iram_offset;
u32 host_shim_offset;
@@ -129,13 +130,13 @@ irqreturn_t catpt_dsp_irq_thread(int irq, void *dev_id);
* HOST <-> DSP communication yet failure to process specific request.
* Use below macro to convert returned non-zero values appropriately
*/
-#define CATPT_IPC_ERROR(err) (((err) < 0) ? (err) : -EREMOTEIO)
+#define CATPT_IPC_RET(ret) (((ret) <= 0) ? (ret) : -EREMOTEIO)
int catpt_dsp_send_msg_timeout(struct catpt_dev *cdev,
struct catpt_ipc_msg request,
- struct catpt_ipc_msg *reply, int timeout);
+ struct catpt_ipc_msg *reply, int timeout, const char *name);
int catpt_dsp_send_msg(struct catpt_dev *cdev, struct catpt_ipc_msg request,
- struct catpt_ipc_msg *reply);
+ struct catpt_ipc_msg *reply, const char *name);
int catpt_first_boot_firmware(struct catpt_dev *cdev);
int catpt_boot_firmware(struct catpt_dev *cdev, bool restore);
diff --git a/sound/soc/intel/catpt/device.c b/sound/soc/intel/catpt/device.c
index d13062c8e907..0638aecba40d 100644
--- a/sound/soc/intel/catpt/device.c
+++ b/sound/soc/intel/catpt/device.c
@@ -41,7 +41,7 @@ static int catpt_do_suspend(struct device *dev)
memset(&cdev->dx_ctx, 0, sizeof(cdev->dx_ctx));
ret = catpt_ipc_enter_dxstate(cdev, CATPT_DX_STATE_D3, &cdev->dx_ctx);
if (ret) {
- ret = CATPT_IPC_ERROR(ret);
+ ret = CATPT_IPC_RET(ret);
goto release_dma_chan;
}
@@ -107,7 +107,7 @@ static int catpt_resume(struct device *dev)
ret = catpt_ipc_set_device_format(cdev, &cdev->devfmt[i]);
if (ret)
- return CATPT_IPC_ERROR(ret);
+ return CATPT_IPC_RET(ret);
}
return 0;
@@ -348,6 +348,7 @@ static struct snd_soc_acpi_mach wpt_machines[] = {
static struct catpt_spec lpt_desc = {
.machines = lpt_machines,
.core_id = 0x01,
+ .fw_name = "intel/IntcSST1.bin",
.host_dram_offset = 0x000000,
.host_iram_offset = 0x080000,
.host_shim_offset = 0x0E7000,
@@ -363,6 +364,7 @@ static struct catpt_spec lpt_desc = {
static struct catpt_spec wpt_desc = {
.machines = wpt_machines,
.core_id = 0x02,
+ .fw_name = "intel/IntcSST2.bin",
.host_dram_offset = 0x000000,
.host_iram_offset = 0x0A0000,
.host_shim_offset = 0x0FB000,
diff --git a/sound/soc/intel/catpt/ipc.c b/sound/soc/intel/catpt/ipc.c
index d26863249097..5a01a9afb26e 100644
--- a/sound/soc/intel/catpt/ipc.c
+++ b/sound/soc/intel/catpt/ipc.c
@@ -84,7 +84,7 @@ static int catpt_wait_msg_completion(struct catpt_dev *cdev, int timeout)
static int catpt_dsp_do_send_msg(struct catpt_dev *cdev,
struct catpt_ipc_msg request,
- struct catpt_ipc_msg *reply, int timeout)
+ struct catpt_ipc_msg *reply, int timeout, const char *name)
{
struct catpt_ipc *ipc = &cdev->ipc;
unsigned long flags;
@@ -111,6 +111,8 @@ static int catpt_dsp_do_send_msg(struct catpt_dev *cdev,
}
ret = ipc->rx.rsp.status;
+ if (ret)
+ dev_err(cdev->dev, "%s (0x%08x) failed: %d\n", name, request.header, ret);
if (reply) {
reply->header = ipc->rx.header;
@@ -123,23 +125,23 @@ static int catpt_dsp_do_send_msg(struct catpt_dev *cdev,
int catpt_dsp_send_msg_timeout(struct catpt_dev *cdev,
struct catpt_ipc_msg request,
- struct catpt_ipc_msg *reply, int timeout)
+ struct catpt_ipc_msg *reply, int timeout, const char *name)
{
struct catpt_ipc *ipc = &cdev->ipc;
int ret;
mutex_lock(&ipc->mutex);
- ret = catpt_dsp_do_send_msg(cdev, request, reply, timeout);
+ ret = catpt_dsp_do_send_msg(cdev, request, reply, timeout, name);
mutex_unlock(&ipc->mutex);
return ret;
}
int catpt_dsp_send_msg(struct catpt_dev *cdev, struct catpt_ipc_msg request,
- struct catpt_ipc_msg *reply)
+ struct catpt_ipc_msg *reply, const char *name)
{
return catpt_dsp_send_msg_timeout(cdev, request, reply,
- cdev->ipc.default_timeout);
+ cdev->ipc.default_timeout, name);
}
static void
diff --git a/sound/soc/intel/catpt/loader.c b/sound/soc/intel/catpt/loader.c
index f5705cd2c1e1..dc7afe587e6f 100644
--- a/sound/soc/intel/catpt/loader.c
+++ b/sound/soc/intel/catpt/loader.c
@@ -580,10 +580,6 @@ release_fw:
static int catpt_load_images(struct catpt_dev *cdev, bool restore)
{
- static const char *const names[] = {
- "intel/IntcSST1.bin",
- "intel/IntcSST2.bin",
- };
struct dma_chan *chan;
int ret;
@@ -591,7 +587,7 @@ static int catpt_load_images(struct catpt_dev *cdev, bool restore)
if (IS_ERR(chan))
return PTR_ERR(chan);
- ret = catpt_load_image(cdev, chan, names[cdev->spec->core_id - 1],
+ ret = catpt_load_image(cdev, chan, cdev->spec->fw_name,
FW_SIGNATURE, restore);
if (ret)
goto release_dma_chan;
@@ -656,7 +652,7 @@ int catpt_first_boot_firmware(struct catpt_dev *cdev)
ret = catpt_ipc_get_mixer_stream_info(cdev, &cdev->mixer);
if (ret)
- return CATPT_IPC_ERROR(ret);
+ return CATPT_IPC_RET(ret);
ret = catpt_arm_stream_templates(cdev);
if (ret) {
diff --git a/sound/soc/intel/catpt/messages.c b/sound/soc/intel/catpt/messages.c
index 30eec2de4dc1..688a2d79500d 100644
--- a/sound/soc/intel/catpt/messages.c
+++ b/sound/soc/intel/catpt/messages.c
@@ -15,17 +15,12 @@ int catpt_ipc_get_fw_version(struct catpt_dev *cdev,
{
union catpt_global_msg msg = CATPT_GLOBAL_MSG(GET_FW_VERSION);
struct catpt_ipc_msg request = {{0}}, reply;
- int ret;
request.header = msg.val;
reply.size = sizeof(*version);
reply.data = version;
- ret = catpt_dsp_send_msg(cdev, request, &reply);
- if (ret)
- dev_err(cdev->dev, "get fw version failed: %d\n", ret);
-
- return ret;
+ return catpt_dsp_send_msg(cdev, request, &reply, "get fw version");
}
struct catpt_alloc_stream_input {
@@ -94,11 +89,7 @@ int catpt_ipc_alloc_stream(struct catpt_dev *cdev,
reply.size = sizeof(*sinfo);
reply.data = sinfo;
- ret = catpt_dsp_send_msg(cdev, request, &reply);
- if (ret)
- dev_err(cdev->dev, "alloc stream type %d failed: %d\n",
- type, ret);
-
+ ret = catpt_dsp_send_msg(cdev, request, &reply, "alloc stream");
kfree(payload);
return ret;
}
@@ -107,18 +98,12 @@ int catpt_ipc_free_stream(struct catpt_dev *cdev, u8 stream_hw_id)
{
union catpt_global_msg msg = CATPT_GLOBAL_MSG(FREE_STREAM);
struct catpt_ipc_msg request;
- int ret;
request.header = msg.val;
request.size = sizeof(stream_hw_id);
request.data = &stream_hw_id;
- ret = catpt_dsp_send_msg(cdev, request, NULL);
- if (ret)
- dev_err(cdev->dev, "free stream %d failed: %d\n",
- stream_hw_id, ret);
-
- return ret;
+ return catpt_dsp_send_msg(cdev, request, NULL, "free stream");
}
int catpt_ipc_set_device_format(struct catpt_dev *cdev,
@@ -126,17 +111,12 @@ int catpt_ipc_set_device_format(struct catpt_dev *cdev,
{
union catpt_global_msg msg = CATPT_GLOBAL_MSG(SET_DEVICE_FORMATS);
struct catpt_ipc_msg request;
- int ret;
request.header = msg.val;
request.size = sizeof(*devfmt);
request.data = devfmt;
- ret = catpt_dsp_send_msg(cdev, request, NULL);
- if (ret)
- dev_err(cdev->dev, "set device format failed: %d\n", ret);
-
- return ret;
+ return catpt_dsp_send_msg(cdev, request, NULL, "set device format");
}
int catpt_ipc_enter_dxstate(struct catpt_dev *cdev, enum catpt_dx_state state,
@@ -144,7 +124,6 @@ int catpt_ipc_enter_dxstate(struct catpt_dev *cdev, enum catpt_dx_state state,
{
union catpt_global_msg msg = CATPT_GLOBAL_MSG(ENTER_DX_STATE);
struct catpt_ipc_msg request, reply;
- int ret;
request.header = msg.val;
request.size = sizeof(state);
@@ -152,11 +131,7 @@ int catpt_ipc_enter_dxstate(struct catpt_dev *cdev, enum catpt_dx_state state,
reply.size = sizeof(*context);
reply.data = context;
- ret = catpt_dsp_send_msg(cdev, request, &reply);
- if (ret)
- dev_err(cdev->dev, "enter dx state failed: %d\n", ret);
-
- return ret;
+ return catpt_dsp_send_msg(cdev, request, &reply, "enter dx state");
}
int catpt_ipc_get_mixer_stream_info(struct catpt_dev *cdev,
@@ -164,68 +139,45 @@ int catpt_ipc_get_mixer_stream_info(struct catpt_dev *cdev,
{
union catpt_global_msg msg = CATPT_GLOBAL_MSG(GET_MIXER_STREAM_INFO);
struct catpt_ipc_msg request = {{0}}, reply;
- int ret;
request.header = msg.val;
reply.size = sizeof(*info);
reply.data = info;
- ret = catpt_dsp_send_msg(cdev, request, &reply);
- if (ret)
- dev_err(cdev->dev, "get mixer info failed: %d\n", ret);
-
- return ret;
+ return catpt_dsp_send_msg(cdev, request, &reply, "get mixer info");
}
int catpt_ipc_reset_stream(struct catpt_dev *cdev, u8 stream_hw_id)
{
union catpt_stream_msg msg = CATPT_STREAM_MSG(RESET_STREAM);
struct catpt_ipc_msg request = {{0}};
- int ret;
msg.stream_hw_id = stream_hw_id;
request.header = msg.val;
- ret = catpt_dsp_send_msg(cdev, request, NULL);
- if (ret)
- dev_err(cdev->dev, "reset stream %d failed: %d\n",
- stream_hw_id, ret);
-
- return ret;
+ return catpt_dsp_send_msg(cdev, request, NULL, "reset stream");
}
int catpt_ipc_pause_stream(struct catpt_dev *cdev, u8 stream_hw_id)
{
union catpt_stream_msg msg = CATPT_STREAM_MSG(PAUSE_STREAM);
struct catpt_ipc_msg request = {{0}};
- int ret;
msg.stream_hw_id = stream_hw_id;
request.header = msg.val;
- ret = catpt_dsp_send_msg(cdev, request, NULL);
- if (ret)
- dev_err(cdev->dev, "pause stream %d failed: %d\n",
- stream_hw_id, ret);
-
- return ret;
+ return catpt_dsp_send_msg(cdev, request, NULL, "pause stream");
}
int catpt_ipc_resume_stream(struct catpt_dev *cdev, u8 stream_hw_id)
{
union catpt_stream_msg msg = CATPT_STREAM_MSG(RESUME_STREAM);
struct catpt_ipc_msg request = {{0}};
- int ret;
msg.stream_hw_id = stream_hw_id;
request.header = msg.val;
- ret = catpt_dsp_send_msg(cdev, request, NULL);
- if (ret)
- dev_err(cdev->dev, "resume stream %d failed: %d\n",
- stream_hw_id, ret);
-
- return ret;
+ return catpt_dsp_send_msg(cdev, request, NULL, "resume stream");
}
struct catpt_set_volume_input {
@@ -243,7 +195,6 @@ int catpt_ipc_set_volume(struct catpt_dev *cdev, u8 stream_hw_id,
union catpt_stream_msg msg = CATPT_STAGE_MSG(SET_VOLUME);
struct catpt_ipc_msg request;
struct catpt_set_volume_input input;
- int ret;
msg.stream_hw_id = stream_hw_id;
input.channel = channel;
@@ -255,12 +206,7 @@ int catpt_ipc_set_volume(struct catpt_dev *cdev, u8 stream_hw_id,
request.size = sizeof(input);
request.data = &input;
- ret = catpt_dsp_send_msg(cdev, request, NULL);
- if (ret)
- dev_err(cdev->dev, "set stream %d volume failed: %d\n",
- stream_hw_id, ret);
-
- return ret;
+ return catpt_dsp_send_msg(cdev, request, NULL, "set stream volume");
}
struct catpt_set_write_pos_input {
@@ -275,7 +221,6 @@ int catpt_ipc_set_write_pos(struct catpt_dev *cdev, u8 stream_hw_id,
union catpt_stream_msg msg = CATPT_STAGE_MSG(SET_WRITE_POSITION);
struct catpt_ipc_msg request;
struct catpt_set_write_pos_input input;
- int ret;
msg.stream_hw_id = stream_hw_id;
input.new_write_pos = pos;
@@ -286,28 +231,18 @@ int catpt_ipc_set_write_pos(struct catpt_dev *cdev, u8 stream_hw_id,
request.size = sizeof(input);
request.data = &input;
- ret = catpt_dsp_send_msg(cdev, request, NULL);
- if (ret)
- dev_err(cdev->dev, "set stream %d write pos failed: %d\n",
- stream_hw_id, ret);
-
- return ret;
+ return catpt_dsp_send_msg(cdev, request, NULL, "set stream write pos");
}
int catpt_ipc_mute_loopback(struct catpt_dev *cdev, u8 stream_hw_id, bool mute)
{
union catpt_stream_msg msg = CATPT_STAGE_MSG(MUTE_LOOPBACK);
struct catpt_ipc_msg request;
- int ret;
msg.stream_hw_id = stream_hw_id;
request.header = msg.val;
request.size = sizeof(mute);
request.data = &mute;
- ret = catpt_dsp_send_msg(cdev, request, NULL);
- if (ret)
- dev_err(cdev->dev, "mute loopback failed: %d\n", ret);
-
- return ret;
+ return catpt_dsp_send_msg(cdev, request, NULL, "mute loopback");
}
diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c
index abd1cb07c60c..2c3405686f79 100644
--- a/sound/soc/intel/catpt/pcm.c
+++ b/sound/soc/intel/catpt/pcm.c
@@ -114,14 +114,10 @@ catpt_stream_find(struct catpt_dev *cdev, u8 stream_hw_id)
return result;
}
-static u32 catpt_stream_read_position(struct catpt_dev *cdev,
- struct catpt_stream_runtime *stream)
+static void catpt_stream_read_position(struct catpt_dev *cdev,
+ struct catpt_stream_runtime *stream, u32 *pos)
{
- u32 pos;
-
- memcpy_fromio(&pos, cdev->lpe_ba + stream->info.read_pos_regaddr,
- sizeof(pos));
- return pos;
+ memcpy_fromio(pos, cdev->lpe_ba + stream->info.read_pos_regaddr, sizeof(*pos));
}
static u32 catpt_stream_volume(struct catpt_dev *cdev,
@@ -365,9 +361,7 @@ static int catpt_dai_apply_usettings(struct snd_soc_dai *dai,
if (stream->template->type != CATPT_STRM_TYPE_LOOPBACK)
return catpt_set_dspvol(cdev, id, (long *)pos->private_value);
ret = catpt_ipc_mute_loopback(cdev, id, *(bool *)pos->private_value);
- if (ret)
- return CATPT_IPC_ERROR(ret);
- return 0;
+ return CATPT_IPC_RET(ret);
}
static int catpt_dai_hw_params(struct snd_pcm_substream *substream,
@@ -414,7 +408,7 @@ static int catpt_dai_hw_params(struct snd_pcm_substream *substream,
cdev->scratch,
&stream->info);
if (ret)
- return CATPT_IPC_ERROR(ret);
+ return CATPT_IPC_RET(ret);
ret = catpt_dai_apply_usettings(dai, stream);
if (ret) {
@@ -456,11 +450,11 @@ static int catpt_dai_prepare(struct snd_pcm_substream *substream,
ret = catpt_ipc_reset_stream(cdev, stream->info.stream_hw_id);
if (ret)
- return CATPT_IPC_ERROR(ret);
+ return CATPT_IPC_RET(ret);
ret = catpt_ipc_pause_stream(cdev, stream->info.stream_hw_id);
if (ret)
- return CATPT_IPC_ERROR(ret);
+ return CATPT_IPC_RET(ret);
stream->prepared = true;
return 0;
@@ -491,7 +485,7 @@ static int catpt_dai_trigger(struct snd_pcm_substream *substream, int cmd,
ret = catpt_ipc_set_write_pos(cdev, stream->info.stream_hw_id,
pos, false, false);
if (ret)
- return CATPT_IPC_ERROR(ret);
+ return CATPT_IPC_RET(ret);
fallthrough;
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
@@ -499,7 +493,7 @@ static int catpt_dai_trigger(struct snd_pcm_substream *substream, int cmd,
catpt_dsp_update_lpclock(cdev);
ret = catpt_ipc_resume_stream(cdev, stream->info.stream_hw_id);
if (ret)
- return CATPT_IPC_ERROR(ret);
+ return CATPT_IPC_RET(ret);
break;
case SNDRV_PCM_TRIGGER_STOP:
@@ -510,7 +504,7 @@ static int catpt_dai_trigger(struct snd_pcm_substream *substream, int cmd,
ret = catpt_ipc_pause_stream(cdev, stream->info.stream_hw_id);
catpt_dsp_update_lpclock(cdev);
if (ret)
- return CATPT_IPC_ERROR(ret);
+ return CATPT_IPC_RET(ret);
break;
default:
@@ -617,7 +611,7 @@ catpt_component_pointer(struct snd_soc_component *component,
return 0;
stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
- pos = catpt_stream_read_position(cdev, stream);
+ catpt_stream_read_position(cdev, stream, &pos);
return bytes_to_frames(substream->runtime, pos);
}
@@ -679,7 +673,7 @@ static int catpt_dai_pcm_new(struct snd_soc_pcm_runtime *rtm,
pm_runtime_put_autosuspend(cdev->dev);
if (ret)
- return CATPT_IPC_ERROR(ret);
+ return CATPT_IPC_RET(ret);
/* store device format set for given SSP */
memcpy(&cdev->devfmt[devfmt.iface], &devfmt, sizeof(devfmt));
@@ -693,7 +687,7 @@ static const struct snd_soc_dai_ops catpt_dai_ops = {
static struct snd_soc_dai_driver dai_drivers[] = {
/* FE DAIs */
{
- .name = "System Pin",
+ .name = "System Pin",
.id = CATPT_STRM_TYPE_SYSTEM,
.ops = &catpt_fe_dai_ops,
.playback = {
@@ -716,7 +710,7 @@ static struct snd_soc_dai_driver dai_drivers[] = {
},
},
{
- .name = "Offload0 Pin",
+ .name = "Offload0 Pin",
.id = CATPT_STRM_TYPE_RENDER,
.ops = &catpt_fe_dai_ops,
.playback = {
@@ -730,7 +724,7 @@ static struct snd_soc_dai_driver dai_drivers[] = {
},
},
{
- .name = "Offload1 Pin",
+ .name = "Offload1 Pin",
.id = CATPT_STRM_TYPE_RENDER,
.ops = &catpt_fe_dai_ops,
.playback = {
@@ -744,7 +738,7 @@ static struct snd_soc_dai_driver dai_drivers[] = {
},
},
{
- .name = "Loopback Pin",
+ .name = "Loopback Pin",
.id = CATPT_STRM_TYPE_LOOPBACK,
.ops = &catpt_fe_dai_ops,
.capture = {
@@ -758,7 +752,7 @@ static struct snd_soc_dai_driver dai_drivers[] = {
},
},
{
- .name = "Bluetooth Pin",
+ .name = "Bluetooth Pin",
.id = CATPT_STRM_TYPE_BLUETOOTH_RENDER,
.ops = &catpt_fe_dai_ops,
.playback = {
@@ -849,9 +843,7 @@ static int catpt_set_dspvol(struct catpt_dev *cdev, u8 stream_id, long *ctlvol)
}
}
- if (ret)
- return CATPT_IPC_ERROR(ret);
- return 0;
+ return CATPT_IPC_RET(ret);
}
static int catpt_volume_info(struct snd_kcontrol *kcontrol,
@@ -1041,7 +1033,7 @@ static int catpt_loopback_switch_put(struct snd_kcontrol *kcontrol,
pm_runtime_put_autosuspend(cdev->dev);
if (ret)
- return CATPT_IPC_ERROR(ret);
+ return CATPT_IPC_RET(ret);
*(bool *)kcontrol->private_value = mute;
return 0;
diff --git a/sound/soc/intel/catpt/sysfs.c b/sound/soc/intel/catpt/sysfs.c
index e961e172f9b7..0cb122a4dfd2 100644
--- a/sound/soc/intel/catpt/sysfs.c
+++ b/sound/soc/intel/catpt/sysfs.c
@@ -24,7 +24,7 @@ static ssize_t fw_version_show(struct device *dev,
pm_runtime_put_autosuspend(cdev->dev);
if (ret)
- return CATPT_IPC_ERROR(ret);
+ return CATPT_IPC_RET(ret);
return sysfs_emit(buf, "%d.%d.%d.%d\n", version.type, version.major,
version.minor, version.build);
diff --git a/sound/soc/intel/common/soc-acpi-intel-arl-match.c b/sound/soc/intel/common/soc-acpi-intel-arl-match.c
index 6bf7a6250ddc..c952f7d2b2c0 100644
--- a/sound/soc/intel/common/soc-acpi-intel-arl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-arl-match.c
@@ -45,23 +45,22 @@ static const struct snd_soc_acpi_endpoint spk_3_endpoint = {
.group_id = 1,
};
-/*
- * RT722 is a multi-function codec, three endpoints are created for
- * its headset, amp and dmic functions.
- */
-static const struct snd_soc_acpi_endpoint rt722_endpoints[] = {
+static const struct snd_soc_acpi_endpoint jack_amp_g1_dmic_endpoints[] = {
+ /* Jack Endpoint */
{
.num = 0,
.aggregated = 0,
.group_position = 0,
.group_id = 0,
},
+ /* Amp Endpoint, work as spk_l_endpoint */
{
.num = 1,
- .aggregated = 0,
+ .aggregated = 1,
.group_position = 0,
- .group_id = 0,
+ .group_id = 1,
},
+ /* DMIC Endpoint */
{
.num = 2,
.aggregated = 0,
@@ -229,11 +228,11 @@ static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = {
}
};
-static const struct snd_soc_acpi_adr_device rt722_0_single_adr[] = {
+static const struct snd_soc_acpi_adr_device rt722_0_agg_adr[] = {
{
.adr = 0x000030025D072201ull,
- .num_endpoints = ARRAY_SIZE(rt722_endpoints),
- .endpoints = rt722_endpoints,
+ .num_endpoints = ARRAY_SIZE(jack_amp_g1_dmic_endpoints),
+ .endpoints = jack_amp_g1_dmic_endpoints,
.name_prefix = "rt722"
}
};
@@ -394,8 +393,8 @@ static const struct snd_soc_acpi_link_adr arl_rt711_l0_rt1316_l3[] = {
static const struct snd_soc_acpi_link_adr arl_rt722_l0_rt1320_l2[] = {
{
.mask = BIT(0),
- .num_adr = ARRAY_SIZE(rt722_0_single_adr),
- .adr_d = rt722_0_single_adr,
+ .num_adr = ARRAY_SIZE(rt722_0_agg_adr),
+ .adr_d = rt722_0_agg_adr,
},
{
.mask = BIT(2),
diff --git a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c
index 1270ee21ee72..72c35e73078e 100644
--- a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c
@@ -1013,7 +1013,7 @@ static const struct snd_soc_acpi_adr_device cs42l42_0_adr[] = {
static const struct snd_soc_acpi_adr_device tas2783_0_adr[] = {
{
- .adr = 0x0000380102000001ull,
+ .adr = 0x00003c0102000001ull,
.num_endpoints = 1,
.endpoints = &spk_l_endpoint,
.name_prefix = "tas2783-1"
@@ -1023,6 +1023,18 @@ static const struct snd_soc_acpi_adr_device tas2783_0_adr[] = {
.num_endpoints = 1,
.endpoints = &spk_r_endpoint,
.name_prefix = "tas2783-2"
+ },
+ {
+ .adr = 0x00003d0102000001ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "tas2783-3"
+ },
+ {
+ .adr = 0x00003a0102000001ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "tas2783-4"
}
};
diff --git a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c
index e297c8ecedb7..ddd919847c1f 100644
--- a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c
@@ -356,33 +356,15 @@ static const struct snd_soc_acpi_adr_device rt1320_3_group1_adr[] = {
}
};
-static const struct snd_soc_acpi_adr_device rt721_0_single_adr[] = {
+static const struct snd_soc_acpi_adr_device rt722_0_agg_adr[] = {
{
- .adr = 0x000030025d072101ull,
- .num_endpoints = ARRAY_SIZE(rt_mf_endpoints),
- .endpoints = rt_mf_endpoints,
- .name_prefix = "rt721"
- }
-};
-
-static const struct snd_soc_acpi_adr_device rt721_3_single_adr[] = {
- {
- .adr = 0x000330025d072101ull,
- .num_endpoints = ARRAY_SIZE(rt_mf_endpoints),
- .endpoints = rt_mf_endpoints,
- .name_prefix = "rt721"
+ .adr = 0x000030025d072201ull,
+ .num_endpoints = ARRAY_SIZE(jack_amp_g1_dmic_endpoints),
+ .endpoints = jack_amp_g1_dmic_endpoints,
+ .name_prefix = "rt722"
}
};
-static const struct snd_soc_acpi_link_adr ptl_rt721_l3[] = {
- {
- .mask = BIT(3),
- .num_adr = ARRAY_SIZE(rt721_3_single_adr),
- .adr_d = rt721_3_single_adr,
- },
- {},
-};
-
static const struct snd_soc_acpi_adr_device rt722_0_single_adr[] = {
{
.adr = 0x000030025d072201ull,
@@ -497,15 +479,6 @@ static const struct snd_soc_acpi_link_adr ptl_cs42l43_l2_cs35l56x6_l13[] = {
{}
};
-static const struct snd_soc_acpi_link_adr ptl_rt721_l0[] = {
- {
- .mask = BIT(0),
- .num_adr = ARRAY_SIZE(rt721_0_single_adr),
- .adr_d = rt721_0_single_adr,
- },
- {}
-};
-
static const struct snd_soc_acpi_link_adr ptl_rt722_only[] = {
{
.mask = BIT(0),
@@ -536,8 +509,8 @@ static const struct snd_soc_acpi_link_adr ptl_rt722_l3[] = {
static const struct snd_soc_acpi_link_adr ptl_rt722_l0_rt1320_l23[] = {
{
.mask = BIT(0),
- .num_adr = ARRAY_SIZE(rt722_0_single_adr),
- .adr_d = rt722_0_single_adr,
+ .num_adr = ARRAY_SIZE(rt722_0_agg_adr),
+ .adr_d = rt722_0_agg_adr,
},
{
.mask = BIT(2),
@@ -727,13 +700,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = {
},
{
.link_mask = BIT(0),
- .links = ptl_rt721_l0,
- .drv_name = "sof_sdw",
- .sof_tplg_filename = "sof-ptl-rt721.tplg",
- .get_function_tplg_files = sof_sdw_get_tplg_files,
- },
- {
- .link_mask = BIT(0),
.links = ptl_rt722_only,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-ptl-rt722.tplg",
@@ -756,13 +722,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = {
},
{
.link_mask = BIT(3),
- .links = ptl_rt721_l3,
- .drv_name = "sof_sdw",
- .sof_tplg_filename = "sof-ptl-rt721.tplg",
- .get_function_tplg_files = sof_sdw_get_tplg_files,
- },
- {
- .link_mask = BIT(3),
.links = ptl_rt722_l3,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-ptl-rt722.tplg",
diff --git a/sound/soc/intel/common/soc-acpi-intel-ssp-common.c b/sound/soc/intel/common/soc-acpi-intel-ssp-common.c
index f56f4bfa5187..a12b11f2cd7a 100644
--- a/sound/soc/intel/common/soc-acpi-intel-ssp-common.c
+++ b/sound/soc/intel/common/soc-acpi-intel-ssp-common.c
@@ -65,6 +65,9 @@ static const struct codec_map amps[] = {
CODEC_MAP_ENTRY("RT1019P", "rt1019", RT1019P_ACPI_HID, CODEC_RT1019P),
CODEC_MAP_ENTRY("RT1308", "rt1308", RT1308_ACPI_HID, CODEC_RT1308),
+ /* Texas Instruments */
+ CODEC_MAP_ENTRY("TAS2563", "tas2563", TAS2563_ACPI_HID, CODEC_TAS2563),
+
/*
* Monolithic components
*
diff --git a/sound/soc/mediatek/mt8189/mt8189-nau8825.c b/sound/soc/mediatek/mt8189/mt8189-nau8825.c
index 5ef15ec988be..e849e7a649bc 100644
--- a/sound/soc/mediatek/mt8189/mt8189-nau8825.c
+++ b/sound/soc/mediatek/mt8189/mt8189-nau8825.c
@@ -342,9 +342,10 @@ static const struct snd_soc_ops mt8189_es8326_ops = {
static int mt8189_dumb_amp_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
int ret;
- ret = snd_soc_dapm_new_controls(&card->dapm, mt8189_dumb_spk_widgets,
+ ret = snd_soc_dapm_new_controls(dapm, mt8189_dumb_spk_widgets,
ARRAY_SIZE(mt8189_dumb_spk_widgets));
if (ret) {
dev_err(rtd->dev, "unable to add Dumb Speaker dapm, ret %d\n", ret);
@@ -418,10 +419,11 @@ static int mt8189_headset_codec_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_jack *jack = &soc_card_data->card_data->jacks[MT8189_JACK_HEADSET];
struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
struct mtk_platform_card_data *card_data = soc_card_data->card_data;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
int ret;
int type;
- ret = snd_soc_dapm_new_controls(&card->dapm, mt8189_headset_widgets,
+ ret = snd_soc_dapm_new_controls(dapm, mt8189_headset_widgets,
ARRAY_SIZE(mt8189_headset_widgets));
if (ret) {
dev_err(rtd->dev, "unable to add nau8825 card widget, ret %d\n", ret);
diff --git a/sound/soc/mediatek/mt8195/mt8195-dai-etdm.c b/sound/soc/mediatek/mt8195/mt8195-dai-etdm.c
index 723cab01e72e..5dcc8ed26e00 100644
--- a/sound/soc/mediatek/mt8195/mt8195-dai-etdm.c
+++ b/sound/soc/mediatek/mt8195/mt8195-dai-etdm.c
@@ -2651,14 +2651,9 @@ static void mt8195_dai_etdm_parse_of(struct mtk_base_afe *afe)
etdm_data = afe_priv->dai_priv[dai_id];
- ret = snprintf(prop, sizeof(prop),
- "mediatek,%s-mclk-always-on-rate",
- of_afe_etdms[i].name);
- if (ret < 0) {
- dev_info(afe->dev, "%s snprintf err=%d\n",
- __func__, ret);
- return;
- }
+ scnprintf(prop, sizeof(prop),
+ "mediatek,%s-mclk-always-on-rate",
+ of_afe_etdms[i].name);
ret = of_property_read_u32(of_node, prop, &sel);
if (ret == 0) {
etdm_data->mclk_dir = SND_SOC_CLOCK_OUT;
@@ -2667,24 +2662,14 @@ static void mt8195_dai_etdm_parse_of(struct mtk_base_afe *afe)
__func__, sel);
}
- ret = snprintf(prop, sizeof(prop),
- "mediatek,%s-multi-pin-mode",
- of_afe_etdms[i].name);
- if (ret < 0) {
- dev_info(afe->dev, "%s snprintf err=%d\n",
- __func__, ret);
- return;
- }
+ scnprintf(prop, sizeof(prop),
+ "mediatek,%s-multi-pin-mode",
+ of_afe_etdms[i].name);
etdm_data->data_mode = of_property_read_bool(of_node, prop);
- ret = snprintf(prop, sizeof(prop),
- "mediatek,%s-cowork-source",
- of_afe_etdms[i].name);
- if (ret < 0) {
- dev_info(afe->dev, "%s snprintf err=%d\n",
- __func__, ret);
- return;
- }
+ scnprintf(prop, sizeof(prop),
+ "mediatek,%s-cowork-source",
+ of_afe_etdms[i].name);
ret = of_property_read_u32(of_node, prop, &sel);
if (ret == 0) {
if (sel >= MT8195_AFE_IO_ETDM_NUM) {
@@ -2706,14 +2691,9 @@ static void mt8195_dai_etdm_parse_of(struct mtk_base_afe *afe)
dai_id = ETDM_TO_DAI_ID(i);
etdm_data = afe_priv->dai_priv[dai_id];
- ret = snprintf(prop, sizeof(prop),
- "mediatek,%s-chn-disabled",
- of_afe_etdms[i].name);
- if (ret < 0) {
- dev_info(afe->dev, "%s snprintf err=%d\n",
- __func__, ret);
- return;
- }
+ scnprintf(prop, sizeof(prop),
+ "mediatek,%s-chn-disabled",
+ of_afe_etdms[i].name);
ret = of_property_read_variable_u8_array(of_node, prop,
disable_chn,
1, max_chn);
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index e54abcd39f79..4bd14ae330d5 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -18,7 +18,6 @@ config SND_PXA2XX_SOC_AC97
select AC97_BUS_NEW
select SND_PXA2XX_LIB
select SND_PXA2XX_LIB_AC97
- select SND_SOC_AC97_BUS_NEW
config SND_PXA2XX_SOC_I2S
select SND_PXA2XX_LIB
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c
index 78f50032afc5..109a4958d9c0 100644
--- a/sound/soc/pxa/pxa2xx-ac97.c
+++ b/sound/soc/pxa/pxa2xx-ac97.c
@@ -222,9 +222,7 @@ static int pxa2xx_ac97_dev_probe(struct platform_device *pdev)
{
int ret;
struct ac97_controller *ctrl;
- pxa2xx_audio_ops_t *pdata = pdev->dev.platform_data;
struct resource *regs;
- void **codecs_pdata;
if (pdev->id != -1) {
dev_err(&pdev->dev, "PXA2xx has only one AC97 port.\n");
@@ -247,10 +245,9 @@ static int pxa2xx_ac97_dev_probe(struct platform_device *pdev)
return ret;
}
- codecs_pdata = pdata ? pdata->codec_pdata : NULL;
ctrl = snd_ac97_controller_register(&pxa2xx_ac97_ops, &pdev->dev,
AC97_SLOTS_AVAILABLE_ALL,
- codecs_pdata);
+ NULL);
if (IS_ERR(ctrl))
return PTR_ERR(ctrl);
diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c
index ded49124581b..241c3b4479c6 100644
--- a/sound/soc/qcom/qdsp6/audioreach.c
+++ b/sound/soc/qcom/qdsp6/audioreach.c
@@ -202,6 +202,31 @@ struct apm_display_port_module_intf_cfg {
} __packed;
#define APM_DP_INTF_CFG_PSIZE ALIGN(sizeof(struct apm_display_port_module_intf_cfg), 8)
+struct apm_module_sp_vi_op_mode_cfg {
+ struct apm_module_param_data param_data;
+ struct param_id_sp_vi_op_mode_cfg cfg;
+} __packed;
+
+#define APM_SP_VI_OP_MODE_CFG_PSIZE(ch) ALIGN( \
+ sizeof(struct apm_module_sp_vi_op_mode_cfg) + \
+ (ch) * sizeof(uint32_t), 8)
+
+struct apm_module_sp_vi_ex_mode_cfg {
+ struct apm_module_param_data param_data;
+ struct param_id_sp_vi_ex_mode_cfg cfg;
+} __packed;
+
+#define APM_SP_VI_EX_MODE_CFG_PSIZE ALIGN(sizeof(struct apm_module_sp_vi_ex_mode_cfg), 8)
+
+struct apm_module_sp_vi_channel_map_cfg {
+ struct apm_module_param_data param_data;
+ struct param_id_sp_vi_channel_map_cfg cfg;
+} __packed;
+
+#define APM_SP_VI_CH_MAP_CFG_PSIZE(ch) ALIGN( \
+ sizeof(struct apm_module_sp_vi_channel_map_cfg) + \
+ (ch) * sizeof(uint32_t), 8)
+
static void *__audioreach_alloc_pkt(int payload_size, uint32_t opcode, uint32_t token,
uint32_t src_port, uint32_t dest_port, bool has_cmd_hdr)
{
@@ -284,7 +309,7 @@ void audioreach_set_default_channel_mapping(u8 *ch_map, int num_channels)
EXPORT_SYMBOL_GPL(audioreach_set_default_channel_mapping);
static void apm_populate_container_config(struct apm_container_obj *cfg,
- struct audioreach_container *cont)
+ const struct audioreach_container *cont)
{
/* Container Config */
@@ -314,7 +339,7 @@ static void apm_populate_container_config(struct apm_container_obj *cfg,
}
static void apm_populate_sub_graph_config(struct apm_sub_graph_data *cfg,
- struct audioreach_sub_graph *sg)
+ const struct audioreach_sub_graph *sg)
{
cfg->sub_graph_cfg.sub_graph_id = sg->sub_graph_id;
cfg->sub_graph_cfg.num_sub_graph_prop = APM_SUB_GRAPH_CFG_NPROP;
@@ -336,7 +361,7 @@ static void apm_populate_sub_graph_config(struct apm_sub_graph_data *cfg,
}
static void apm_populate_module_prop_obj(struct apm_mod_prop_obj *obj,
- struct audioreach_module *module)
+ const struct audioreach_module *module)
{
obj->instance_id = module->instance_id;
@@ -348,7 +373,7 @@ static void apm_populate_module_prop_obj(struct apm_mod_prop_obj *obj,
}
static void apm_populate_module_list_obj(struct apm_mod_list_obj *obj,
- struct audioreach_container *container,
+ const struct audioreach_container *container,
int sub_graph_id)
{
struct audioreach_module *module;
@@ -365,9 +390,10 @@ static void apm_populate_module_list_obj(struct apm_mod_list_obj *obj,
}
}
-static void audioreach_populate_graph(struct q6apm *apm, struct audioreach_graph_info *info,
+static void audioreach_populate_graph(struct q6apm *apm,
+ const struct audioreach_graph_info *info,
struct apm_graph_open_params *open,
- struct list_head *sg_list,
+ const struct list_head *sg_list,
int num_sub_graphs)
{
struct apm_mod_conn_list_params *mc_data = open->mod_conn_list_data;
@@ -439,7 +465,8 @@ static void audioreach_populate_graph(struct q6apm *apm, struct audioreach_graph
}
}
-void *audioreach_alloc_graph_pkt(struct q6apm *apm, struct audioreach_graph_info *info)
+void *audioreach_alloc_graph_pkt(struct q6apm *apm,
+ const struct audioreach_graph_info *info)
{
int payload_size, sg_sz, cont_sz, ml_sz, mp_sz, mc_sz;
struct apm_module_param_data *param_data;
@@ -452,7 +479,7 @@ void *audioreach_alloc_graph_pkt(struct q6apm *apm, struct audioreach_graph_info
struct audioreach_module *module;
struct audioreach_sub_graph *sgs;
struct apm_mod_list_obj *mlobj;
- struct list_head *sg_list;
+ const struct list_head *sg_list;
int num_connections = 0;
int num_containers = 0;
int num_sub_graphs = 0;
@@ -605,8 +632,8 @@ int audioreach_graph_send_cmd_sync(struct q6apm_graph *graph, struct gpr_pkt *pk
EXPORT_SYMBOL_GPL(audioreach_graph_send_cmd_sync);
static int audioreach_display_port_set_media_format(struct q6apm_graph *graph,
- struct audioreach_module *module,
- struct audioreach_module_config *cfg)
+ const struct audioreach_module *module,
+ const struct audioreach_module_config *cfg)
{
struct apm_display_port_module_intf_cfg *intf_cfg;
struct apm_module_frame_size_factor_cfg *fs_cfg;
@@ -617,6 +644,7 @@ static int audioreach_display_port_set_media_format(struct q6apm_graph *graph,
int fs_sz = APM_FS_CFG_PSIZE;
int size = ic_sz + ep_sz + fs_sz;
void *p;
+
struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0);
if (IS_ERR(pkt))
return PTR_ERR(pkt);
@@ -661,8 +689,8 @@ static int audioreach_display_port_set_media_format(struct q6apm_graph *graph,
/* LPASS Codec DMA port Module Media Format Setup */
static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph,
- struct audioreach_module *module,
- struct audioreach_module_config *cfg)
+ const struct audioreach_module *module,
+ const struct audioreach_module_config *cfg)
{
struct apm_codec_dma_module_intf_cfg *intf_cfg;
struct apm_module_frame_size_factor_cfg *fs_cfg;
@@ -675,6 +703,7 @@ static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph,
int pm_sz = APM_HW_EP_PMODE_CFG_PSIZE;
int size = ic_sz + ep_sz + fs_sz + pm_sz;
void *p;
+
struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0);
if (IS_ERR(pkt))
return PTR_ERR(pkt);
@@ -726,19 +755,20 @@ static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph,
return q6apm_send_cmd_sync(graph->apm, pkt, 0);
}
-int audioreach_send_u32_param(struct q6apm_graph *graph, struct audioreach_module *module,
+int audioreach_send_u32_param(struct q6apm_graph *graph,
+ const struct audioreach_module *module,
uint32_t param_id, uint32_t param_val)
{
struct apm_module_param_data *param_data;
- struct gpr_pkt *pkt __free(kfree) = NULL;
uint32_t *param;
int payload_size = sizeof(uint32_t) + APM_MODULE_PARAM_DATA_SIZE;
- void *p = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
- if (IS_ERR(p))
+ void *p;
+
+ struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
+ if (IS_ERR(pkt))
return -ENOMEM;
- pkt = p;
- p = p + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+ p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
param_data = p;
param_data->module_instance_id = module->instance_id;
@@ -755,39 +785,41 @@ int audioreach_send_u32_param(struct q6apm_graph *graph, struct audioreach_modul
EXPORT_SYMBOL_GPL(audioreach_send_u32_param);
static int audioreach_sal_limiter_enable(struct q6apm_graph *graph,
- struct audioreach_module *module, bool enable)
+ const struct audioreach_module *module,
+ bool enable)
{
return audioreach_send_u32_param(graph, module, PARAM_ID_SAL_LIMITER_ENABLE, enable);
}
static int audioreach_sal_set_media_format(struct q6apm_graph *graph,
- struct audioreach_module *module,
- struct audioreach_module_config *cfg)
+ const struct audioreach_module *module,
+ const struct audioreach_module_config *cfg)
{
return audioreach_send_u32_param(graph, module, PARAM_ID_SAL_OUTPUT_CFG, cfg->bit_width);
}
static int audioreach_module_enable(struct q6apm_graph *graph,
- struct audioreach_module *module,
+ const struct audioreach_module *module,
bool enable)
{
return audioreach_send_u32_param(graph, module, PARAM_ID_MODULE_ENABLE, enable);
}
static int audioreach_gapless_set_media_format(struct q6apm_graph *graph,
- struct audioreach_module *module,
- struct audioreach_module_config *cfg)
+ const struct audioreach_module *module,
+ const struct audioreach_module_config *cfg)
{
return audioreach_send_u32_param(graph, module, PARAM_ID_EARLY_EOS_DELAY,
EARLY_EOS_DELAY_MS);
}
static int audioreach_set_module_config(struct q6apm_graph *graph,
- struct audioreach_module *module,
- struct audioreach_module_config *cfg)
+ const struct audioreach_module *module,
+ const struct audioreach_module_config *cfg)
{
int size = le32_to_cpu(module->data->size);
void *p;
+
struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0);
if (IS_ERR(pkt))
return PTR_ERR(pkt);
@@ -800,8 +832,8 @@ static int audioreach_set_module_config(struct q6apm_graph *graph,
}
static int audioreach_mfc_set_media_format(struct q6apm_graph *graph,
- struct audioreach_module *module,
- struct audioreach_module_config *cfg)
+ const struct audioreach_module *module,
+ const struct audioreach_module_config *cfg)
{
struct apm_module_param_data *param_data;
struct param_id_mfc_media_format *media_format;
@@ -810,6 +842,7 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph,
APM_MODULE_PARAM_DATA_SIZE;
int i;
void *p;
+
struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
if (IS_ERR(pkt))
return PTR_ERR(pkt);
@@ -834,7 +867,8 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph,
}
static int audioreach_set_compr_media_format(struct media_format *media_fmt_hdr,
- void *p, struct audioreach_module_config *mcfg)
+ void *p,
+ const struct audioreach_module_config *mcfg)
{
struct payload_media_fmt_aac_t *aac_cfg;
struct payload_media_fmt_pcm *mp3_cfg;
@@ -915,20 +949,21 @@ static int audioreach_set_compr_media_format(struct media_format *media_fmt_hdr,
return 0;
}
-int audioreach_compr_set_param(struct q6apm_graph *graph, struct audioreach_module_config *mcfg)
+int audioreach_compr_set_param(struct q6apm_graph *graph,
+ const struct audioreach_module_config *mcfg)
{
struct media_format *header;
int rc;
void *p;
int iid = q6apm_graph_get_rx_shmem_module_iid(graph);
int payload_size = sizeof(struct apm_sh_module_media_fmt_cmd);
+
struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_cmd_pkt(payload_size,
DATA_CMD_WR_SH_MEM_EP_MEDIA_FORMAT,
0, graph->port->id, iid);
if (IS_ERR(pkt))
return -ENOMEM;
-
p = (void *)pkt + GPR_HDR_SIZE;
header = p;
rc = audioreach_set_compr_media_format(header, p, mcfg);
@@ -940,8 +975,8 @@ int audioreach_compr_set_param(struct q6apm_graph *graph, struct audioreach_modu
EXPORT_SYMBOL_GPL(audioreach_compr_set_param);
static int audioreach_i2s_set_media_format(struct q6apm_graph *graph,
- struct audioreach_module *module,
- struct audioreach_module_config *cfg)
+ const struct audioreach_module *module,
+ const struct audioreach_module_config *cfg)
{
struct apm_module_frame_size_factor_cfg *fs_cfg;
struct apm_module_param_data *param_data;
@@ -952,6 +987,7 @@ static int audioreach_i2s_set_media_format(struct q6apm_graph *graph,
int fs_sz = APM_FS_CFG_PSIZE;
int size = ic_sz + ep_sz + fs_sz;
void *p;
+
struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0);
if (IS_ERR(pkt))
return PTR_ERR(pkt);
@@ -1007,12 +1043,13 @@ static int audioreach_i2s_set_media_format(struct q6apm_graph *graph,
}
static int audioreach_logging_set_media_format(struct q6apm_graph *graph,
- struct audioreach_module *module)
+ const struct audioreach_module *module)
{
struct apm_module_param_data *param_data;
struct data_logging_config *cfg;
int size = sizeof(*cfg) + APM_MODULE_PARAM_DATA_SIZE;
void *p;
+
struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0);
if (IS_ERR(pkt))
return PTR_ERR(pkt);
@@ -1035,15 +1072,14 @@ static int audioreach_logging_set_media_format(struct q6apm_graph *graph,
}
static int audioreach_pcm_set_media_format(struct q6apm_graph *graph,
- struct audioreach_module *module,
- struct audioreach_module_config *mcfg)
+ const struct audioreach_module *module,
+ const struct audioreach_module_config *mcfg)
{
struct payload_pcm_output_format_cfg *media_cfg;
uint32_t num_channels = mcfg->num_channels;
struct apm_pcm_module_media_fmt_cmd *cfg;
struct apm_module_param_data *param_data;
int payload_size;
- struct gpr_pkt *pkt __free(kfree) = NULL;
if (num_channels > 4) {
dev_err(graph->dev, "Error: Invalid channels (%d)!\n", num_channels);
@@ -1052,7 +1088,8 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph,
payload_size = APM_PCM_MODULE_FMT_CMD_PSIZE(num_channels);
- pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
+ struct gpr_pkt *pkt __free(kfree) =
+ audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
if (IS_ERR(pkt))
return PTR_ERR(pkt);
@@ -1082,15 +1119,14 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph,
}
static int audioreach_shmem_set_media_format(struct q6apm_graph *graph,
- struct audioreach_module *module,
- struct audioreach_module_config *mcfg)
+ const struct audioreach_module *module,
+ const struct audioreach_module_config *mcfg)
{
uint32_t num_channels = mcfg->num_channels;
struct apm_module_param_data *param_data;
struct payload_media_fmt_pcm *cfg;
struct media_format *header;
int rc, payload_size;
- struct gpr_pkt *pkt __free(kfree) = NULL;
void *p;
if (num_channels > 4) {
@@ -1100,8 +1136,9 @@ static int audioreach_shmem_set_media_format(struct q6apm_graph *graph,
payload_size = APM_SHMEM_FMT_CFG_PSIZE(num_channels) + APM_MODULE_PARAM_DATA_SIZE;
- pkt = audioreach_alloc_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0,
- graph->port->id, module->instance_id);
+ struct gpr_pkt *pkt __free(kfree) =
+ audioreach_alloc_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0,
+ graph->port->id, module->instance_id);
if (IS_ERR(pkt))
return PTR_ERR(pkt);
@@ -1139,7 +1176,8 @@ static int audioreach_shmem_set_media_format(struct q6apm_graph *graph,
return audioreach_graph_send_cmd_sync(graph, pkt, 0);
}
-int audioreach_gain_set_vol_ctrl(struct q6apm *apm, struct audioreach_module *module, int vol)
+int audioreach_gain_set_vol_ctrl(struct q6apm *apm,
+ const struct audioreach_module *module, int vol)
{
struct param_id_vol_ctrl_master_gain *cfg;
struct apm_module_param_data *param_data;
@@ -1164,7 +1202,8 @@ int audioreach_gain_set_vol_ctrl(struct q6apm *apm, struct audioreach_module *mo
}
EXPORT_SYMBOL_GPL(audioreach_gain_set_vol_ctrl);
-static int audioreach_gain_set(struct q6apm_graph *graph, struct audioreach_module *module)
+static int audioreach_gain_set(struct q6apm_graph *graph,
+ const struct audioreach_module *module)
{
struct apm_module_param_data *param_data;
struct apm_gain_module_cfg *cfg;
@@ -1186,8 +1225,96 @@ static int audioreach_gain_set(struct q6apm_graph *graph, struct audioreach_modu
return q6apm_send_cmd_sync(graph->apm, pkt, 0);
}
-int audioreach_set_media_format(struct q6apm_graph *graph, struct audioreach_module *module,
- struct audioreach_module_config *cfg)
+static int audioreach_speaker_protection(struct q6apm_graph *graph,
+ const struct audioreach_module *module,
+ uint32_t operation_mode)
+{
+ return audioreach_send_u32_param(graph, module, PARAM_ID_SP_OP_MODE,
+ operation_mode);
+}
+
+static int audioreach_speaker_protection_vi(struct q6apm_graph *graph,
+ const struct audioreach_module *module,
+ const struct audioreach_module_config *mcfg)
+{
+ u32 num_channels = mcfg->num_channels;
+ struct apm_module_sp_vi_op_mode_cfg *op_cfg;
+ struct apm_module_sp_vi_channel_map_cfg *cm_cfg;
+ struct apm_module_sp_vi_ex_mode_cfg *ex_cfg;
+ int op_sz, cm_sz, ex_sz;
+ struct apm_module_param_data *param_data;
+ int rc, i, payload_size;
+ struct gpr_pkt *pkt;
+ void *p;
+
+ if (num_channels > 2) {
+ dev_err(graph->dev, "Error: Invalid channels (%d)!\n", num_channels);
+ return -EINVAL;
+ }
+
+ op_sz = APM_SP_VI_OP_MODE_CFG_PSIZE(num_channels);
+ /* Channel mapping for Isense and Vsense, thus twice number of speakers. */
+ cm_sz = APM_SP_VI_CH_MAP_CFG_PSIZE(num_channels * 2);
+ ex_sz = APM_SP_VI_EX_MODE_CFG_PSIZE;
+
+ payload_size = op_sz + cm_sz + ex_sz;
+
+ pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ op_cfg = p;
+ param_data = &op_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_SP_VI_OP_MODE_CFG;
+ param_data->param_size = op_sz - APM_MODULE_PARAM_DATA_SIZE;
+
+ op_cfg->cfg.num_channels = num_channels;
+ op_cfg->cfg.operation_mode = PARAM_ID_SP_VI_OP_MODE_NORMAL;
+ p += op_sz;
+
+ cm_cfg = p;
+ param_data = &cm_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_SP_VI_CHANNEL_MAP_CFG;
+ param_data->param_size = cm_sz - APM_MODULE_PARAM_DATA_SIZE;
+
+ cm_cfg->cfg.num_channels = num_channels * 2;
+ for (i = 0; i < num_channels; i++) {
+ /*
+ * Map speakers into Vsense and then Isense of each channel.
+ * E.g. for PCM_CHANNEL_FL and PCM_CHANNEL_FR to:
+ * [1, 2, 3, 4]
+ */
+ cm_cfg->cfg.channel_mapping[2 * i] = (mcfg->channel_map[i] - 1) * 2 + 1;
+ cm_cfg->cfg.channel_mapping[2 * i + 1] = (mcfg->channel_map[i] - 1) * 2 + 2;
+ }
+
+ p += cm_sz;
+
+ ex_cfg = p;
+ param_data = &ex_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_SP_VI_EX_MODE_CFG;
+ param_data->param_size = ex_sz - APM_MODULE_PARAM_DATA_SIZE;
+
+ ex_cfg->cfg.factory_mode = 0;
+
+ rc = q6apm_send_cmd_sync(graph->apm, pkt, 0);
+
+ kfree(pkt);
+
+ return rc;
+}
+
+int audioreach_set_media_format(struct q6apm_graph *graph,
+ const struct audioreach_module *module,
+ const struct audioreach_module_config *cfg)
{
int rc;
@@ -1235,6 +1362,14 @@ int audioreach_set_media_format(struct q6apm_graph *graph, struct audioreach_mod
case MODULE_ID_GAPLESS:
rc = audioreach_gapless_set_media_format(graph, module, cfg);
break;
+ case MODULE_ID_SPEAKER_PROTECTION:
+ rc = audioreach_speaker_protection(graph, module,
+ PARAM_ID_SP_OP_MODE_NORMAL);
+ break;
+ case MODULE_ID_SPEAKER_PROTECTION_VI:
+ rc = audioreach_speaker_protection_vi(graph, module, cfg);
+ break;
+
default:
rc = 0;
}
diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h
index d1b60b36468a..89f172aab8c0 100644
--- a/sound/soc/qcom/qdsp6/audioreach.h
+++ b/sound/soc/qcom/qdsp6/audioreach.h
@@ -31,6 +31,8 @@ struct q6apm_graph;
#define MODULE_ID_MP3_DECODE 0x0700103B
#define MODULE_ID_GAPLESS 0x0700104D
#define MODULE_ID_DISPLAY_PORT_SINK 0x07001069
+#define MODULE_ID_SPEAKER_PROTECTION 0x070010E2
+#define MODULE_ID_SPEAKER_PROTECTION_VI 0x070010E3
#define MODULE_ID_OPUS_DEC 0x07001174
#define APM_CMD_GET_SPF_STATE 0x01001021
@@ -559,6 +561,43 @@ struct data_logging_config {
uint32_t mode;
} __packed;
+/* Speaker Protection */
+#define PARAM_ID_SP_OP_MODE 0x080011e9
+#define PARAM_ID_SP_OP_MODE_NORMAL 0
+#define PARAM_ID_SP_OP_MODE_CALIBRATION 1
+#define PARAM_ID_SP_OP_MODE_FACTORY_TEST 2
+#define PARAM_ID_SP_OP_MODE_VALIDATION 3
+
+struct param_id_sp_op_mode {
+ uint32_t operation_mode;
+} __packed;
+
+/* Speaker Protection VI */
+
+#define PARAM_ID_SP_VI_OP_MODE_CFG 0x080011f4
+#define PARAM_ID_SP_VI_OP_MODE_NORMAL 0
+#define PARAM_ID_SP_VI_OP_MODE_CALIBRATION 1
+#define PARAM_ID_SP_VI_OP_MODE_FACTORY_TEST 2
+#define PARAM_ID_SP_VI_OP_MODE_VALIDATION 3
+struct param_id_sp_vi_op_mode_cfg {
+ uint32_t num_channels;
+ uint32_t operation_mode;
+ uint32_t quick_calibration;
+ uint32_t r0_t0_selection[];
+} __packed;
+
+#define PARAM_ID_SP_VI_EX_MODE_CFG 0x080011ff
+struct param_id_sp_vi_ex_mode_cfg {
+ uint32_t factory_mode;
+} __packed;
+
+#define PARAM_ID_SP_VI_CHANNEL_MAP_CFG 0x08001203
+struct param_id_sp_vi_channel_map_cfg {
+ uint32_t num_channels;
+ /* [ Vsense of ch 1, Isense of ch 1, Vsense of ch 2, Isense of ch 2, ... ] */
+ uint32_t channel_mapping[];
+} __packed;
+
#define PARAM_ID_SAL_OUTPUT_CFG 0x08001016
struct param_id_sal_output_config {
uint32_t bits_per_sample;
@@ -792,8 +831,8 @@ void *audioreach_alloc_apm_pkt(int pkt_size, uint32_t opcode, uint32_t token,
void *audioreach_alloc_pkt(int payload_size, uint32_t opcode,
uint32_t token, uint32_t src_port,
uint32_t dest_port);
-void *audioreach_alloc_graph_pkt(struct q6apm *apm, struct audioreach_graph_info
- *info);
+void *audioreach_alloc_graph_pkt(struct q6apm *apm,
+ const struct audioreach_graph_info *info);
/* Topology specific */
int audioreach_tplg_init(struct snd_soc_component *component);
@@ -809,13 +848,15 @@ int audioreach_send_cmd_sync(struct device *dev, gpr_device_t *gdev, struct gpr_
int audioreach_graph_send_cmd_sync(struct q6apm_graph *graph, struct gpr_pkt *pkt,
uint32_t rsp_opcode);
int audioreach_set_media_format(struct q6apm_graph *graph,
- struct audioreach_module *module,
- struct audioreach_module_config *cfg);
+ const struct audioreach_module *module,
+ const struct audioreach_module_config *cfg);
int audioreach_shared_memory_send_eos(struct q6apm_graph *graph);
int audioreach_gain_set_vol_ctrl(struct q6apm *apm,
- struct audioreach_module *module, int vol);
-int audioreach_send_u32_param(struct q6apm_graph *graph, struct audioreach_module *module,
+ const struct audioreach_module *module, int vol);
+int audioreach_send_u32_param(struct q6apm_graph *graph,
+ const struct audioreach_module *module,
uint32_t param_id, uint32_t param_val);
-int audioreach_compr_set_param(struct q6apm_graph *graph, struct audioreach_module_config *mcfg);
+int audioreach_compr_set_param(struct q6apm_graph *graph,
+ const struct audioreach_module_config *mcfg);
#endif /* __AUDIOREACH_H__ */
diff --git a/sound/soc/qcom/qdsp6/q6adm.c b/sound/soc/qcom/qdsp6/q6adm.c
index 0b8d06ec8b26..40bc44003453 100644
--- a/sound/soc/qcom/qdsp6/q6adm.c
+++ b/sound/soc/qcom/qdsp6/q6adm.c
@@ -186,11 +186,11 @@ static void q6adm_free_copp(struct kref *ref)
kfree(c);
}
-static int q6adm_callback(struct apr_device *adev, struct apr_resp_pkt *data)
+static int q6adm_callback(struct apr_device *adev, const struct apr_resp_pkt *data)
{
- struct aprv2_ibasic_rsp_result_t *result = data->payload;
+ const struct aprv2_ibasic_rsp_result_t *result = data->payload;
int port_idx, copp_idx;
- struct apr_hdr *hdr = &data->hdr;
+ const struct apr_hdr *hdr = &data->hdr;
struct q6copp *copp;
struct q6adm *adm = dev_get_drvdata(&adev->dev);
@@ -331,6 +331,7 @@ static int q6adm_device_open(struct q6adm *adm, struct q6copp *copp,
int afe_port = q6afe_get_port_id(port_id);
struct apr_pkt *pkt;
int ret, pkt_size = APR_HDR_SIZE + sizeof(*open);
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -466,6 +467,7 @@ int q6adm_matrix_map(struct device *dev, int path,
struct q6copp *copp;
int pkt_size = (APR_HDR_SIZE + sizeof(*route) + sizeof(*node) +
(sizeof(uint32_t) * payload_map.num_copps));
+
void *matrix_map __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!matrix_map)
return -ENOMEM;
diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c
index 0b01fc9e13a7..76e14fc1b2b5 100644
--- a/sound/soc/qcom/qdsp6/q6afe.c
+++ b/sound/soc/qcom/qdsp6/q6afe.c
@@ -931,13 +931,11 @@ static void q6afe_port_free(struct kref *ref)
{
struct q6afe_port *port;
struct q6afe *afe;
- unsigned long flags;
port = container_of(ref, struct q6afe_port, refcount);
afe = port->afe;
- spin_lock_irqsave(&afe->port_list_lock, flags);
- list_del(&port->node);
- spin_unlock_irqrestore(&afe->port_list_lock, flags);
+ scoped_guard(spinlock_irqsave, &afe->port_list_lock)
+ list_del(&port->node);
kfree(port->scfg);
kfree(port);
}
@@ -958,11 +956,11 @@ static struct q6afe_port *q6afe_find_port(struct q6afe *afe, int token)
return ret;
}
-static int q6afe_callback(struct apr_device *adev, struct apr_resp_pkt *data)
+static int q6afe_callback(struct apr_device *adev, const struct apr_resp_pkt *data)
{
struct q6afe *afe = dev_get_drvdata(&adev->dev);
- struct aprv2_ibasic_rsp_result_t *res;
- struct apr_hdr *hdr = &data->hdr;
+ const struct aprv2_ibasic_rsp_result_t *res;
+ const struct apr_hdr *hdr = &data->hdr;
struct q6afe_port *port;
if (!data->payload_size)
@@ -1077,6 +1075,7 @@ static int q6afe_set_param(struct q6afe *afe, struct q6afe_port *port,
struct apr_pkt *pkt;
int ret, pkt_size = APR_HDR_SIZE + sizeof(*param) + sizeof(*pdata) + psize;
void *pl;
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -1128,6 +1127,7 @@ static int q6afe_port_set_param_v2(struct q6afe_port *port, void *data,
u16 port_id = port->id;
int ret, pkt_size = APR_HDR_SIZE + sizeof(*param) + sizeof(*pdata) + psize;
void *pl;
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -1277,7 +1277,6 @@ int q6afe_port_stop(struct q6afe_port *port)
int port_id = port->id;
int ret = 0;
int index, pkt_size;
- void *p __free(kfree) = NULL;
index = port->token;
if (index < 0 || index >= AFE_PORT_MAX) {
@@ -1286,7 +1285,7 @@ int q6afe_port_stop(struct q6afe_port *port)
}
pkt_size = APR_HDR_SIZE + sizeof(*stop);
- p = kzalloc(pkt_size, GFP_KERNEL);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -1667,7 +1666,6 @@ int q6afe_port_start(struct q6afe_port *port)
int ret, param_id = port->cfg_type;
struct apr_pkt *pkt;
int pkt_size;
- void *p __free(kfree) = NULL;
ret = q6afe_port_set_param_v2(port, &port->port_cfg, param_id,
AFE_MODULE_AUDIO_DEV_INTERFACE,
@@ -1690,7 +1688,7 @@ int q6afe_port_start(struct q6afe_port *port)
}
pkt_size = APR_HDR_SIZE + sizeof(*start);
- p = kzalloc(pkt_size, GFP_KERNEL);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -1807,8 +1805,8 @@ struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id)
port->cfg_type = cfg_type;
kref_init(&port->refcount);
- guard(spinlock_irqsave)(&afe->port_list_lock);
- list_add_tail(&port->node, &afe->port_list);
+ scoped_guard(spinlock_irqsave, &afe->port_list_lock)
+ list_add_tail(&port->node, &afe->port_list);
return port;
@@ -1834,6 +1832,7 @@ int q6afe_unvote_lpass_core_hw(struct device *dev, uint32_t hw_block_id,
struct apr_pkt *pkt;
int ret = 0;
int pkt_size = APR_HDR_SIZE + sizeof(*vote_cfg);
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -1868,6 +1867,7 @@ int q6afe_vote_lpass_core_hw(struct device *dev, uint32_t hw_block_id,
struct apr_pkt *pkt;
int ret = 0;
int pkt_size = APR_HDR_SIZE + sizeof(*vote_cfg);
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c
index 94cc6376a367..1d5edf285793 100644
--- a/sound/soc/qcom/qdsp6/q6apm.c
+++ b/sound/soc/qcom/qdsp6/q6apm.c
@@ -100,6 +100,7 @@ static int audioreach_graph_mgmt_cmd(struct audioreach_graph *graph, uint32_t op
struct audioreach_sub_graph *sg;
struct q6apm *apm = graph->apm;
int i = 0, payload_size = APM_GRAPH_MGMT_PSIZE(mgmt_cmd, num_sub_graphs);
+
struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(payload_size, opcode, 0);
if (IS_ERR(pkt))
return PTR_ERR(pkt);
@@ -259,7 +260,6 @@ int q6apm_unmap_memory_regions(struct q6apm_graph *graph, unsigned int dir)
{
struct apm_cmd_shared_mem_unmap_regions *cmd;
struct audioreach_graph_data *data;
- struct gpr_pkt *pkt __free(kfree) = NULL;
int rc;
if (dir == SNDRV_PCM_STREAM_PLAYBACK)
@@ -270,8 +270,9 @@ int q6apm_unmap_memory_regions(struct q6apm_graph *graph, unsigned int dir)
if (!data->mem_map_handle)
return 0;
- pkt = audioreach_alloc_apm_pkt(sizeof(*cmd), APM_CMD_SHARED_MEM_UNMAP_REGIONS, dir,
- graph->port->id);
+ struct gpr_pkt *pkt __free(kfree) =
+ audioreach_alloc_apm_pkt(sizeof(*cmd), APM_CMD_SHARED_MEM_UNMAP_REGIONS,
+ dir, graph->port->id);
if (IS_ERR(pkt))
return PTR_ERR(pkt);
@@ -409,6 +410,7 @@ int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts,
struct apm_data_cmd_wr_sh_mem_ep_data_buffer_v2 *write_buffer;
struct audio_buffer *ab;
int iid = q6apm_graph_get_rx_shmem_module_iid(graph);
+
struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_pkt(sizeof(*write_buffer),
DATA_CMD_WR_SH_MEM_EP_DATA_BUFFER_V2,
graph->rx_data.dsp_buf | (len << APM_WRITE_TOKEN_LEN_SHIFT),
@@ -446,6 +448,7 @@ int q6apm_read(struct q6apm_graph *graph)
struct audioreach_graph_data *port;
struct audio_buffer *ab;
int iid = q6apm_graph_get_tx_shmem_module_iid(graph);
+
struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_pkt(sizeof(*read_buffer),
DATA_CMD_RD_SH_MEM_EP_DATA_BUFFER_V2,
graph->tx_data.dsp_buf, graph->port->id, iid);
@@ -487,14 +490,14 @@ int q6apm_get_hw_pointer(struct q6apm_graph *graph, int dir)
}
EXPORT_SYMBOL_GPL(q6apm_get_hw_pointer);
-static int graph_callback(struct gpr_resp_pkt *data, void *priv, int op)
+static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op)
{
struct data_cmd_rsp_rd_sh_mem_ep_data_buffer_done_v2 *rd_done;
struct data_cmd_rsp_wr_sh_mem_ep_data_buffer_done_v2 *done;
struct apm_cmd_rsp_shared_mem_map_regions *rsp;
- struct gpr_ibasic_rsp_result_t *result;
+ const struct gpr_ibasic_rsp_result_t *result;
struct q6apm_graph *graph = priv;
- struct gpr_hdr *hdr = &data->hdr;
+ const struct gpr_hdr *hdr = &data->hdr;
struct device *dev = graph->dev;
uint32_t client_event;
phys_addr_t phys;
@@ -761,13 +764,13 @@ struct audioreach_module *q6apm_find_module_by_mid(struct q6apm_graph *graph, ui
}
-static int apm_callback(struct gpr_resp_pkt *data, void *priv, int op)
+static int apm_callback(const struct gpr_resp_pkt *data, void *priv, int op)
{
gpr_device_t *gdev = priv;
struct q6apm *apm = dev_get_drvdata(&gdev->dev);
struct device *dev = &gdev->dev;
struct gpr_ibasic_rsp_result_t *result;
- struct gpr_hdr *hdr = &data->hdr;
+ const struct gpr_hdr *hdr = &data->hdr;
result = data->payload;
diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c
index e7295b7b2461..1bb295407c60 100644
--- a/sound/soc/qcom/qdsp6/q6asm.c
+++ b/sound/soc/qcom/qdsp6/q6asm.c
@@ -335,7 +335,6 @@ static int __q6asm_memory_unmap(struct audio_client *ac,
struct q6asm *a = dev_get_drvdata(ac->dev->parent);
struct apr_pkt *pkt;
int rc, pkt_size;
- void *p __free(kfree) = NULL;
if (ac->port[dir].mem_map_handle == 0) {
dev_err(ac->dev, "invalid mem handle\n");
@@ -343,7 +342,7 @@ static int __q6asm_memory_unmap(struct audio_client *ac,
}
pkt_size = APR_HDR_SIZE + sizeof(*mem_unmap);
- p = kzalloc(pkt_size, GFP_KERNEL);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -428,7 +427,6 @@ static int __q6asm_memory_map_regions(struct audio_client *ac, int dir,
struct audio_port_data *port = NULL;
struct audio_buffer *ab = NULL;
struct apr_pkt *pkt;
- void *p __free(kfree) = NULL;
unsigned long flags;
uint32_t num_regions, buf_sz;
int i, pkt_size;
@@ -447,7 +445,7 @@ static int __q6asm_memory_map_regions(struct audio_client *ac, int dir,
pkt_size = APR_HDR_SIZE + sizeof(*cmd) +
(sizeof(*mregions) * num_regions);
- p = kzalloc(pkt_size, GFP_KERNEL);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -599,12 +597,12 @@ int q6asm_get_hw_pointer(struct audio_client *ac, unsigned int dir)
EXPORT_SYMBOL_GPL(q6asm_get_hw_pointer);
static int32_t q6asm_stream_callback(struct apr_device *adev,
- struct apr_resp_pkt *data,
+ const struct apr_resp_pkt *data,
int session_id)
{
struct q6asm *q6asm = dev_get_drvdata(&adev->dev);
- struct aprv2_ibasic_rsp_result_t *result;
- struct apr_hdr *hdr = &data->hdr;
+ const struct aprv2_ibasic_rsp_result_t *result;
+ const struct apr_hdr *hdr = &data->hdr;
struct audio_port_data *port;
struct audio_client *ac;
uint32_t client_event = 0;
@@ -638,7 +636,6 @@ static int32_t q6asm_stream_callback(struct apr_device *adev,
client_event = ASM_CLIENT_EVENT_CMD_OUT_FLUSH_DONE;
break;
case ASM_STREAM_CMD_OPEN_WRITE_V3:
- case ASM_DATA_CMD_WRITE_V2:
case ASM_STREAM_CMD_OPEN_READ_V3:
case ASM_STREAM_CMD_OPEN_READWRITE_V2:
case ASM_STREAM_CMD_SET_ENCDEC_PARAM:
@@ -657,8 +654,9 @@ static int32_t q6asm_stream_callback(struct apr_device *adev,
break;
case ASM_DATA_CMD_EOS:
case ASM_DATA_CMD_READ_V2:
+ case ASM_DATA_CMD_WRITE_V2:
/* response as result of close stream */
- break;
+ goto done;
default:
dev_err(ac->dev, "command[0x%x] not expecting rsp\n",
result->opcode);
@@ -744,13 +742,13 @@ done:
}
static int q6asm_srvc_callback(struct apr_device *adev,
- struct apr_resp_pkt *data)
+ const struct apr_resp_pkt *data)
{
struct q6asm *q6asm = dev_get_drvdata(&adev->dev);
struct aprv2_ibasic_rsp_result_t *result;
struct audio_port_data *port;
struct audio_client *ac = NULL;
- struct apr_hdr *hdr = &data->hdr;
+ const struct apr_hdr *hdr = &data->hdr;
struct q6asm *a;
uint32_t sid = 0;
uint32_t dir = 0;
@@ -930,6 +928,7 @@ int q6asm_open_write(struct audio_client *ac, uint32_t stream_id,
struct asm_stream_cmd_open_write_v3 *open;
struct apr_pkt *pkt;
int rc, pkt_size = APR_HDR_SIZE + sizeof(*open);
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -1007,6 +1006,7 @@ static int __q6asm_run(struct audio_client *ac, uint32_t stream_id,
struct asm_session_cmd_run_v2 *run;
struct apr_pkt *pkt;
int rc, pkt_size = APR_HDR_SIZE + sizeof(*run);
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_ATOMIC);
if (!p)
return -ENOMEM;
@@ -1089,6 +1089,7 @@ int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
struct apr_pkt *pkt;
u8 *channel_mapping;
int pkt_size = APR_HDR_SIZE + sizeof(*fmt);
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -1127,6 +1128,7 @@ int q6asm_stream_media_format_block_flac(struct audio_client *ac,
struct asm_flac_fmt_blk_v2 *fmt;
struct apr_pkt *pkt;
int pkt_size = APR_HDR_SIZE + sizeof(*fmt);
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -1158,6 +1160,7 @@ int q6asm_stream_media_format_block_wma_v9(struct audio_client *ac,
struct asm_wmastdv9_fmt_blk_v2 *fmt;
struct apr_pkt *pkt;
int pkt_size = APR_HDR_SIZE + sizeof(*fmt);
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -1190,6 +1193,7 @@ int q6asm_stream_media_format_block_wma_v10(struct audio_client *ac,
struct asm_wmaprov10_fmt_blk_v2 *fmt;
struct apr_pkt *pkt;
int pkt_size = APR_HDR_SIZE + sizeof(*fmt);
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -1223,6 +1227,7 @@ int q6asm_stream_media_format_block_alac(struct audio_client *ac,
struct asm_alac_fmt_blk_v2 *fmt;
struct apr_pkt *pkt;
int pkt_size = APR_HDR_SIZE + sizeof(*fmt);
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -1259,6 +1264,7 @@ int q6asm_stream_media_format_block_ape(struct audio_client *ac,
struct asm_ape_fmt_blk_v2 *fmt;
struct apr_pkt *pkt;
int pkt_size = APR_HDR_SIZE + sizeof(*fmt);
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -1293,6 +1299,7 @@ static int q6asm_stream_remove_silence(struct audio_client *ac, uint32_t stream_
uint32_t *samples;
struct apr_pkt *pkt;
int rc, pkt_size = APR_HDR_SIZE + sizeof(uint32_t);
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_ATOMIC);
if (!p)
return -ENOMEM;
@@ -1351,6 +1358,7 @@ int q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac,
u8 *channel_mapping;
u32 frames_per_buf = 0;
int pkt_size = APR_HDR_SIZE + sizeof(*enc_cfg);
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -1397,6 +1405,7 @@ int q6asm_read(struct audio_client *ac, uint32_t stream_id)
unsigned long flags;
int pkt_size = APR_HDR_SIZE + sizeof(*read);
int rc = 0;
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_ATOMIC);
if (!p)
return -ENOMEM;
@@ -1439,6 +1448,7 @@ static int __q6asm_open_read(struct audio_client *ac, uint32_t stream_id,
struct asm_stream_cmd_open_read_v3 *open;
struct apr_pkt *pkt;
int pkt_size = APR_HDR_SIZE + sizeof(*open);
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -1509,6 +1519,7 @@ int q6asm_write_async(struct audio_client *ac, uint32_t stream_id, uint32_t len,
struct apr_pkt *pkt;
int pkt_size = APR_HDR_SIZE + sizeof(*write);
int rc = 0;
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_ATOMIC);
if (!p)
return -ENOMEM;
diff --git a/sound/soc/qcom/qdsp6/q6core.c b/sound/soc/qcom/qdsp6/q6core.c
index 49cfb32cd209..f4939302b88a 100644
--- a/sound/soc/qcom/qdsp6/q6core.c
+++ b/sound/soc/qcom/qdsp6/q6core.c
@@ -67,11 +67,11 @@ struct q6core {
static struct q6core *g_core;
-static int q6core_callback(struct apr_device *adev, struct apr_resp_pkt *data)
+static int q6core_callback(struct apr_device *adev, const struct apr_resp_pkt *data)
{
struct q6core *core = dev_get_drvdata(&adev->dev);
- struct aprv2_ibasic_rsp_result_t *result;
- struct apr_hdr *hdr = &data->hdr;
+ const struct aprv2_ibasic_rsp_result_t *result;
+ const struct apr_hdr *hdr = &data->hdr;
result = data->payload;
switch (hdr->opcode) {
diff --git a/sound/soc/qcom/qdsp6/q6prm.c b/sound/soc/qcom/qdsp6/q6prm.c
index 0b8fad0bc832..04892fb4423f 100644
--- a/sound/soc/qcom/qdsp6/q6prm.c
+++ b/sound/soc/qcom/qdsp6/q6prm.c
@@ -62,7 +62,6 @@ static int q6prm_set_hw_core_req(struct device *dev, uint32_t hw_block_id, bool
struct prm_cmd_request_hw_core *req;
gpr_device_t *gdev = prm->gdev;
uint32_t opcode, rsp_opcode;
- struct gpr_pkt *pkt __free(kfree) = NULL;
if (enable) {
opcode = PRM_CMD_REQUEST_HW_RSC;
@@ -72,7 +71,8 @@ static int q6prm_set_hw_core_req(struct device *dev, uint32_t hw_block_id, bool
rsp_opcode = PRM_CMD_RSP_RELEASE_HW_RSC;
}
- pkt = audioreach_alloc_cmd_pkt(sizeof(*req), opcode, 0, gdev->svc.id, GPR_PRM_MODULE_IID);
+ struct gpr_pkt *pkt __free(kfree) =
+ audioreach_alloc_cmd_pkt(sizeof(*req), opcode, 0, gdev->svc.id, GPR_PRM_MODULE_IID);
if (IS_ERR(pkt))
return PTR_ERR(pkt);
@@ -111,10 +111,10 @@ static int q6prm_request_lpass_clock(struct device *dev, int clk_id, int clk_att
struct apm_module_param_data *param_data;
struct prm_cmd_request_rsc *req;
gpr_device_t *gdev = prm->gdev;
- struct gpr_pkt *pkt __free(kfree) = NULL;
- pkt = audioreach_alloc_cmd_pkt(sizeof(*req), PRM_CMD_REQUEST_HW_RSC, 0, gdev->svc.id,
- GPR_PRM_MODULE_IID);
+ struct gpr_pkt *pkt __free(kfree) =
+ audioreach_alloc_cmd_pkt(sizeof(*req), PRM_CMD_REQUEST_HW_RSC, 0,
+ gdev->svc.id, GPR_PRM_MODULE_IID);
if (IS_ERR(pkt))
return PTR_ERR(pkt);
@@ -143,10 +143,10 @@ static int q6prm_release_lpass_clock(struct device *dev, int clk_id, int clk_att
struct apm_module_param_data *param_data;
struct prm_cmd_release_rsc *rel;
gpr_device_t *gdev = prm->gdev;
- struct gpr_pkt *pkt __free(kfree) = NULL;
- pkt = audioreach_alloc_cmd_pkt(sizeof(*rel), PRM_CMD_RELEASE_HW_RSC, 0, gdev->svc.id,
- GPR_PRM_MODULE_IID);
+ struct gpr_pkt *pkt __free(kfree) =
+ audioreach_alloc_cmd_pkt(sizeof(*rel), PRM_CMD_RELEASE_HW_RSC, 0,
+ gdev->svc.id, GPR_PRM_MODULE_IID);
if (IS_ERR(pkt))
return PTR_ERR(pkt);
@@ -175,12 +175,12 @@ int q6prm_set_lpass_clock(struct device *dev, int clk_id, int clk_attr, int clk_
}
EXPORT_SYMBOL_GPL(q6prm_set_lpass_clock);
-static int prm_callback(struct gpr_resp_pkt *data, void *priv, int op)
+static int prm_callback(const struct gpr_resp_pkt *data, void *priv, int op)
{
gpr_device_t *gdev = priv;
struct q6prm *prm = dev_get_drvdata(&gdev->dev);
- struct gpr_ibasic_rsp_result_t *result;
- struct gpr_hdr *hdr = &data->hdr;
+ const struct gpr_ibasic_rsp_result_t *result;
+ const struct gpr_hdr *hdr = &data->hdr;
switch (hdr->opcode) {
case PRM_CMD_RSP_REQUEST_HW_RSC:
diff --git a/sound/soc/qcom/qdsp6/topology.c b/sound/soc/qcom/qdsp6/topology.c
index 5ce6edf3305e..2e71eaa90441 100644
--- a/sound/soc/qcom/qdsp6/topology.c
+++ b/sound/soc/qcom/qdsp6/topology.c
@@ -206,15 +206,15 @@ static struct audioreach_module *audioreach_tplg_alloc_module(struct q6apm *apm,
return mod;
}
-static struct snd_soc_tplg_vendor_array *audioreach_get_sg_array(
- struct snd_soc_tplg_private *private)
+static const struct snd_soc_tplg_vendor_array *
+audioreach_get_sg_array(const struct snd_soc_tplg_private *private)
{
- struct snd_soc_tplg_vendor_array *sg_array = NULL;
+ const struct snd_soc_tplg_vendor_array *sg_array = NULL;
bool found = false;
int sz;
for (sz = 0; !found && (sz < le32_to_cpu(private->size)); ) {
- struct snd_soc_tplg_vendor_value_elem *sg_elem;
+ const struct snd_soc_tplg_vendor_value_elem *sg_elem;
int tkn_count = 0;
sg_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz);
@@ -239,15 +239,15 @@ static struct snd_soc_tplg_vendor_array *audioreach_get_sg_array(
return NULL;
}
-static struct snd_soc_tplg_vendor_array *audioreach_get_cont_array(
- struct snd_soc_tplg_private *private)
+static const struct snd_soc_tplg_vendor_array *
+audioreach_get_cont_array(const struct snd_soc_tplg_private *private)
{
- struct snd_soc_tplg_vendor_array *cont_array = NULL;
+ const struct snd_soc_tplg_vendor_array *cont_array = NULL;
bool found = false;
int sz;
for (sz = 0; !found && (sz < le32_to_cpu(private->size)); ) {
- struct snd_soc_tplg_vendor_value_elem *cont_elem;
+ const struct snd_soc_tplg_vendor_value_elem *cont_elem;
int tkn_count = 0;
cont_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz);
@@ -272,15 +272,15 @@ static struct snd_soc_tplg_vendor_array *audioreach_get_cont_array(
return NULL;
}
-static struct snd_soc_tplg_vendor_array *audioreach_get_module_array(
- struct snd_soc_tplg_private *private)
+static const struct snd_soc_tplg_vendor_array *
+audioreach_get_module_array(const struct snd_soc_tplg_private *private)
{
- struct snd_soc_tplg_vendor_array *mod_array = NULL;
+ const struct snd_soc_tplg_vendor_array *mod_array = NULL;
bool found = false;
int sz = 0;
for (sz = 0; !found && (sz < le32_to_cpu(private->size)); ) {
- struct snd_soc_tplg_vendor_value_elem *mod_elem;
+ const struct snd_soc_tplg_vendor_value_elem *mod_elem;
int tkn_count = 0;
mod_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz);
@@ -305,13 +305,13 @@ static struct snd_soc_tplg_vendor_array *audioreach_get_module_array(
return NULL;
}
-static struct audioreach_module_priv_data *audioreach_get_module_priv_data(
- struct snd_soc_tplg_private *private)
+static struct audioreach_module_priv_data *
+audioreach_get_module_priv_data(const struct snd_soc_tplg_private *private)
{
int sz;
for (sz = 0; sz < le32_to_cpu(private->size); ) {
- struct snd_soc_tplg_vendor_array *mod_array;
+ const struct snd_soc_tplg_vendor_array *mod_array;
mod_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz);
if (le32_to_cpu(mod_array->type) == SND_SOC_AR_TPLG_MODULE_CFG_TYPE) {
@@ -334,10 +334,10 @@ static struct audioreach_module_priv_data *audioreach_get_module_priv_data(
}
static struct audioreach_sub_graph *audioreach_parse_sg_tokens(struct q6apm *apm,
- struct snd_soc_tplg_private *private)
+ const struct snd_soc_tplg_private *private)
{
- struct snd_soc_tplg_vendor_value_elem *sg_elem;
- struct snd_soc_tplg_vendor_array *sg_array;
+ const struct snd_soc_tplg_vendor_value_elem *sg_elem;
+ const struct snd_soc_tplg_vendor_array *sg_array;
struct audioreach_graph_info *info = NULL;
int graph_id, sub_graph_id, tkn_count = 0;
struct audioreach_sub_graph *sg;
@@ -392,10 +392,10 @@ static struct audioreach_sub_graph *audioreach_parse_sg_tokens(struct q6apm *apm
static struct audioreach_container *audioreach_parse_cont_tokens(struct q6apm *apm,
struct audioreach_sub_graph *sg,
- struct snd_soc_tplg_private *private)
+ const struct snd_soc_tplg_private *private)
{
- struct snd_soc_tplg_vendor_value_elem *cont_elem;
- struct snd_soc_tplg_vendor_array *cont_array;
+ const struct snd_soc_tplg_vendor_value_elem *cont_elem;
+ const struct snd_soc_tplg_vendor_array *cont_array;
struct audioreach_container *cont;
int container_id, tkn_count = 0;
bool found = false;
@@ -437,7 +437,7 @@ static struct audioreach_container *audioreach_parse_cont_tokens(struct q6apm *a
static struct audioreach_module *audioreach_parse_common_tokens(struct q6apm *apm,
struct audioreach_container *cont,
- struct snd_soc_tplg_private *private,
+ const struct snd_soc_tplg_private *private,
struct snd_soc_dapm_widget *w)
{
uint32_t max_ip_port = 0, max_op_port = 0;
@@ -447,8 +447,8 @@ static struct audioreach_module *audioreach_parse_common_tokens(struct q6apm *ap
uint32_t src_mod_inst_id = 0;
int module_id = 0, instance_id = 0, tkn_count = 0;
- struct snd_soc_tplg_vendor_value_elem *mod_elem;
- struct snd_soc_tplg_vendor_array *mod_array;
+ const struct snd_soc_tplg_vendor_value_elem *mod_elem;
+ const struct snd_soc_tplg_vendor_array *mod_array;
struct audioreach_module *mod = NULL;
uint32_t token;
bool found;
@@ -590,7 +590,7 @@ static struct audioreach_module *audioreach_parse_common_tokens(struct q6apm *ap
static int audioreach_widget_load_module_common(struct snd_soc_component *component,
int index, struct snd_soc_dapm_widget *w,
- struct snd_soc_tplg_dapm_widget *tplg_w)
+ const struct snd_soc_tplg_dapm_widget *tplg_w)
{
struct q6apm *apm = dev_get_drvdata(component->dev);
struct audioreach_container *cont;
@@ -620,10 +620,10 @@ static int audioreach_widget_load_module_common(struct snd_soc_component *compon
static int audioreach_widget_load_enc_dec_cnv(struct snd_soc_component *component,
int index, struct snd_soc_dapm_widget *w,
- struct snd_soc_tplg_dapm_widget *tplg_w)
+ const struct snd_soc_tplg_dapm_widget *tplg_w)
{
- struct snd_soc_tplg_vendor_value_elem *mod_elem;
- struct snd_soc_tplg_vendor_array *mod_array;
+ const struct snd_soc_tplg_vendor_value_elem *mod_elem;
+ const struct snd_soc_tplg_vendor_array *mod_array;
struct audioreach_module *mod;
struct snd_soc_dobj *dobj;
int tkn_count = 0;
@@ -660,9 +660,9 @@ static int audioreach_widget_load_enc_dec_cnv(struct snd_soc_component *componen
}
static int audioreach_widget_log_module_load(struct audioreach_module *mod,
- struct snd_soc_tplg_vendor_array *mod_array)
+ const struct snd_soc_tplg_vendor_array *mod_array)
{
- struct snd_soc_tplg_vendor_value_elem *mod_elem;
+ const struct snd_soc_tplg_vendor_value_elem *mod_elem;
int tkn_count = 0;
mod_elem = mod_array->value;
@@ -690,9 +690,9 @@ static int audioreach_widget_log_module_load(struct audioreach_module *mod,
}
static int audioreach_widget_dma_module_load(struct audioreach_module *mod,
- struct snd_soc_tplg_vendor_array *mod_array)
+ const struct snd_soc_tplg_vendor_array *mod_array)
{
- struct snd_soc_tplg_vendor_value_elem *mod_elem;
+ const struct snd_soc_tplg_vendor_value_elem *mod_elem;
int tkn_count = 0;
mod_elem = mod_array->value;
@@ -719,9 +719,9 @@ static int audioreach_widget_dma_module_load(struct audioreach_module *mod,
}
static int audioreach_widget_i2s_module_load(struct audioreach_module *mod,
- struct snd_soc_tplg_vendor_array *mod_array)
+ const struct snd_soc_tplg_vendor_array *mod_array)
{
- struct snd_soc_tplg_vendor_value_elem *mod_elem;
+ const struct snd_soc_tplg_vendor_value_elem *mod_elem;
int tkn_count = 0;
mod_elem = mod_array->value;
@@ -754,9 +754,9 @@ static int audioreach_widget_i2s_module_load(struct audioreach_module *mod,
}
static int audioreach_widget_dp_module_load(struct audioreach_module *mod,
- struct snd_soc_tplg_vendor_array *mod_array)
+ const struct snd_soc_tplg_vendor_array *mod_array)
{
- struct snd_soc_tplg_vendor_value_elem *mod_elem;
+ const struct snd_soc_tplg_vendor_value_elem *mod_elem;
int tkn_count = 0;
mod_elem = mod_array->value;
@@ -778,9 +778,9 @@ static int audioreach_widget_dp_module_load(struct audioreach_module *mod,
static int audioreach_widget_load_buffer(struct snd_soc_component *component,
int index, struct snd_soc_dapm_widget *w,
- struct snd_soc_tplg_dapm_widget *tplg_w)
+ const struct snd_soc_tplg_dapm_widget *tplg_w)
{
- struct snd_soc_tplg_vendor_array *mod_array;
+ const struct snd_soc_tplg_vendor_array *mod_array;
struct audioreach_module *mod;
struct snd_soc_dobj *dobj;
int ret;
@@ -818,10 +818,10 @@ static int audioreach_widget_load_buffer(struct snd_soc_component *component,
static int audioreach_widget_load_mixer(struct snd_soc_component *component,
int index, struct snd_soc_dapm_widget *w,
- struct snd_soc_tplg_dapm_widget *tplg_w)
+ const struct snd_soc_tplg_dapm_widget *tplg_w)
{
- struct snd_soc_tplg_vendor_value_elem *w_elem;
- struct snd_soc_tplg_vendor_array *w_array;
+ const struct snd_soc_tplg_vendor_value_elem *w_elem;
+ const struct snd_soc_tplg_vendor_array *w_array;
struct snd_ar_control *scontrol;
struct q6apm *data = dev_get_drvdata(component->dev);
struct snd_soc_dobj *dobj;
@@ -886,7 +886,7 @@ static const struct snd_soc_tplg_widget_events audioreach_widget_ops[] = {
static int audioreach_widget_load_pga(struct snd_soc_component *component,
int index, struct snd_soc_dapm_widget *w,
- struct snd_soc_tplg_dapm_widget *tplg_w)
+ const struct snd_soc_tplg_dapm_widget *tplg_w)
{
struct audioreach_module *mod;
struct snd_soc_dobj *dobj;
@@ -947,7 +947,7 @@ static int audioreach_widget_ready(struct snd_soc_component *component,
static int audioreach_widget_unload(struct snd_soc_component *scomp,
struct snd_soc_dobj *dobj)
{
- struct snd_soc_dapm_widget *w = container_of(dobj, struct snd_soc_dapm_widget, dobj);
+ const struct snd_soc_dapm_widget *w = container_of(dobj, struct snd_soc_dapm_widget, dobj);
struct q6apm *apm = dev_get_drvdata(scomp->dev);
struct audioreach_container *cont;
struct audioreach_module *mod;
@@ -1032,7 +1032,7 @@ static struct audioreach_module *audioreach_find_module(struct snd_soc_component
static int audioreach_route_load(struct snd_soc_component *scomp, int index,
struct snd_soc_dapm_route *route)
{
- struct audioreach_module *src_module, *sink_module;
+ const struct audioreach_module *src_module, *sink_module;
struct snd_ar_control *control;
struct snd_soc_dapm_widget *w;
int i;
@@ -1098,8 +1098,8 @@ static int audioreach_link_load(struct snd_soc_component *component, int index,
}
static void audioreach_connect_sub_graphs(struct q6apm *apm,
- struct snd_ar_control *m1,
- struct snd_ar_control *m2,
+ const struct snd_ar_control *m1,
+ const struct snd_ar_control *m2,
bool connect)
{
struct audioreach_graph_info *info;
@@ -1123,10 +1123,10 @@ static void audioreach_connect_sub_graphs(struct q6apm *apm,
}
static bool audioreach_is_vmixer_connected(struct q6apm *apm,
- struct snd_ar_control *m1,
- struct snd_ar_control *m2)
+ const struct snd_ar_control *m1,
+ const struct snd_ar_control *m2)
{
- struct audioreach_graph_info *info;
+ const struct audioreach_graph_info *info;
mutex_lock(&apm->lock);
info = idr_find(&apm->graph_info_idr, m2->graph_id);
@@ -1144,10 +1144,10 @@ static int audioreach_get_audio_mixer(struct snd_kcontrol *kcontrol,
{
struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value;
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
- struct snd_soc_dapm_widget *dw = snd_soc_dapm_kcontrol_to_widget(kcontrol);
+ const struct snd_soc_dapm_widget *dw = snd_soc_dapm_kcontrol_to_widget(kcontrol);
struct snd_soc_component *c = snd_soc_dapm_to_component(dapm);
- struct snd_ar_control *dapm_scontrol = dw->dobj.private;
- struct snd_ar_control *scontrol = mc->dobj.private;
+ const struct snd_ar_control *dapm_scontrol = dw->dobj.private;
+ const struct snd_ar_control *scontrol = mc->dobj.private;
struct q6apm *data = dev_get_drvdata(c->dev);
bool connected;
@@ -1167,8 +1167,8 @@ static int audioreach_put_audio_mixer(struct snd_kcontrol *kcontrol,
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
struct snd_soc_dapm_widget *dw = snd_soc_dapm_kcontrol_to_widget(kcontrol);
struct snd_soc_component *c = snd_soc_dapm_to_component(dapm);
- struct snd_ar_control *dapm_scontrol = dw->dobj.private;
- struct snd_ar_control *scontrol = mc->dobj.private;
+ const struct snd_ar_control *dapm_scontrol = dw->dobj.private;
+ const struct snd_ar_control *scontrol = mc->dobj.private;
struct q6apm *data = dev_get_drvdata(c->dev);
if (ucontrol->value.integer.value[0]) {
@@ -1206,14 +1206,14 @@ static int audioreach_put_vol_ctrl_audio_mixer(struct snd_kcontrol *kcontrol,
static int audioreach_control_load_mix(struct snd_soc_component *scomp,
struct snd_ar_control *scontrol,
struct snd_kcontrol_new *kc,
- struct snd_soc_tplg_ctl_hdr *hdr)
+ const struct snd_soc_tplg_ctl_hdr *hdr)
{
- struct snd_soc_tplg_vendor_value_elem *c_elem;
- struct snd_soc_tplg_vendor_array *c_array;
- struct snd_soc_tplg_mixer_control *mc;
+ const struct snd_soc_tplg_vendor_value_elem *c_elem;
+ const struct snd_soc_tplg_vendor_array *c_array;
+ const struct snd_soc_tplg_mixer_control *mc;
int tkn_count = 0;
- mc = container_of(hdr, struct snd_soc_tplg_mixer_control, hdr);
+ mc = container_of_const(hdr, struct snd_soc_tplg_mixer_control, hdr);
c_array = (struct snd_soc_tplg_vendor_array *)mc->priv.data;
c_elem = c_array->value;
diff --git a/sound/soc/renesas/rz-ssi.c b/sound/soc/renesas/rz-ssi.c
index f4dc2f68dead..39aa865bdca3 100644
--- a/sound/soc/renesas/rz-ssi.c
+++ b/sound/soc/renesas/rz-ssi.c
@@ -38,6 +38,7 @@
#define SSICR_MST BIT(14)
#define SSICR_BCKP BIT(13)
#define SSICR_LRCKP BIT(12)
+#define SSICR_PDTA BIT(9)
#define SSICR_CKDV(x) (((x) & 0xf) << 4)
#define SSICR_TEN BIT(1)
#define SSICR_REN BIT(0)
@@ -74,7 +75,8 @@
#define PREALLOC_BUFFER_MAX (SZ_32K)
#define SSI_RATES SNDRV_PCM_RATE_8000_48000 /* 8k-48kHz */
-#define SSI_FMTS SNDRV_PCM_FMTBIT_S16_LE
+#define SSI_FMTS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
#define SSI_CHAN_MIN 2
#define SSI_CHAN_MAX 2
#define SSI_FIFO_DEPTH 32
@@ -178,12 +180,7 @@ static inline bool rz_ssi_stream_is_play(struct snd_pcm_substream *substream)
static inline struct rz_ssi_stream *
rz_ssi_stream_get(struct rz_ssi_priv *ssi, struct snd_pcm_substream *substream)
{
- struct rz_ssi_stream *stream = &ssi->playback;
-
- if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
- stream = &ssi->capture;
-
- return stream;
+ return (ssi->playback.substream == substream) ? &ssi->playback : &ssi->capture;
}
static inline bool rz_ssi_is_dma_enabled(struct rz_ssi_priv *ssi)
@@ -294,11 +291,27 @@ static int rz_ssi_clk_setup(struct rz_ssi_priv *ssi, unsigned int rate,
}
/*
- * DWL: Data Word Length = 16 bits
+ * DWL: Data Word Length = {16, 24, 32} bits
* SWL: System Word Length = 32 bits
*/
ssicr |= SSICR_CKDV(clk_ckdv);
- ssicr |= SSICR_DWL(1) | SSICR_SWL(3);
+ switch (ssi->hw_params_cache.sample_width) {
+ case 16:
+ ssicr |= SSICR_DWL(1);
+ break;
+ case 24:
+ ssicr |= SSICR_DWL(5) | SSICR_PDTA;
+ break;
+ case 32:
+ ssicr |= SSICR_DWL(6);
+ break;
+ default:
+ dev_err(ssi->dev, "Not support %u data width",
+ ssi->hw_params_cache.sample_width);
+ return -EINVAL;
+ }
+
+ ssicr |= SSICR_SWL(3);
rz_ssi_reg_writel(ssi, SSICR, ssicr);
rz_ssi_reg_writel(ssi, SSIFCR, SSIFCR_AUCKE | SSIFCR_FIFO_RST);
@@ -455,7 +468,6 @@ static int rz_ssi_pio_recv(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm)
{
struct snd_pcm_substream *substream = strm->substream;
struct snd_pcm_runtime *runtime;
- u16 *buf;
int fifo_samples;
int frames_left;
int samples;
@@ -490,12 +502,23 @@ static int rz_ssi_pio_recv(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm)
break;
/* calculate new buffer index */
- buf = (u16 *)runtime->dma_area;
- buf += strm->buffer_pos * runtime->channels;
+ if (ssi->hw_params_cache.sample_width == 16) {
+ u16 *buf;
- /* Note, only supports 16-bit samples */
- for (i = 0; i < samples; i++)
- *buf++ = (u16)(rz_ssi_reg_readl(ssi, SSIFRDR) >> 16);
+ buf = (u16 *)runtime->dma_area;
+ buf += strm->buffer_pos * runtime->channels;
+
+ for (i = 0; i < samples; i++)
+ *buf++ = (u16)(rz_ssi_reg_readl(ssi, SSIFRDR) >> 16);
+ } else {
+ u32 *buf;
+
+ buf = (u32 *)runtime->dma_area;
+ buf += strm->buffer_pos * runtime->channels;
+
+ for (i = 0; i < samples; i++)
+ *buf++ = rz_ssi_reg_readl(ssi, SSIFRDR);
+ }
rz_ssi_reg_mask_setl(ssi, SSIFSR, SSIFSR_RDF, 0);
rz_ssi_pointer_update(strm, samples / runtime->channels);
@@ -513,7 +536,6 @@ static int rz_ssi_pio_send(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm)
int frames_left;
int i;
u32 ssifsr;
- u16 *buf;
if (!rz_ssi_stream_is_valid(ssi, strm))
return -EINVAL;
@@ -542,12 +564,23 @@ static int rz_ssi_pio_send(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm)
return 0;
/* calculate new buffer index */
- buf = (u16 *)(runtime->dma_area);
- buf += strm->buffer_pos * runtime->channels;
+ if (ssi->hw_params_cache.sample_width == 16) {
+ u16 *buf;
- /* Note, only supports 16-bit samples */
- for (i = 0; i < samples; i++)
- rz_ssi_reg_writel(ssi, SSIFTDR, ((u32)(*buf++) << 16));
+ buf = (u16 *)(runtime->dma_area);
+ buf += strm->buffer_pos * runtime->channels;
+
+ for (i = 0; i < samples; i++)
+ rz_ssi_reg_writel(ssi, SSIFTDR, ((u32)(*buf++) << 16));
+ } else {
+ u32 *buf;
+
+ buf = (u32 *)(runtime->dma_area);
+ buf += strm->buffer_pos * runtime->channels;
+
+ for (i = 0; i < samples; i++)
+ rz_ssi_reg_writel(ssi, SSIFTDR, *buf++);
+ }
rz_ssi_reg_mask_setl(ssi, SSIFSR, SSIFSR_TDE, 0);
rz_ssi_pointer_update(strm, samples / runtime->channels);
@@ -571,7 +604,8 @@ static irqreturn_t rz_ssi_interrupt(int irq, void *data)
return IRQ_HANDLED; /* Left over TX/RX interrupt */
if (irq == ssi->irq_int) { /* error or idle */
- bool is_stopped = false;
+ bool is_stopped = !!(ssisr & (SSISR_RUIRQ | SSISR_ROIRQ |
+ SSISR_TUIRQ | SSISR_TOIRQ));
int i, count;
if (rz_ssi_is_dma_enabled(ssi))
@@ -579,9 +613,6 @@ static irqreturn_t rz_ssi_interrupt(int irq, void *data)
else
count = 1;
- if (ssisr & (SSISR_RUIRQ | SSISR_ROIRQ | SSISR_TUIRQ | SSISR_TOIRQ))
- is_stopped = true;
-
if (ssi->capture.substream && is_stopped) {
if (ssisr & SSISR_RUIRQ)
strm_capture->uerr_num++;
@@ -658,8 +689,13 @@ static int rz_ssi_dma_slave_config(struct rz_ssi_priv *ssi,
cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
cfg.dst_addr = ssi->phys + SSIFTDR;
cfg.src_addr = ssi->phys + SSIFRDR;
- cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
- cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ if (ssi->hw_params_cache.sample_width == 16) {
+ cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ } else {
+ cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ }
return dmaengine_slave_config(dma_ch, &cfg);
}
@@ -774,14 +810,6 @@ static int rz_ssi_dma_request(struct rz_ssi_priv *ssi, struct device *dev)
if (!rz_ssi_is_dma_enabled(ssi))
goto no_dma;
- if (ssi->playback.dma_ch &&
- (rz_ssi_dma_slave_config(ssi, ssi->playback.dma_ch, true) < 0))
- goto no_dma;
-
- if (ssi->capture.dma_ch &&
- (rz_ssi_dma_slave_config(ssi, ssi->capture.dma_ch, false) < 0))
- goto no_dma;
-
return 0;
no_dma:
@@ -829,28 +857,31 @@ static int rz_ssi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
if (cmd == SNDRV_PCM_TRIGGER_START)
rz_ssi_stream_init(strm, substream);
- if (ssi->dma_rt) {
- bool is_playback;
+ if (rz_ssi_is_dma_enabled(ssi)) {
+ bool is_playback = rz_ssi_stream_is_play(substream);
+
+ if (ssi->dma_rt)
+ ret = rz_ssi_dma_slave_config(ssi, ssi->playback.dma_ch,
+ is_playback);
+ else
+ ret = rz_ssi_dma_slave_config(ssi, strm->dma_ch,
+ is_playback);
- is_playback = rz_ssi_stream_is_play(substream);
- ret = rz_ssi_dma_slave_config(ssi, ssi->playback.dma_ch,
- is_playback);
/* Fallback to pio */
if (ret < 0) {
ssi->playback.transfer = rz_ssi_pio_send;
ssi->capture.transfer = rz_ssi_pio_recv;
rz_ssi_release_dma_channels(ssi);
+ } else {
+ /* For DMA, queue up multiple DMA descriptors */
+ num_transfer = 4;
}
}
- /* For DMA, queue up multiple DMA descriptors */
- if (rz_ssi_is_dma_enabled(ssi))
- num_transfer = 4;
-
for (i = 0; i < num_transfer; i++) {
ret = strm->transfer(ssi, strm);
if (ret)
- goto done;
+ return ret;
}
ret = rz_ssi_start(ssi, strm);
@@ -866,7 +897,6 @@ static int rz_ssi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
break;
}
-done:
return ret;
}
@@ -982,7 +1012,7 @@ static int rz_ssi_dai_hw_params(struct snd_pcm_substream *substream,
unsigned int rate = params_rate(params);
int ret;
- if (sample_bits != 16) {
+ if (!(sample_bits == 16 || sample_bits == 24 || sample_bits == 32)) {
dev_err(ssi->dev, "Unsupported sample width: %d\n",
sample_bits);
return -EINVAL;
@@ -1119,19 +1149,16 @@ static int rz_ssi_probe(struct platform_device *pdev)
audio_clk = devm_clk_get(dev, "audio_clk1");
if (IS_ERR(audio_clk))
- return dev_err_probe(&pdev->dev, PTR_ERR(audio_clk),
- "no audio clk1");
+ return dev_err_probe(dev, PTR_ERR(audio_clk), "no audio clk1");
ssi->audio_clk_1 = clk_get_rate(audio_clk);
audio_clk = devm_clk_get(dev, "audio_clk2");
if (IS_ERR(audio_clk))
- return dev_err_probe(&pdev->dev, PTR_ERR(audio_clk),
- "no audio clk2");
+ return dev_err_probe(dev, PTR_ERR(audio_clk), "no audio clk2");
ssi->audio_clk_2 = clk_get_rate(audio_clk);
if (!(ssi->audio_clk_1 || ssi->audio_clk_2))
- return dev_err_probe(&pdev->dev, -EINVAL,
- "no audio clk1 or audio clk2");
+ return dev_err_probe(dev, -EINVAL, "no audio clk1 or audio clk2");
ssi->audio_mck = ssi->audio_clk_1 ? ssi->audio_clk_1 : ssi->audio_clk_2;
@@ -1160,7 +1187,7 @@ static int rz_ssi_probe(struct platform_device *pdev)
goto err_release_dma_chs;
}
- ret = devm_request_irq(dev, ssi->irq_int, &rz_ssi_interrupt,
+ ret = devm_request_irq(dev, ssi->irq_int, rz_ssi_interrupt,
0, dev_name(dev), ssi);
if (ret < 0) {
dev_err_probe(dev, ret, "irq request error (int_req)\n");
@@ -1177,7 +1204,7 @@ static int rz_ssi_probe(struct platform_device *pdev)
return ssi->irq_rt;
ret = devm_request_irq(dev, ssi->irq_rt,
- &rz_ssi_interrupt, 0,
+ rz_ssi_interrupt, 0,
dev_name(dev), ssi);
if (ret < 0)
return dev_err_probe(dev, ret,
@@ -1190,14 +1217,14 @@ static int rz_ssi_probe(struct platform_device *pdev)
return ssi->irq_rx;
ret = devm_request_irq(dev, ssi->irq_tx,
- &rz_ssi_interrupt, 0,
+ rz_ssi_interrupt, 0,
dev_name(dev), ssi);
if (ret < 0)
return dev_err_probe(dev, ret,
"irq request error (dma_tx)\n");
ret = devm_request_irq(dev, ssi->irq_rx,
- &rz_ssi_interrupt, 0,
+ rz_ssi_interrupt, 0,
dev_name(dev), ssi);
if (ret < 0)
return dev_err_probe(dev, ret,
@@ -1247,7 +1274,7 @@ static void rz_ssi_remove(struct platform_device *pdev)
static const struct of_device_id rz_ssi_of_match[] = {
{ .compatible = "renesas,rz-ssi", },
- {/* Sentinel */},
+ { /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, rz_ssi_of_match);
diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig
index bd210fafe9fe..391ce2225fde 100644
--- a/sound/soc/rockchip/Kconfig
+++ b/sound/soc/rockchip/Kconfig
@@ -41,6 +41,7 @@ config SND_SOC_ROCKCHIP_SAI
config SND_SOC_ROCKCHIP_SPDIF
tristate "Rockchip SPDIF Device Driver"
+ select SND_PCM_IEC958
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for SPDIF driver for
diff --git a/sound/soc/rockchip/rockchip_sai.c b/sound/soc/rockchip/rockchip_sai.c
index ebdf0056065b..1bf614dbdf4d 100644
--- a/sound/soc/rockchip/rockchip_sai.c
+++ b/sound/soc/rockchip/rockchip_sai.c
@@ -1487,8 +1487,9 @@ static int rockchip_sai_probe(struct platform_device *pdev)
return 0;
err_runtime_suspend:
- /* If we're !CONFIG_PM, we get -ENOSYS and disable manually */
- if (pm_runtime_put(&pdev->dev))
+ if (IS_ENABLED(CONFIG_PM))
+ pm_runtime_put(&pdev->dev);
+ else
rockchip_sai_runtime_suspend(&pdev->dev);
return ret;
diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c
index d365168934dc..581624f2682e 100644
--- a/sound/soc/rockchip/rockchip_spdif.c
+++ b/sound/soc/rockchip/rockchip_spdif.c
@@ -5,10 +5,11 @@
*
* Copyright (c) 2014 Rockchip Electronics Co. Ltd.
* Author: Jianqun <jay.xu@rock-chips.com>
- * Copyright (c) 2015 Collabora Ltd.
+ * Copyright (c) 2015-2026 Collabora Ltd.
* Author: Sjoerd Simons <sjoerd.simons@collabora.co.uk>
*/
+#include <linux/bitfield.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/clk.h>
@@ -16,6 +17,7 @@
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <sound/pcm_params.h>
+#include <sound/pcm_iec958.h>
#include <sound/dmaengine_pcm.h>
#include "rockchip_spdif.h"
@@ -27,7 +29,25 @@ enum rk_spdif_type {
RK_SPDIF_RK3366,
};
-#define RK3288_GRF_SOC_CON2 0x24c
+/*
+ * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+ * CS0: | Mode | d | c | b | a |
+ * CS1: | Category Code |
+ * CS2: | Channel Number | Source Number |
+ * CS3: | Clock Accuracy | Sample Freq |
+ * CS4: | Ori Sample Freq | Word Length |
+ * CS5: | | CGMS-A |
+ * CS6~CS23: Reserved
+ *
+ * a: use of channel status block
+ * b: linear PCM identification: 0 for lpcm, 1 for nlpcm
+ * c: copyright information
+ * d: additional format information
+ */
+#define CS_BYTE 6
+#define CS_FRAME(c) ((c) << 16 | (c))
+
+#define RK3288_GRF_SOC_CON2 0x24c
struct rk_spdif_dev {
struct device *dev;
@@ -40,29 +60,6 @@ struct rk_spdif_dev {
struct regmap *regmap;
};
-static const struct of_device_id rk_spdif_match[] __maybe_unused = {
- { .compatible = "rockchip,rk3066-spdif",
- .data = (void *)RK_SPDIF_RK3066 },
- { .compatible = "rockchip,rk3188-spdif",
- .data = (void *)RK_SPDIF_RK3188 },
- { .compatible = "rockchip,rk3228-spdif",
- .data = (void *)RK_SPDIF_RK3366 },
- { .compatible = "rockchip,rk3288-spdif",
- .data = (void *)RK_SPDIF_RK3288 },
- { .compatible = "rockchip,rk3328-spdif",
- .data = (void *)RK_SPDIF_RK3366 },
- { .compatible = "rockchip,rk3366-spdif",
- .data = (void *)RK_SPDIF_RK3366 },
- { .compatible = "rockchip,rk3368-spdif",
- .data = (void *)RK_SPDIF_RK3366 },
- { .compatible = "rockchip,rk3399-spdif",
- .data = (void *)RK_SPDIF_RK3366 },
- { .compatible = "rockchip,rk3568-spdif",
- .data = (void *)RK_SPDIF_RK3366 },
- {},
-};
-MODULE_DEVICE_TABLE(of, rk_spdif_match);
-
static int rk_spdif_runtime_suspend(struct device *dev)
{
struct rk_spdif_dev *spdif = dev_get_drvdata(dev);
@@ -109,39 +106,63 @@ static int rk_spdif_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
+ unsigned int mclk_rate = clk_get_rate(spdif->mclk);
unsigned int val = SPDIF_CFGR_HALFWORD_ENABLE;
- int srate, mclk;
- int ret;
+ int bmc, div, ret, i;
+ u16 *fc;
+ u8 cs[CS_BYTE];
+
+ ret = snd_pcm_create_iec958_consumer_hw_params(params, cs, sizeof(cs));
+ if (ret < 0)
+ return ret;
- srate = params_rate(params);
- mclk = srate * 128;
+ fc = (u16 *)cs;
+ for (i = 0; i < CS_BYTE / 2; i++)
+ regmap_write(spdif->regmap, SPDIF_CHNSRn(i), CS_FRAME(fc[i]));
+
+ regmap_update_bits(spdif->regmap, SPDIF_CFGR, SPDIF_CFGR_CSE_MASK,
+ SPDIF_CFGR_CSE_EN);
+
+ /* bmc = 128fs */
+ bmc = 128 * params_rate(params);
+ div = DIV_ROUND_CLOSEST(mclk_rate, bmc);
+ val |= SPDIF_CFGR_CLK_DIV(div);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
val |= SPDIF_CFGR_VDW_16;
+ val |= SPDIF_CFGR_ADJ_RIGHT_J;
break;
case SNDRV_PCM_FORMAT_S20_3LE:
val |= SPDIF_CFGR_VDW_20;
+ val |= SPDIF_CFGR_ADJ_RIGHT_J;
break;
case SNDRV_PCM_FORMAT_S24_LE:
val |= SPDIF_CFGR_VDW_24;
+ val |= SPDIF_CFGR_ADJ_RIGHT_J;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ val |= SPDIF_CFGR_VDW_24;
+ val |= SPDIF_CFGR_ADJ_LEFT_J;
break;
default:
return -EINVAL;
}
- /* Set clock and calculate divider */
- ret = clk_set_rate(spdif->mclk, mclk);
- if (ret != 0) {
- dev_err(spdif->dev, "Failed to set module clock rate: %d\n",
- ret);
- return ret;
- }
+ /*
+ * clear MCLK domain logic before setting Fmclk and Fsdo to ensure
+ * that switching between S16_LE and S32_LE audio does not result
+ * in accidential channels swap.
+ */
+ regmap_update_bits(spdif->regmap, SPDIF_CFGR, SPDIF_CFGR_CLR_MASK,
+ SPDIF_CFGR_CLR_EN);
+ udelay(1);
ret = regmap_update_bits(spdif->regmap, SPDIF_CFGR,
SPDIF_CFGR_CLK_DIV_MASK |
- SPDIF_CFGR_HALFWORD_ENABLE |
- SDPIF_CFGR_VDW_MASK, val);
+ SPDIF_CFGR_HALFWORD_MASK |
+ SDPIF_CFGR_VDW_MASK |
+ SPDIF_CFGR_ADJ_MASK, val);
return ret;
}
@@ -157,7 +178,7 @@ static int rk_spdif_trigger(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
ret = regmap_update_bits(spdif->regmap, SPDIF_DMACR,
- SPDIF_DMACR_TDE_ENABLE |
+ SPDIF_DMACR_TDE_MASK |
SPDIF_DMACR_TDL_MASK,
SPDIF_DMACR_TDE_ENABLE |
SPDIF_DMACR_TDL(16));
@@ -166,21 +187,21 @@ static int rk_spdif_trigger(struct snd_pcm_substream *substream,
return ret;
ret = regmap_update_bits(spdif->regmap, SPDIF_XFER,
- SPDIF_XFER_TXS_START,
+ SPDIF_XFER_TXS_MASK,
SPDIF_XFER_TXS_START);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
ret = regmap_update_bits(spdif->regmap, SPDIF_DMACR,
- SPDIF_DMACR_TDE_ENABLE,
+ SPDIF_DMACR_TDE_MASK,
SPDIF_DMACR_TDE_DISABLE);
if (ret != 0)
return ret;
ret = regmap_update_bits(spdif->regmap, SPDIF_XFER,
- SPDIF_XFER_TXS_START,
+ SPDIF_XFER_TXS_MASK,
SPDIF_XFER_TXS_STOP);
break;
default:
@@ -200,7 +221,24 @@ static int rk_spdif_dai_probe(struct snd_soc_dai *dai)
return 0;
}
+static int rk_spdif_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ if (!freq)
+ return 0;
+
+ ret = clk_set_rate(spdif->mclk, freq);
+ if (ret)
+ dev_err(spdif->dev, "Failed to set mclk: %d\n", ret);
+
+ return ret;
+}
+
static const struct snd_soc_dai_ops rk_spdif_dai_ops = {
+ .set_sysclk = rk_spdif_set_sysclk,
.probe = rk_spdif_dai_probe,
.hw_params = rk_spdif_hw_params,
.trigger = rk_spdif_trigger,
@@ -211,14 +249,11 @@ static struct snd_soc_dai_driver rk_spdif_dai = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
- .rates = (SNDRV_PCM_RATE_32000 |
- SNDRV_PCM_RATE_44100 |
- SNDRV_PCM_RATE_48000 |
- SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000),
+ .rates = SNDRV_PCM_RATE_8000_192000,
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S20_3LE |
- SNDRV_PCM_FMTBIT_S24_LE),
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
},
.ops = &rk_spdif_dai_ops,
};
@@ -236,6 +271,9 @@ static bool rk_spdif_wr_reg(struct device *dev, unsigned int reg)
case SPDIF_INTCR:
case SPDIF_XFER:
case SPDIF_SMPDR:
+ case SPDIF_VLDFRn(0) ... SPDIF_VLDFRn(11):
+ case SPDIF_USRDRn(0) ... SPDIF_USRDRn(11):
+ case SPDIF_CHNSRn(0) ... SPDIF_CHNSRn(11):
return true;
default:
return false;
@@ -251,6 +289,9 @@ static bool rk_spdif_rd_reg(struct device *dev, unsigned int reg)
case SPDIF_INTSR:
case SPDIF_XFER:
case SPDIF_SMPDR:
+ case SPDIF_VLDFRn(0) ... SPDIF_VLDFRn(11):
+ case SPDIF_USRDRn(0) ... SPDIF_USRDRn(11):
+ case SPDIF_CHNSRn(0) ... SPDIF_CHNSRn(11):
return true;
default:
return false;
@@ -273,32 +314,38 @@ static const struct regmap_config rk_spdif_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
- .max_register = SPDIF_SMPDR,
+ .max_register = SPDIF_VERSION,
.writeable_reg = rk_spdif_wr_reg,
.readable_reg = rk_spdif_rd_reg,
.volatile_reg = rk_spdif_volatile_reg,
.cache_type = REGCACHE_FLAT,
};
+static void rk_spdif_suspend(void *data)
+{
+ struct device *dev = data;
+
+ if (!pm_runtime_status_suspended(dev))
+ rk_spdif_runtime_suspend(dev);
+}
+
static int rk_spdif_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
+ enum rk_spdif_type spdif_type;
struct rk_spdif_dev *spdif;
- const struct of_device_id *match;
struct resource *res;
void __iomem *regs;
int ret;
- match = of_match_node(rk_spdif_match, np);
- if (match->data == (void *)RK_SPDIF_RK3288) {
+ spdif_type = (uintptr_t) device_get_match_data(&pdev->dev);
+ if (spdif_type == RK_SPDIF_RK3288) {
struct regmap *grf;
grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
- if (IS_ERR(grf)) {
- dev_err(&pdev->dev,
+ if (IS_ERR(grf))
+ return dev_err_probe(&pdev->dev, PTR_ERR(grf),
"rockchip_spdif missing 'rockchip,grf'\n");
- return PTR_ERR(grf);
- }
/* Select the 8 channel SPDIF solution on RK3288 as
* the 2 channel one does not appear to work
@@ -334,55 +381,63 @@ static int rk_spdif_probe(struct platform_device *pdev)
spdif->dev = &pdev->dev;
dev_set_drvdata(&pdev->dev, spdif);
- pm_runtime_enable(&pdev->dev);
+ ret = devm_add_action_or_reset(&pdev->dev, rk_spdif_suspend, &pdev->dev);
+ if (ret)
+ return ret;
+
+ devm_pm_runtime_enable(&pdev->dev);
+
if (!pm_runtime_enabled(&pdev->dev)) {
ret = rk_spdif_runtime_resume(&pdev->dev);
if (ret)
- goto err_pm_runtime;
+ return ret;
}
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Could not register PCM\n");
+
ret = devm_snd_soc_register_component(&pdev->dev,
&rk_spdif_component,
&rk_spdif_dai, 1);
- if (ret) {
- dev_err(&pdev->dev, "Could not register DAI\n");
- goto err_pm_suspend;
- }
-
- ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
- if (ret) {
- dev_err(&pdev->dev, "Could not register PCM\n");
- goto err_pm_suspend;
- }
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Could not register DAI\n");
return 0;
-
-err_pm_suspend:
- if (!pm_runtime_status_suspended(&pdev->dev))
- rk_spdif_runtime_suspend(&pdev->dev);
-err_pm_runtime:
- pm_runtime_disable(&pdev->dev);
-
- return ret;
-}
-
-static void rk_spdif_remove(struct platform_device *pdev)
-{
- pm_runtime_disable(&pdev->dev);
- if (!pm_runtime_status_suspended(&pdev->dev))
- rk_spdif_runtime_suspend(&pdev->dev);
}
static const struct dev_pm_ops rk_spdif_pm_ops = {
RUNTIME_PM_OPS(rk_spdif_runtime_suspend, rk_spdif_runtime_resume, NULL)
};
+static const struct of_device_id rk_spdif_match[] = {
+ { .compatible = "rockchip,rk3066-spdif",
+ .data = (void *)RK_SPDIF_RK3066 },
+ { .compatible = "rockchip,rk3188-spdif",
+ .data = (void *)RK_SPDIF_RK3188 },
+ { .compatible = "rockchip,rk3228-spdif",
+ .data = (void *)RK_SPDIF_RK3366 },
+ { .compatible = "rockchip,rk3288-spdif",
+ .data = (void *)RK_SPDIF_RK3288 },
+ { .compatible = "rockchip,rk3328-spdif",
+ .data = (void *)RK_SPDIF_RK3366 },
+ { .compatible = "rockchip,rk3366-spdif",
+ .data = (void *)RK_SPDIF_RK3366 },
+ { .compatible = "rockchip,rk3368-spdif",
+ .data = (void *)RK_SPDIF_RK3366 },
+ { .compatible = "rockchip,rk3399-spdif",
+ .data = (void *)RK_SPDIF_RK3366 },
+ { .compatible = "rockchip,rk3568-spdif",
+ .data = (void *)RK_SPDIF_RK3366 },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rk_spdif_match);
+
static struct platform_driver rk_spdif_driver = {
.probe = rk_spdif_probe,
- .remove = rk_spdif_remove,
.driver = {
.name = "rockchip-spdif",
- .of_match_table = of_match_ptr(rk_spdif_match),
+ .of_match_table = rk_spdif_match,
.pm = pm_ptr(&rk_spdif_pm_ops),
},
};
diff --git a/sound/soc/rockchip/rockchip_spdif.h b/sound/soc/rockchip/rockchip_spdif.h
index d8be9aae5b19..ec33295e2512 100644
--- a/sound/soc/rockchip/rockchip_spdif.h
+++ b/sound/soc/rockchip/rockchip_spdif.h
@@ -2,7 +2,7 @@
/*
* ALSA SoC Audio Layer - Rockchip SPDIF transceiver driver
*
- * Copyright (c) 2015 Collabora Ltd.
+ * Copyright (c) 2015-2026 Collabora Ltd.
* Author: Sjoerd Simons <sjoerd.simons@collabora.co.uk>
*/
@@ -13,41 +13,50 @@
* CFGR
* transfer configuration register
*/
-#define SPDIF_CFGR_CLK_DIV_SHIFT (16)
-#define SPDIF_CFGR_CLK_DIV_MASK (0xff << SPDIF_CFGR_CLK_DIV_SHIFT)
-#define SPDIF_CFGR_CLK_DIV(x) (x << SPDIF_CFGR_CLK_DIV_SHIFT)
+#define SPDIF_CFGR_CLK_DIV_MASK GENMASK(23, 16)
+#define SPDIF_CFGR_CLK_DIV(x) FIELD_PREP(SPDIF_CFGR_CLK_DIV_MASK, x-1)
-#define SPDIF_CFGR_HALFWORD_SHIFT 2
-#define SPDIF_CFGR_HALFWORD_DISABLE (0 << SPDIF_CFGR_HALFWORD_SHIFT)
-#define SPDIF_CFGR_HALFWORD_ENABLE (1 << SPDIF_CFGR_HALFWORD_SHIFT)
+#define SPDIF_CFGR_CLR_MASK BIT(7)
+#define SPDIF_CFGR_CLR_EN FIELD_PREP(SPDIF_CFGR_CLR_MASK, 1)
+#define SPDIF_CFGR_CLR_DIS FIELD_PREP(SPDIF_CFGR_CLR_MASK, 0)
-#define SPDIF_CFGR_VDW_SHIFT 0
-#define SPDIF_CFGR_VDW(x) (x << SPDIF_CFGR_VDW_SHIFT)
-#define SDPIF_CFGR_VDW_MASK (0xf << SPDIF_CFGR_VDW_SHIFT)
+#define SPDIF_CFGR_CSE_MASK BIT(6)
+#define SPDIF_CFGR_CSE_EN FIELD_PREP(SPDIF_CFGR_CSE_MASK, 1)
+#define SPDIF_CFGR_CSE_DIS FIELD_PREP(SPDIF_CFGR_CSE_MASK, 0)
-#define SPDIF_CFGR_VDW_16 SPDIF_CFGR_VDW(0x0)
-#define SPDIF_CFGR_VDW_20 SPDIF_CFGR_VDW(0x1)
-#define SPDIF_CFGR_VDW_24 SPDIF_CFGR_VDW(0x2)
+#define SPDIF_CFGR_ADJ_MASK BIT(3)
+#define SPDIF_CFGR_ADJ_LEFT_J FIELD_PREP(SPDIF_CFGR_ADJ_MASK, 1)
+#define SPDIF_CFGR_ADJ_RIGHT_J FIELD_PREP(SPDIF_CFGR_ADJ_MASK, 0)
+
+#define SPDIF_CFGR_HALFWORD_MASK BIT(2)
+#define SPDIF_CFGR_HALFWORD_DISABLE FIELD_PREP(SPDIF_CFGR_HALFWORD_MASK, 0)
+#define SPDIF_CFGR_HALFWORD_ENABLE FIELD_PREP(SPDIF_CFGR_HALFWORD_MASK, 1)
+
+#define SDPIF_CFGR_VDW_MASK GENMASK(1, 0)
+#define SPDIF_CFGR_VDW(x) FIELD_PREP(SDPIF_CFGR_VDW_MASK, x)
+
+#define SPDIF_CFGR_VDW_16 SPDIF_CFGR_VDW(0x0)
+#define SPDIF_CFGR_VDW_20 SPDIF_CFGR_VDW(0x1)
+#define SPDIF_CFGR_VDW_24 SPDIF_CFGR_VDW(0x2)
/*
* DMACR
* DMA control register
*/
-#define SPDIF_DMACR_TDE_SHIFT 5
-#define SPDIF_DMACR_TDE_DISABLE (0 << SPDIF_DMACR_TDE_SHIFT)
-#define SPDIF_DMACR_TDE_ENABLE (1 << SPDIF_DMACR_TDE_SHIFT)
+#define SPDIF_DMACR_TDE_MASK BIT(5)
+#define SPDIF_DMACR_TDE_DISABLE FIELD_PREP(SPDIF_DMACR_TDE_MASK, 0)
+#define SPDIF_DMACR_TDE_ENABLE FIELD_PREP(SPDIF_DMACR_TDE_MASK, 1)
-#define SPDIF_DMACR_TDL_SHIFT 0
-#define SPDIF_DMACR_TDL(x) ((x) << SPDIF_DMACR_TDL_SHIFT)
-#define SPDIF_DMACR_TDL_MASK (0x1f << SPDIF_DMACR_TDL_SHIFT)
+#define SPDIF_DMACR_TDL_MASK GENMASK(4, 0)
+#define SPDIF_DMACR_TDL(x) FIELD_PREP(SPDIF_DMACR_TDL_MASK, x)
/*
* XFER
* Transfer control register
*/
-#define SPDIF_XFER_TXS_SHIFT 0
-#define SPDIF_XFER_TXS_STOP (0 << SPDIF_XFER_TXS_SHIFT)
-#define SPDIF_XFER_TXS_START (1 << SPDIF_XFER_TXS_SHIFT)
+#define SPDIF_XFER_TXS_MASK BIT(0)
+#define SPDIF_XFER_TXS_STOP FIELD_PREP(SPDIF_XFER_TXS_MASK, 0)
+#define SPDIF_XFER_TXS_START FIELD_PREP(SPDIF_XFER_TXS_MASK, 1)
#define SPDIF_CFGR (0x0000)
#define SPDIF_SDBLR (0x0004)
@@ -56,5 +65,9 @@
#define SPDIF_INTSR (0x0010)
#define SPDIF_XFER (0x0018)
#define SPDIF_SMPDR (0x0020)
+#define SPDIF_VLDFRn(x) (0x0060 + (x) * 4)
+#define SPDIF_USRDRn(x) (0x0090 + (x) * 4)
+#define SPDIF_CHNSRn(x) (0x00c0 + (x) * 4)
+#define SPDIF_VERSION (0x01c0)
#endif /* _ROCKCHIP_SPDIF_H */
diff --git a/sound/soc/sdca/Kconfig b/sound/soc/sdca/Kconfig
index fabb69a3450d..87ab2895096c 100644
--- a/sound/soc/sdca/Kconfig
+++ b/sound/soc/sdca/Kconfig
@@ -46,12 +46,14 @@ config SND_SOC_SDCA_CLASS
select SND_SOC_SDCA_FDL
select SND_SOC_SDCA_HID
select SND_SOC_SDCA_IRQ
+ select REGMAP_SOUNDWIRE
help
This option enables support for the SDCA Class driver which should
support any class compliant SDCA part.
config SND_SOC_SDCA_CLASS_FUNCTION
tristate
+ select REGMAP_SOUNDWIRE_MBQ
help
This option enables support for the SDCA Class Function drivers,
these implement the individual functions of the SDCA Class driver.
diff --git a/sound/soc/sdca/Makefile b/sound/soc/sdca/Makefile
index f6b73275d964..b3b0f5d94c8d 100644
--- a/sound/soc/sdca/Makefile
+++ b/sound/soc/sdca/Makefile
@@ -3,7 +3,7 @@
snd-soc-sdca-y := sdca_functions.o sdca_device.o sdca_function_device.o \
sdca_regmap.o sdca_asoc.o sdca_ump.o
snd-soc-sdca-$(CONFIG_SND_SOC_SDCA_HID) += sdca_hid.o
-snd-soc-sdca-$(CONFIG_SND_SOC_SDCA_IRQ) += sdca_interrupts.o
+snd-soc-sdca-$(CONFIG_SND_SOC_SDCA_IRQ) += sdca_interrupts.o sdca_jack.o
snd-soc-sdca-$(CONFIG_SND_SOC_SDCA_FDL) += sdca_fdl.o
snd-soc-sdca-class-y := sdca_class.o
diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c
index 2d328bbb95b9..bb6e74e80a3e 100644
--- a/sound/soc/sdca/sdca_asoc.c
+++ b/sound/soc/sdca/sdca_asoc.c
@@ -16,6 +16,7 @@
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/overflow.h>
+#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/soundwire/sdw_registers.h>
#include <linux/string_helpers.h>
@@ -115,6 +116,41 @@ int sdca_asoc_count_component(struct device *dev, struct sdca_function_data *fun
}
EXPORT_SYMBOL_NS(sdca_asoc_count_component, "SND_SOC_SDCA");
+static int ge_put_enum_double(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+ struct device *dev = component->dev;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ unsigned int reg = e->reg;
+ int ret;
+
+ reg &= ~SDW_SDCA_CTL_CSEL(0x3F);
+ reg |= SDW_SDCA_CTL_CSEL(SDCA_CTL_GE_DETECTED_MODE);
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0) {
+ dev_err(dev, "failed to resume writing %s: %d\n",
+ kcontrol->id.name, ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_read(component, reg);
+ pm_runtime_put(dev);
+ if (ret < 0)
+ return ret;
+ else if (ret <= SDCA_DETECTED_MODE_DETECTION_IN_PROGRESS)
+ return -EBUSY;
+
+ ret = snd_soc_enum_item_to_val(e, item[0]);
+ if (ret <= SDCA_DETECTED_MODE_DETECTION_IN_PROGRESS)
+ return -EINVAL;
+
+ return snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+}
+
static int entity_early_parse_ge(struct device *dev,
struct sdca_function_data *function,
struct sdca_entity *entity)
@@ -191,7 +227,7 @@ static int entity_early_parse_ge(struct device *dev,
kctl->name = control_name;
kctl->info = snd_soc_info_enum_double;
kctl->get = snd_soc_dapm_get_enum_double;
- kctl->put = snd_soc_dapm_put_enum_double;
+ kctl->put = ge_put_enum_double;
kctl->private_value = (unsigned long)soc_enum;
entity->ge.kctl = kctl;
@@ -792,6 +828,48 @@ static int control_limit_kctl(struct device *dev,
return 0;
}
+static int volatile_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct device *dev = component->dev;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0) {
+ dev_err(dev, "failed to resume reading %s: %d\n",
+ kcontrol->id.name, ret);
+ return ret;
+ }
+
+ ret = snd_soc_get_volsw(kcontrol, ucontrol);
+
+ pm_runtime_put(dev);
+
+ return ret;
+}
+
+static int volatile_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct device *dev = component->dev;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0) {
+ dev_err(dev, "failed to resume writing %s: %d\n",
+ kcontrol->id.name, ret);
+ return ret;
+ }
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+
+ pm_runtime_put(dev);
+
+ return ret;
+}
+
static int populate_control(struct device *dev,
struct sdca_function_data *function,
struct sdca_entity *entity,
@@ -849,8 +927,13 @@ static int populate_control(struct device *dev,
(*kctl)->private_value = (unsigned long)mc;
(*kctl)->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
(*kctl)->info = snd_soc_info_volsw;
- (*kctl)->get = snd_soc_get_volsw;
- (*kctl)->put = snd_soc_put_volsw;
+ if (control->is_volatile) {
+ (*kctl)->get = volatile_get_volsw;
+ (*kctl)->put = volatile_put_volsw;
+ } else {
+ (*kctl)->get = snd_soc_get_volsw;
+ (*kctl)->put = snd_soc_put_volsw;
+ }
if (readonly_control(control))
(*kctl)->access = SNDRV_CTL_ELEM_ACCESS_READ;
@@ -1478,7 +1561,7 @@ static int set_usage(struct device *dev, struct regmap *regmap,
unsigned int rate = sdca_range(range, SDCA_USAGE_SAMPLE_RATE, i);
unsigned int width = sdca_range(range, SDCA_USAGE_SAMPLE_WIDTH, i);
- if ((!rate || rate == target_rate) && width == target_width) {
+ if ((!rate || rate == target_rate) && (!width || width == target_width)) {
unsigned int usage = sdca_range(range, SDCA_USAGE_NUMBER, i);
unsigned int reg = SDW_SDCA_CTL(function->desc->adr,
entity->id, sel, 0);
diff --git a/sound/soc/sdca/sdca_class.c b/sound/soc/sdca/sdca_class.c
index 349d32933ba8..918b638acb57 100644
--- a/sound/soc/sdca/sdca_class.c
+++ b/sound/soc/sdca/sdca_class.c
@@ -205,6 +205,7 @@ static int class_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id *id
drv->dev = dev;
drv->sdw = sdw;
mutex_init(&drv->regmap_lock);
+ mutex_init(&drv->init_lock);
dev_set_drvdata(drv->dev, drv);
@@ -238,6 +239,38 @@ static int class_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id *id
return 0;
}
+static int class_suspend(struct device *dev)
+{
+ struct sdca_class_drv *drv = dev_get_drvdata(dev);
+ int ret;
+
+ disable_irq(drv->sdw->irq);
+
+ ret = pm_runtime_force_suspend(dev);
+ if (ret) {
+ dev_err(dev, "failed to force suspend: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int class_resume(struct device *dev)
+{
+ struct sdca_class_drv *drv = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret) {
+ dev_err(dev, "failed to force resume: %d\n", ret);
+ return ret;
+ }
+
+ enable_irq(drv->sdw->irq);
+
+ return 0;
+}
+
static int class_runtime_suspend(struct device *dev)
{
struct sdca_class_drv *drv = dev_get_drvdata(dev);
@@ -278,6 +311,7 @@ err:
}
static const struct dev_pm_ops class_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(class_suspend, class_resume)
RUNTIME_PM_OPS(class_runtime_suspend, class_runtime_resume, NULL)
};
diff --git a/sound/soc/sdca/sdca_class.h b/sound/soc/sdca/sdca_class.h
index bb4c9dd12429..6f24ea2bbd38 100644
--- a/sound/soc/sdca/sdca_class.h
+++ b/sound/soc/sdca/sdca_class.h
@@ -28,6 +28,8 @@ struct sdca_class_drv {
struct sdca_interrupt_info *irq_info;
struct mutex regmap_lock;
+ /* Serialise function initialisations */
+ struct mutex init_lock;
struct work_struct boot_work;
struct completion device_attach;
diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c
index 0028482a1e75..98fd3fd1052b 100644
--- a/sound/soc/sdca/sdca_class_function.c
+++ b/sound/soc/sdca/sdca_class_function.c
@@ -8,6 +8,7 @@
*/
#include <linux/auxiliary_bus.h>
+#include <linux/cleanup.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/pm.h>
@@ -19,6 +20,7 @@
#include <sound/sdca_fdl.h>
#include <sound/sdca_function.h>
#include <sound/sdca_interrupts.h>
+#include <sound/sdca_jack.h>
#include <sound/sdca_regmap.h>
#include <sound/sdw.h>
#include <sound/soc-component.h>
@@ -32,6 +34,7 @@ struct class_function_drv {
struct sdca_class_drv *core;
struct sdca_function_data *function;
+ bool suspended;
};
static void class_function_regmap_lock(void *data)
@@ -195,26 +198,26 @@ static int class_function_component_probe(struct snd_soc_component *component)
return sdca_irq_populate(drv->function, component, core->irq_info);
}
+static int class_function_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *d)
+{
+ struct class_function_drv *drv = snd_soc_component_get_drvdata(component);
+ struct sdca_class_drv *core = drv->core;
+
+ return sdca_jack_set_jack(core->irq_info, jack);
+}
+
static const struct snd_soc_component_driver class_function_component_drv = {
.probe = class_function_component_probe,
.endianness = 1,
};
-static int class_function_boot(struct class_function_drv *drv)
+static int class_function_init_device(struct class_function_drv *drv,
+ unsigned int status)
{
- unsigned int reg = SDW_SDCA_CTL(drv->function->desc->adr,
- SDCA_ENTITY_TYPE_ENTITY_0,
- SDCA_CTL_ENTITY_0_FUNCTION_STATUS, 0);
- unsigned int val;
int ret;
- ret = regmap_read(drv->regmap, reg, &val);
- if (ret < 0) {
- dev_err(drv->dev, "failed to read function status: %d\n", ret);
- return ret;
- }
-
- if (!(val & SDCA_CTL_ENTITY_0_FUNCTION_HAS_BEEN_RESET)) {
+ if (!(status & SDCA_CTL_ENTITY_0_FUNCTION_HAS_BEEN_RESET)) {
dev_dbg(drv->dev, "reset function device\n");
ret = sdca_reset_function(drv->dev, drv->function, drv->regmap);
@@ -222,24 +225,38 @@ static int class_function_boot(struct class_function_drv *drv)
return ret;
}
- if (val & SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION) {
+ if (status & SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION) {
dev_dbg(drv->dev, "write initialisation\n");
ret = sdca_regmap_write_init(drv->dev, drv->core->dev_regmap,
drv->function);
if (ret)
return ret;
+ }
- ret = regmap_write(drv->regmap, reg,
- SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION);
- if (ret < 0) {
- dev_err(drv->dev,
- "failed to clear function init status: %d\n",
- ret);
- return ret;
- }
+ return 0;
+}
+
+static int class_function_boot(struct class_function_drv *drv)
+{
+ unsigned int reg = SDW_SDCA_CTL(drv->function->desc->adr,
+ SDCA_ENTITY_TYPE_ENTITY_0,
+ SDCA_CTL_ENTITY_0_FUNCTION_STATUS, 0);
+ unsigned int val;
+ int ret;
+
+ guard(mutex)(&drv->core->init_lock);
+
+ ret = regmap_read(drv->regmap, reg, &val);
+ if (ret < 0) {
+ dev_err(drv->dev, "failed to read function status: %d\n", ret);
+ return ret;
}
+ ret = class_function_init_device(drv, val);
+ if (ret)
+ return ret;
+
/* Start FDL process */
ret = sdca_irq_populate_early(drv->dev, drv->regmap, drv->function,
drv->core->irq_info);
@@ -351,12 +368,17 @@ static int class_function_probe(struct auxiliary_device *auxdev,
return dev_err_probe(dev, PTR_ERR(drv->regmap),
"failed to create regmap");
+ if (desc->type == SDCA_FUNCTION_TYPE_UAJ)
+ cmp_drv->set_jack = class_function_set_jack;
+
ret = sdca_asoc_populate_component(dev, drv->function, cmp_drv,
&dais, &num_dais,
&class_function_sdw_ops);
if (ret)
return ret;
+ dev_pm_set_driver_flags(dev, DPM_FLAG_NO_DIRECT_COMPLETE);
+
pm_runtime_set_autosuspend_delay(dev, 200);
pm_runtime_use_autosuspend(dev);
pm_runtime_set_active(dev);
@@ -401,9 +423,44 @@ static int class_function_runtime_resume(struct device *dev)
struct class_function_drv *drv = auxiliary_get_drvdata(auxdev);
int ret;
+ guard(mutex)(&drv->core->init_lock);
+
regcache_mark_dirty(drv->regmap);
regcache_cache_only(drv->regmap, false);
+ if (drv->suspended) {
+ unsigned int reg = SDW_SDCA_CTL(drv->function->desc->adr,
+ SDCA_ENTITY_TYPE_ENTITY_0,
+ SDCA_CTL_ENTITY_0_FUNCTION_STATUS, 0);
+ unsigned int val;
+
+ ret = regmap_read(drv->regmap, reg, &val);
+ if (ret < 0) {
+ dev_err(drv->dev, "failed to read function status: %d\n", ret);
+ goto err;
+ }
+
+ ret = class_function_init_device(drv, val);
+ if (ret)
+ goto err;
+
+ sdca_irq_enable_early(drv->function, drv->core->irq_info);
+
+ ret = sdca_fdl_sync(drv->dev, drv->function, drv->core->irq_info);
+ if (ret)
+ goto err;
+
+ sdca_irq_enable(drv->function, drv->core->irq_info);
+
+ ret = regmap_write(drv->regmap, reg, 0xFF);
+ if (ret < 0) {
+ dev_err(drv->dev, "failed to clear function status: %d\n", ret);
+ goto err;
+ }
+
+ drv->suspended = false;
+ }
+
ret = regcache_sync(drv->regmap);
if (ret) {
dev_err(drv->dev, "failed to restore register cache: %d\n", ret);
@@ -418,7 +475,49 @@ err:
return ret;
}
+static int class_function_suspend(struct device *dev)
+{
+ struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
+ struct class_function_drv *drv = auxiliary_get_drvdata(auxdev);
+ int ret;
+
+ drv->suspended = true;
+
+ /* Ensure runtime resume runs on resume */
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret) {
+ dev_err(dev, "failed to resume for suspend: %d\n", ret);
+ return ret;
+ }
+
+ sdca_irq_disable(drv->function, drv->core->irq_info);
+
+ ret = pm_runtime_force_suspend(dev);
+ if (ret) {
+ dev_err(dev, "failed to force suspend: %d\n", ret);
+ return ret;
+ }
+
+ pm_runtime_put_noidle(dev);
+
+ return 0;
+}
+
+static int class_function_resume(struct device *dev)
+{
+ int ret;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret) {
+ dev_err(dev, "failed to force resume: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
static const struct dev_pm_ops class_function_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(class_function_suspend, class_function_resume)
RUNTIME_PM_OPS(class_function_runtime_suspend,
class_function_runtime_resume, NULL)
};
diff --git a/sound/soc/sdca/sdca_fdl.c b/sound/soc/sdca/sdca_fdl.c
index 3180ebd07c40..07892bc3a44e 100644
--- a/sound/soc/sdca/sdca_fdl.c
+++ b/sound/soc/sdca/sdca_fdl.c
@@ -256,8 +256,7 @@ static int fdl_load_file(struct sdca_interrupt *interrupt,
tmp->file_length != firmware->size) {
dev_err(dev, "bad disk SWF size\n");
} else if (!swf || swf->file_version <= tmp->file_version) {
- dev_dbg(dev, "using SWF from disk: %x-%x-%x\n",
- tmp->vendor_id, tmp->file_id, tmp->file_version);
+ dev_dbg(dev, "using SWF from disk\n");
swf = tmp;
}
}
@@ -267,6 +266,9 @@ static int fdl_load_file(struct sdca_interrupt *interrupt,
return -ENOENT;
}
+ dev_info(dev, "loading SWF: %x-%x-%x\n",
+ swf->vendor_id, swf->file_id, swf->file_version);
+
ret = sdca_ump_write_message(dev, interrupt->device_regmap,
interrupt->function_regmap,
interrupt->function, interrupt->entity,
@@ -487,7 +489,7 @@ int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt)
struct device *dev = interrupt->dev;
struct fdl_state *fdl_state;
- fdl_state = devm_kzalloc(dev, sizeof(struct fdl_state), GFP_KERNEL);
+ fdl_state = devm_kzalloc(dev, sizeof(*fdl_state), GFP_KERNEL);
if (!fdl_state)
return -ENOMEM;
diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c
index 5a1f120487ef..95b67bb904c3 100644
--- a/sound/soc/sdca/sdca_functions.c
+++ b/sound/soc/sdca/sdca_functions.c
@@ -911,10 +911,38 @@ static int find_sdca_control_value(struct device *dev, struct sdca_entity *entit
return 0;
}
-/*
- * TODO: Add support for -cn- properties, allowing different channels to have
- * different defaults etc.
- */
+static int find_sdca_control_reset(const struct sdca_entity *entity,
+ struct sdca_control *control)
+{
+ switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
+ case SDCA_CTL_TYPE_S(FU, AGC):
+ case SDCA_CTL_TYPE_S(FU, BASS_BOOST):
+ case SDCA_CTL_TYPE_S(FU, LOUDNESS):
+ case SDCA_CTL_TYPE_S(SMPU, TRIGGER_ENABLE):
+ case SDCA_CTL_TYPE_S(GE, SELECTED_MODE):
+ case SDCA_CTL_TYPE_S(TG, TONE_DIVIDER):
+ case SDCA_CTL_TYPE_S(ENTITY_0, COMMIT_GROUP_MASK):
+ control->has_reset = true;
+ control->reset = 0;
+ break;
+ case SDCA_CTL_TYPE_S(XU, BYPASS):
+ case SDCA_CTL_TYPE_S(MFPU, BYPASS):
+ case SDCA_CTL_TYPE_S(FU, MUTE):
+ case SDCA_CTL_TYPE_S(CX, CLOCK_SELECT):
+ control->has_reset = true;
+ control->reset = 1;
+ break;
+ case SDCA_CTL_TYPE_S(PDE, REQUESTED_PS):
+ control->has_reset = true;
+ control->reset = 3;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static int find_sdca_entity_control(struct device *dev, struct sdca_entity *entity,
struct fwnode_handle *control_node,
struct sdca_control *control)
@@ -952,7 +980,7 @@ static int find_sdca_entity_control(struct device *dev, struct sdca_entity *enti
}
control->values = devm_kcalloc(dev, hweight64(control->cn_list),
- sizeof(int), GFP_KERNEL);
+ sizeof(*control->values), GFP_KERNEL);
if (!control->values)
return -ENOMEM;
@@ -990,6 +1018,10 @@ static int find_sdca_entity_control(struct device *dev, struct sdca_entity *enti
control->is_volatile = find_sdca_control_volatile(entity, control);
+ ret = find_sdca_control_reset(entity, control);
+ if (ret)
+ return ret;
+
ret = find_sdca_control_range(dev, control_node, &control->range);
if (ret) {
dev_err(dev, "%s: control %#x: range missing: %d\n",
@@ -1184,7 +1216,6 @@ static int find_sdca_entity_pde(struct device *dev,
{
static const int mult_delay = 3;
struct sdca_entity_pde *power = &entity->pde;
- u32 *delay_list __free(kfree) = NULL;
struct sdca_pde_delay *delays;
int num_delays;
int i, j;
@@ -1205,7 +1236,8 @@ static int find_sdca_entity_pde(struct device *dev,
return -EINVAL;
}
- delay_list = kcalloc(num_delays, sizeof(*delay_list), GFP_KERNEL);
+ u32 *delay_list __free(kfree) = kcalloc(num_delays, sizeof(*delay_list),
+ GFP_KERNEL);
if (!delay_list)
return -ENOMEM;
@@ -1250,7 +1282,6 @@ static int find_sdca_entity_ge(struct device *dev,
struct sdca_entity *entity)
{
struct sdca_entity_ge *group = &entity->ge;
- u8 *affected_list __free(kfree) = NULL;
u8 *affected_iter;
int num_affected;
int i, j;
@@ -1269,7 +1300,8 @@ static int find_sdca_entity_ge(struct device *dev,
return -EINVAL;
}
- affected_list = kcalloc(num_affected, sizeof(*affected_list), GFP_KERNEL);
+ u8 *affected_list __free(kfree) = kcalloc(num_affected, sizeof(*affected_list),
+ GFP_KERNEL);
if (!affected_list)
return -ENOMEM;
@@ -1495,7 +1527,6 @@ static int find_sdca_entities(struct device *dev, struct sdw_slave *sdw,
struct fwnode_handle *function_node,
struct sdca_function_data *function)
{
- u32 *entity_list __free(kfree) = NULL;
struct sdca_entity *entities;
int num_entities;
int i, ret;
@@ -1517,7 +1548,8 @@ static int find_sdca_entities(struct device *dev, struct sdw_slave *sdw,
if (!entities)
return -ENOMEM;
- entity_list = kcalloc(num_entities, sizeof(*entity_list), GFP_KERNEL);
+ u32 *entity_list __free(kfree) = kcalloc(num_entities, sizeof(*entity_list),
+ GFP_KERNEL);
if (!entity_list)
return -ENOMEM;
@@ -1642,7 +1674,6 @@ static int find_sdca_entity_connection_pde(struct device *dev,
struct sdca_entity *entity)
{
struct sdca_entity_pde *power = &entity->pde;
- u32 *managed_list __free(kfree) = NULL;
struct sdca_entity **managed;
int num_managed;
int i;
@@ -1664,7 +1695,8 @@ static int find_sdca_entity_connection_pde(struct device *dev,
if (!managed)
return -ENOMEM;
- managed_list = kcalloc(num_managed, sizeof(*managed_list), GFP_KERNEL);
+ u32 *managed_list __free(kfree) = kcalloc(num_managed, sizeof(*managed_list),
+ GFP_KERNEL);
if (!managed_list)
return -ENOMEM;
@@ -1961,7 +1993,6 @@ static int find_sdca_clusters(struct device *dev,
struct fwnode_handle *function_node,
struct sdca_function_data *function)
{
- u32 *cluster_list __free(kfree) = NULL;
struct sdca_cluster *clusters;
int num_clusters;
int i, ret;
@@ -1982,7 +2013,8 @@ static int find_sdca_clusters(struct device *dev,
if (!clusters)
return -ENOMEM;
- cluster_list = kcalloc(num_clusters, sizeof(*cluster_list), GFP_KERNEL);
+ u32 *cluster_list __free(kfree) = kcalloc(num_clusters, sizeof(*cluster_list),
+ GFP_KERNEL);
if (!cluster_list)
return -ENOMEM;
@@ -2026,7 +2058,6 @@ static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw,
{
static const int mult_fileset = 3;
char fileset_name[SDCA_PROPERTY_LENGTH];
- u32 *filesets_list __free(kfree) = NULL;
struct sdca_fdl_set *sets;
int num_sets;
int i, j;
@@ -2034,6 +2065,7 @@ static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw,
num_sets = fwnode_property_count_u32(function_node,
"mipi-sdca-file-set-id-list");
if (num_sets == 0 || num_sets == -EINVAL) {
+ dev_dbg(dev, "%pfwP: file set id list missing\n", function_node);
return 0;
} else if (num_sets < 0) {
dev_err(dev, "%pfwP: failed to read file set list: %d\n",
@@ -2041,19 +2073,19 @@ static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw,
return num_sets;
}
- filesets_list = kcalloc(num_sets, sizeof(u32), GFP_KERNEL);
+ u32 *filesets_list __free(kfree) = kcalloc(num_sets, sizeof(u32),
+ GFP_KERNEL);
if (!filesets_list)
return -ENOMEM;
fwnode_property_read_u32_array(function_node, "mipi-sdca-file-set-id-list",
filesets_list, num_sets);
- sets = devm_kcalloc(dev, num_sets, sizeof(struct sdca_fdl_set), GFP_KERNEL);
+ sets = devm_kcalloc(dev, num_sets, sizeof(*sets), GFP_KERNEL);
if (!sets)
return -ENOMEM;
for (i = 0; i < num_sets; i++) {
- u32 *fileset_entries __free(kfree) = NULL;
struct sdca_fdl_set *set = &sets[i];
struct sdca_fdl_file *files;
int num_files, num_entries;
@@ -2075,11 +2107,12 @@ static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw,
dev_dbg(dev, "fileset: %#x\n", filesets_list[i]);
files = devm_kcalloc(dev, num_entries / mult_fileset,
- sizeof(struct sdca_fdl_file), GFP_KERNEL);
+ sizeof(*files), GFP_KERNEL);
if (!files)
return -ENOMEM;
- fileset_entries = kcalloc(num_entries, sizeof(u32), GFP_KERNEL);
+ u32 *fileset_entries __free(kfree) = kcalloc(num_entries, sizeof(u32),
+ GFP_KERNEL);
if (!fileset_entries)
return -ENOMEM;
diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c
index 8f6a2adfb6fb..d9e22cf40f77 100644
--- a/sound/soc/sdca/sdca_interrupts.c
+++ b/sound/soc/sdca/sdca_interrupts.c
@@ -22,6 +22,7 @@
#include <sound/sdca_function.h>
#include <sound/sdca_hid.h>
#include <sound/sdca_interrupts.h>
+#include <sound/sdca_jack.h>
#include <sound/sdca_ump.h>
#include <sound/soc-component.h>
#include <sound/soc.h>
@@ -155,14 +156,7 @@ static irqreturn_t detected_mode_handler(int irq, void *data)
{
struct sdca_interrupt *interrupt = data;
struct device *dev = interrupt->dev;
- struct snd_soc_component *component = interrupt->component;
- struct snd_soc_card *card = component->card;
- struct rw_semaphore *rwsem = &card->snd_card->controls_rwsem;
- struct snd_kcontrol *kctl = interrupt->priv;
- struct snd_ctl_elem_value *ucontrol __free(kfree) = NULL;
- struct soc_enum *soc_enum;
irqreturn_t irqret = IRQ_NONE;
- unsigned int reg, val;
int ret;
ret = pm_runtime_get_sync(dev);
@@ -171,76 +165,9 @@ static irqreturn_t detected_mode_handler(int irq, void *data)
goto error;
}
- if (!kctl) {
- const char *name __free(kfree) = kasprintf(GFP_KERNEL, "%s %s",
- interrupt->entity->label,
- SDCA_CTL_SELECTED_MODE_NAME);
-
- if (!name)
- goto error;
-
- kctl = snd_soc_component_get_kcontrol(component, name);
- if (!kctl) {
- dev_dbg(dev, "control not found: %s\n", name);
- goto error;
- }
-
- interrupt->priv = kctl;
- }
-
- soc_enum = (struct soc_enum *)kctl->private_value;
-
- reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id,
- interrupt->control->sel, 0);
-
- ret = regmap_read(interrupt->function_regmap, reg, &val);
- if (ret < 0) {
- dev_err(dev, "failed to read detected mode: %d\n", ret);
- goto error;
- }
-
- switch (val) {
- case SDCA_DETECTED_MODE_DETECTION_IN_PROGRESS:
- case SDCA_DETECTED_MODE_JACK_UNKNOWN:
- reg = SDW_SDCA_CTL(interrupt->function->desc->adr,
- interrupt->entity->id,
- SDCA_CTL_GE_SELECTED_MODE, 0);
-
- /*
- * Selected mode is not normally marked as volatile register
- * (RW), but here force a read from the hardware. If the
- * detected mode is unknown we need to see what the device
- * selected as a "safe" option.
- */
- regcache_drop_region(interrupt->function_regmap, reg, reg);
-
- ret = regmap_read(interrupt->function_regmap, reg, &val);
- if (ret) {
- dev_err(dev, "failed to re-check selected mode: %d\n", ret);
- goto error;
- }
- break;
- default:
- break;
- }
-
- dev_dbg(dev, "%s: %#x\n", interrupt->name, val);
-
- ucontrol = kzalloc(sizeof(*ucontrol), GFP_KERNEL);
- if (!ucontrol)
- goto error;
-
- ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(soc_enum, val);
-
- down_write(rwsem);
- ret = kctl->put(kctl, ucontrol);
- up_write(rwsem);
- if (ret < 0) {
- dev_err(dev, "failed to update selected mode: %d\n", ret);
+ ret = sdca_jack_process(interrupt);
+ if (ret)
goto error;
- }
-
- snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
irqret = IRQ_HANDLED;
error:
@@ -271,6 +198,18 @@ error:
return irqret;
}
+#ifdef CONFIG_PM_SLEEP
+static bool no_pm_in_progress(struct device *dev)
+{
+ return completion_done(&dev->power.completion);
+}
+#else
+static bool no_pm_in_progress(struct device *dev)
+{
+ return true;
+}
+#endif
+
static irqreturn_t fdl_owner_handler(int irq, void *data)
{
struct sdca_interrupt *interrupt = data;
@@ -278,10 +217,16 @@ static irqreturn_t fdl_owner_handler(int irq, void *data)
irqreturn_t irqret = IRQ_NONE;
int ret;
- ret = pm_runtime_get_sync(dev);
- if (ret < 0) {
- dev_err(dev, "failed to resume for fdl: %d\n", ret);
- goto error;
+ /*
+ * FDL has to run from the system resume handler, at which point
+ * we can't wait for the pm runtime.
+ */
+ if (no_pm_in_progress(dev)) {
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ dev_err(dev, "failed to resume for fdl: %d\n", ret);
+ goto error;
+ }
}
ret = sdca_fdl_process(interrupt);
@@ -290,7 +235,8 @@ static irqreturn_t fdl_owner_handler(int irq, void *data)
irqret = IRQ_HANDLED;
error:
- pm_runtime_put(dev);
+ if (no_pm_in_progress(dev))
+ pm_runtime_put(dev);
return irqret;
}
@@ -536,6 +482,10 @@ int sdca_irq_populate(struct sdca_function_data *function,
handler = function_status_handler;
break;
case SDCA_CTL_TYPE_S(GE, DETECTED_MODE):
+ ret = sdca_jack_alloc_state(interrupt);
+ if (ret)
+ return ret;
+
handler = detected_mode_handler;
break;
case SDCA_CTL_TYPE_S(XU, FDL_CURRENTOWNER):
@@ -610,3 +560,79 @@ struct sdca_interrupt_info *sdca_irq_allocate(struct device *sdev,
return info;
}
EXPORT_SYMBOL_NS_GPL(sdca_irq_allocate, "SND_SOC_SDCA");
+
+static void irq_enable_flags(struct sdca_function_data *function,
+ struct sdca_interrupt_info *info, bool early)
+{
+ struct sdca_interrupt *interrupt;
+ int i;
+
+ for (i = 0; i < SDCA_MAX_INTERRUPTS; i++) {
+ interrupt = &info->irqs[i];
+
+ if (!interrupt || interrupt->function != function)
+ continue;
+
+ switch (SDCA_CTL_TYPE(interrupt->entity->type,
+ interrupt->control->sel)) {
+ case SDCA_CTL_TYPE_S(XU, FDL_CURRENTOWNER):
+ if (early)
+ enable_irq(interrupt->irq);
+ break;
+ default:
+ if (!early)
+ enable_irq(interrupt->irq);
+ break;
+ }
+ }
+}
+
+/**
+ * sdca_irq_enable_early - Re-enable early SDCA IRQs for a given function
+ * @function: Pointer to the SDCA Function.
+ * @info: Pointer to the SDCA interrupt info for this device.
+ *
+ * The early version of the IRQ enable allows enabling IRQs which may be
+ * necessary to bootstrap functionality for other IRQs, such as the FDL
+ * process.
+ */
+void sdca_irq_enable_early(struct sdca_function_data *function,
+ struct sdca_interrupt_info *info)
+{
+ irq_enable_flags(function, info, true);
+}
+EXPORT_SYMBOL_NS_GPL(sdca_irq_enable_early, "SND_SOC_SDCA");
+
+/**
+ * sdca_irq_enable - Re-enable SDCA IRQs for a given function
+ * @function: Pointer to the SDCA Function.
+ * @info: Pointer to the SDCA interrupt info for this device.
+ */
+void sdca_irq_enable(struct sdca_function_data *function,
+ struct sdca_interrupt_info *info)
+{
+ irq_enable_flags(function, info, false);
+}
+EXPORT_SYMBOL_NS_GPL(sdca_irq_enable, "SND_SOC_SDCA");
+
+/**
+ * sdca_irq_disable - Disable SDCA IRQs for a given function
+ * @function: Pointer to the SDCA Function.
+ * @info: Pointer to the SDCA interrupt info for this device.
+ */
+void sdca_irq_disable(struct sdca_function_data *function,
+ struct sdca_interrupt_info *info)
+{
+ struct sdca_interrupt *interrupt;
+ int i;
+
+ for (i = 0; i < SDCA_MAX_INTERRUPTS; i++) {
+ interrupt = &info->irqs[i];
+
+ if (!interrupt || interrupt->function != function)
+ continue;
+
+ disable_irq(interrupt->irq);
+ }
+}
+EXPORT_SYMBOL_NS_GPL(sdca_irq_disable, "SND_SOC_SDCA");
diff --git a/sound/soc/sdca/sdca_jack.c b/sound/soc/sdca/sdca_jack.c
new file mode 100644
index 000000000000..605514f02045
--- /dev/null
+++ b/sound/soc/sdca/sdca_jack.c
@@ -0,0 +1,248 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2025 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+/*
+ * The MIPI SDCA specification is available for public downloads at
+ * https://www.mipi.org/mipi-sdca-v1-0-download
+ */
+
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/dev_printk.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/sprintf.h>
+#include <linux/regmap.h>
+#include <linux/rwsem.h>
+#include <sound/asound.h>
+#include <sound/control.h>
+#include <sound/jack.h>
+#include <sound/sdca.h>
+#include <sound/sdca_function.h>
+#include <sound/sdca_interrupts.h>
+#include <sound/sdca_jack.h>
+#include <sound/soc-component.h>
+#include <sound/soc-jack.h>
+#include <sound/soc.h>
+
+/**
+ * sdca_jack_process - Process an SDCA jack event
+ * @interrupt: SDCA interrupt structure
+ *
+ * Return: Zero on success or a negative error code.
+ */
+int sdca_jack_process(struct sdca_interrupt *interrupt)
+{
+ struct device *dev = interrupt->dev;
+ struct snd_soc_component *component = interrupt->component;
+ struct snd_soc_card *card = component->card;
+ struct rw_semaphore *rwsem = &card->snd_card->controls_rwsem;
+ struct jack_state *state = interrupt->priv;
+ struct snd_kcontrol *kctl = state->kctl;
+ struct snd_ctl_elem_value *ucontrol __free(kfree) = NULL;
+ unsigned int reg, val;
+ int ret;
+
+ guard(rwsem_write)(rwsem);
+
+ if (!kctl) {
+ const char *name __free(kfree) = kasprintf(GFP_KERNEL, "%s %s",
+ interrupt->entity->label,
+ SDCA_CTL_SELECTED_MODE_NAME);
+
+ if (!name)
+ return -ENOMEM;
+
+ kctl = snd_soc_component_get_kcontrol(component, name);
+ if (!kctl)
+ dev_dbg(dev, "control not found: %s\n", name);
+ else
+ state->kctl = kctl;
+ }
+
+ reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id,
+ interrupt->control->sel, 0);
+
+ ret = regmap_read(interrupt->function_regmap, reg, &val);
+ if (ret < 0) {
+ dev_err(dev, "failed to read detected mode: %d\n", ret);
+ return ret;
+ }
+
+ reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id,
+ SDCA_CTL_GE_SELECTED_MODE, 0);
+
+ switch (val) {
+ case SDCA_DETECTED_MODE_DETECTION_IN_PROGRESS:
+ case SDCA_DETECTED_MODE_JACK_UNKNOWN:
+ /*
+ * Selected mode is not normally marked as volatile register
+ * (RW), but here force a read from the hardware. If the
+ * detected mode is unknown we need to see what the device
+ * selected as a "safe" option.
+ */
+ regcache_drop_region(interrupt->function_regmap, reg, reg);
+
+ ret = regmap_read(interrupt->function_regmap, reg, &val);
+ if (ret) {
+ dev_err(dev, "failed to re-check selected mode: %d\n", ret);
+ return ret;
+ }
+ break;
+ default:
+ break;
+ }
+
+ dev_dbg(dev, "%s: %#x\n", interrupt->name, val);
+
+ if (kctl) {
+ struct soc_enum *soc_enum = (struct soc_enum *)kctl->private_value;
+
+ ucontrol = kzalloc(sizeof(*ucontrol), GFP_KERNEL);
+ if (!ucontrol)
+ return -ENOMEM;
+
+ ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(soc_enum, val);
+
+ ret = snd_soc_dapm_put_enum_double(kctl, ucontrol);
+ if (ret < 0) {
+ dev_err(dev, "failed to update selected mode: %d\n", ret);
+ return ret;
+ }
+
+ snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
+ } else {
+ ret = regmap_write(interrupt->function_regmap, reg, val);
+ if (ret) {
+ dev_err(dev, "failed to write selected mode: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return sdca_jack_report(interrupt);
+}
+EXPORT_SYMBOL_NS_GPL(sdca_jack_process, "SND_SOC_SDCA");
+
+/**
+ * sdca_jack_alloc_state - allocate state for a jack interrupt
+ * @interrupt: SDCA interrupt structure.
+ *
+ * Return: Zero on success or a negative error code.
+ */
+int sdca_jack_alloc_state(struct sdca_interrupt *interrupt)
+{
+ struct device *dev = interrupt->dev;
+ struct jack_state *jack_state;
+
+ jack_state = devm_kzalloc(dev, sizeof(*jack_state), GFP_KERNEL);
+ if (!jack_state)
+ return -ENOMEM;
+
+ interrupt->priv = jack_state;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(sdca_jack_alloc_state, "SND_SOC_SDCA");
+
+/**
+ * sdca_jack_set_jack - attach an ASoC jack to SDCA
+ * @info: SDCA interrupt information.
+ * @jack: ASoC jack to be attached.
+ *
+ * Return: Zero on success or a negative error code.
+ */
+int sdca_jack_set_jack(struct sdca_interrupt_info *info, struct snd_soc_jack *jack)
+{
+ int i, ret;
+
+ guard(mutex)(&info->irq_lock);
+
+ for (i = 0; i < SDCA_MAX_INTERRUPTS; i++) {
+ struct sdca_interrupt *interrupt = &info->irqs[i];
+ struct sdca_control *control = interrupt->control;
+ struct sdca_entity *entity = interrupt->entity;
+ struct jack_state *jack_state;
+
+ if (!interrupt->irq)
+ continue;
+
+ switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
+ case SDCA_CTL_TYPE_S(GE, DETECTED_MODE):
+ jack_state = interrupt->priv;
+ jack_state->jack = jack;
+
+ /* Report initial state in case IRQ was already handled */
+ ret = sdca_jack_report(interrupt);
+ if (ret)
+ return ret;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(sdca_jack_set_jack, "SND_SOC_SDCA");
+
+int sdca_jack_report(struct sdca_interrupt *interrupt)
+{
+ struct jack_state *jack_state = interrupt->priv;
+ struct sdca_control_range *range;
+ enum sdca_terminal_type type;
+ unsigned int report = 0;
+ unsigned int reg, val;
+ int ret;
+
+ reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id,
+ SDCA_CTL_GE_SELECTED_MODE, 0);
+
+ ret = regmap_read(interrupt->function_regmap, reg, &val);
+ if (ret) {
+ dev_err(interrupt->dev, "failed to read selected mode: %d\n", ret);
+ return ret;
+ }
+
+ range = sdca_selector_find_range(interrupt->dev, interrupt->entity,
+ SDCA_CTL_GE_SELECTED_MODE,
+ SDCA_SELECTED_MODE_NCOLS, 0);
+ if (!range)
+ return -EINVAL;
+
+ type = sdca_range_search(range, SDCA_SELECTED_MODE_INDEX,
+ val, SDCA_SELECTED_MODE_TERM_TYPE);
+
+ switch (type) {
+ case SDCA_TERM_TYPE_LINEIN_STEREO:
+ case SDCA_TERM_TYPE_LINEIN_FRONT_LR:
+ case SDCA_TERM_TYPE_LINEIN_CENTER_LFE:
+ case SDCA_TERM_TYPE_LINEIN_SURROUND_LR:
+ case SDCA_TERM_TYPE_LINEIN_REAR_LR:
+ report = SND_JACK_LINEIN;
+ break;
+ case SDCA_TERM_TYPE_LINEOUT_STEREO:
+ case SDCA_TERM_TYPE_LINEOUT_FRONT_LR:
+ case SDCA_TERM_TYPE_LINEOUT_CENTER_LFE:
+ case SDCA_TERM_TYPE_LINEOUT_SURROUND_LR:
+ case SDCA_TERM_TYPE_LINEOUT_REAR_LR:
+ report = SND_JACK_LINEOUT;
+ break;
+ case SDCA_TERM_TYPE_MIC_JACK:
+ report = SND_JACK_MICROPHONE;
+ break;
+ case SDCA_TERM_TYPE_HEADPHONE_JACK:
+ report = SND_JACK_HEADPHONE;
+ break;
+ case SDCA_TERM_TYPE_HEADSET_JACK:
+ report = SND_JACK_HEADSET;
+ break;
+ default:
+ break;
+ }
+
+ snd_soc_jack_report(jack_state->jack, report, 0xFFFF);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(sdca_jack_report, "SND_SOC_SDCA");
diff --git a/sound/soc/sdca/sdca_regmap.c b/sound/soc/sdca/sdca_regmap.c
index 2cca9a9c71ea..4f8a685dc43d 100644
--- a/sound/soc/sdca/sdca_regmap.c
+++ b/sound/soc/sdca/sdca_regmap.c
@@ -218,7 +218,8 @@ int sdca_regmap_count_constants(struct device *dev,
struct sdca_entity *entity = &function->entities[i];
for (j = 0; j < entity->num_controls; j++) {
- if (entity->controls[j].mode == SDCA_ACCESS_MODE_DC)
+ if (entity->controls[j].mode == SDCA_ACCESS_MODE_DC ||
+ entity->controls[j].has_reset)
nconsts += hweight64(entity->controls[j].cn_list);
}
}
@@ -255,7 +256,8 @@ int sdca_regmap_populate_constants(struct device *dev,
struct sdca_control *control = &entity->controls[j];
int cn;
- if (control->mode != SDCA_ACCESS_MODE_DC)
+ if (control->mode != SDCA_ACCESS_MODE_DC &&
+ !control->has_reset)
continue;
l = 0;
@@ -264,7 +266,10 @@ int sdca_regmap_populate_constants(struct device *dev,
consts[k].reg = SDW_SDCA_CTL(function->desc->adr,
entity->id,
control->sel, cn);
- consts[k].def = control->values[l];
+ if (control->mode == SDCA_ACCESS_MODE_DC)
+ consts[k].def = control->values[l];
+ else
+ consts[k].def = control->reset;
k++;
l++;
}
@@ -306,6 +311,9 @@ static int populate_control_defaults(struct device *dev, struct regmap *regmap,
i++;
} else if (!control->is_volatile) {
+ if (control->has_reset)
+ regcache_drop_region(regmap, reg, reg);
+
ret = regmap_read(regmap, reg, &val);
if (ret) {
dev_err(dev, "Failed to read initial %#x: %d\n",
diff --git a/sound/soc/sdca/sdca_ump.c b/sound/soc/sdca/sdca_ump.c
index 8aba3ff16872..a86bb28c6d0a 100644
--- a/sound/soc/sdca/sdca_ump.c
+++ b/sound/soc/sdca/sdca_ump.c
@@ -257,6 +257,6 @@ void sdca_ump_schedule_timeout(struct delayed_work *work, unsigned int timeout_u
if (!timeout_us)
return;
- queue_delayed_work(system_wq, work, usecs_to_jiffies(timeout_us));
+ queue_delayed_work(system_dfl_wq, work, usecs_to_jiffies(timeout_us));
}
EXPORT_SYMBOL_NS_GPL(sdca_ump_schedule_timeout, "SND_SOC_SDCA");
diff --git a/sound/soc/sdw_utils/soc_sdw_ti_amp.c b/sound/soc/sdw_utils/soc_sdw_ti_amp.c
index cbd60faecd09..488ef2ef45d4 100644
--- a/sound/soc/sdw_utils/soc_sdw_ti_amp.c
+++ b/sound/soc/sdw_utils/soc_sdw_ti_amp.c
@@ -58,6 +58,10 @@ int asoc_sdw_ti_spk_rtd_init(struct snd_soc_pcm_runtime *rtd,
strscpy(speaker, "Left Spk", sizeof(speaker));
} else if (!strncmp(prefix, "tas2783-2", strlen("tas2783-2"))) {
strscpy(speaker, "Right Spk", sizeof(speaker));
+ } else if (!strncmp(prefix, "tas2783-3", strlen("tas2783-3"))) {
+ strscpy(speaker, "Left Spk2", sizeof(speaker));
+ } else if (!strncmp(prefix, "tas2783-4", strlen("tas2783-4"))) {
+ strscpy(speaker, "Right Spk2", sizeof(speaker));
} else {
ret = -EINVAL;
dev_err(card->dev, "unhandled prefix %s", prefix);
diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c
index ccf149f949e8..0e67d9f34cba 100644
--- a/sound/soc/sdw_utils/soc_sdw_utils.c
+++ b/sound/soc/sdw_utils/soc_sdw_utils.c
@@ -40,11 +40,25 @@ static const struct snd_soc_dapm_widget lr_spk_widgets[] = {
SND_SOC_DAPM_SPK("Right Spk", NULL),
};
+static const struct snd_soc_dapm_widget lr_4spk_widgets[] = {
+ SND_SOC_DAPM_SPK("Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Right Spk", NULL),
+ SND_SOC_DAPM_SPK("Left Spk2", NULL),
+ SND_SOC_DAPM_SPK("Right Spk2", NULL),
+};
+
static const struct snd_kcontrol_new lr_spk_controls[] = {
SOC_DAPM_PIN_SWITCH("Left Spk"),
SOC_DAPM_PIN_SWITCH("Right Spk"),
};
+static const struct snd_kcontrol_new lr_4spk_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Left Spk"),
+ SOC_DAPM_PIN_SWITCH("Right Spk"),
+ SOC_DAPM_PIN_SWITCH("Left Spk2"),
+ SOC_DAPM_PIN_SWITCH("Right Spk2"),
+};
+
static const struct snd_soc_dapm_widget rt700_widgets[] = {
SND_SOC_DAPM_HP("Headphones", NULL),
SND_SOC_DAPM_MIC("AMIC", NULL),
@@ -69,10 +83,10 @@ struct asoc_sdw_codec_info codec_info_list[] = {
.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
.init = asoc_sdw_ti_amp_init,
.rtd_init = asoc_sdw_ti_spk_rtd_init,
- .controls = lr_spk_controls,
- .num_controls = ARRAY_SIZE(lr_spk_controls),
- .widgets = lr_spk_widgets,
- .num_widgets = ARRAY_SIZE(lr_spk_widgets),
+ .controls = lr_4spk_controls,
+ .num_controls = ARRAY_SIZE(lr_4spk_controls),
+ .widgets = lr_4spk_widgets,
+ .num_widgets = ARRAY_SIZE(lr_4spk_widgets),
},
},
.dai_num = 1,
@@ -492,6 +506,8 @@ struct asoc_sdw_codec_info codec_info_list[] = {
.dai_type = SOC_SDW_DAI_TYPE_MIC,
.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
.rtd_init = asoc_sdw_rt_dmic_rtd_init,
+ .quirk = SOC_SDW_CODEC_MIC,
+ .quirk_exclude = true,
},
},
.dai_num = 3,
@@ -1421,29 +1437,14 @@ static int is_sdca_endpoint_present(struct device *dev,
const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[adr_index];
const struct snd_soc_acpi_endpoint *adr_end;
const struct asoc_sdw_dai_info *dai_info;
- struct snd_soc_dai_link_component *dlc;
- struct snd_soc_dai *codec_dai;
struct sdw_slave *slave;
struct device *sdw_dev;
const char *sdw_codec_name;
int ret, i;
- dlc = kzalloc(sizeof(*dlc), GFP_KERNEL);
- if (!dlc)
- return -ENOMEM;
-
adr_end = &adr_dev->endpoints[end_index];
dai_info = &codec_info->dais[adr_end->num];
- dlc->dai_name = dai_info->dai_name;
- codec_dai = snd_soc_find_dai_with_mutex(dlc);
- if (!codec_dai) {
- dev_warn(dev, "codec dai %s not registered yet\n", dlc->dai_name);
- kfree(dlc);
- return -EPROBE_DEFER;
- }
- kfree(dlc);
-
sdw_codec_name = _asoc_sdw_get_codec_name(dev, adr_link, adr_index);
if (!sdw_codec_name)
return -ENOMEM;
diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c
index c815fd1b3fd1..89f236ab3034 100644
--- a/sound/soc/soc-component.c
+++ b/sound/soc/soc-component.c
@@ -142,88 +142,6 @@ int snd_soc_component_set_bias_level(struct snd_soc_component *component,
return soc_component_ret(component, ret);
}
-int snd_soc_component_enable_pin(struct snd_soc_component *component,
- const char *pin)
-{
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(component);
- return snd_soc_dapm_enable_pin(dapm, pin);
-}
-EXPORT_SYMBOL_GPL(snd_soc_component_enable_pin);
-
-int snd_soc_component_enable_pin_unlocked(struct snd_soc_component *component,
- const char *pin)
-{
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(component);
- return snd_soc_dapm_enable_pin_unlocked(dapm, pin);
-}
-EXPORT_SYMBOL_GPL(snd_soc_component_enable_pin_unlocked);
-
-int snd_soc_component_disable_pin(struct snd_soc_component *component,
- const char *pin)
-{
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(component);
- return snd_soc_dapm_disable_pin(dapm, pin);
-}
-EXPORT_SYMBOL_GPL(snd_soc_component_disable_pin);
-
-int snd_soc_component_disable_pin_unlocked(struct snd_soc_component *component,
- const char *pin)
-{
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(component);
- return snd_soc_dapm_disable_pin_unlocked(dapm, pin);
-}
-EXPORT_SYMBOL_GPL(snd_soc_component_disable_pin_unlocked);
-
-int snd_soc_component_nc_pin(struct snd_soc_component *component,
- const char *pin)
-{
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(component);
- return snd_soc_dapm_nc_pin(dapm, pin);
-}
-EXPORT_SYMBOL_GPL(snd_soc_component_nc_pin);
-
-int snd_soc_component_nc_pin_unlocked(struct snd_soc_component *component,
- const char *pin)
-{
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(component);
- return snd_soc_dapm_nc_pin_unlocked(dapm, pin);
-}
-EXPORT_SYMBOL_GPL(snd_soc_component_nc_pin_unlocked);
-
-int snd_soc_component_get_pin_status(struct snd_soc_component *component,
- const char *pin)
-{
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(component);
- return snd_soc_dapm_get_pin_status(dapm, pin);
-}
-EXPORT_SYMBOL_GPL(snd_soc_component_get_pin_status);
-
-int snd_soc_component_force_enable_pin(struct snd_soc_component *component,
- const char *pin)
-{
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(component);
- return snd_soc_dapm_force_enable_pin(dapm, pin);
-}
-EXPORT_SYMBOL_GPL(snd_soc_component_force_enable_pin);
-
-int snd_soc_component_force_enable_pin_unlocked(
- struct snd_soc_component *component,
- const char *pin)
-{
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(component);
- return snd_soc_dapm_force_enable_pin_unlocked(dapm, pin);
-}
-EXPORT_SYMBOL_GPL(snd_soc_component_force_enable_pin_unlocked);
-
static void soc_get_kcontrol_name(struct snd_soc_component *component,
char *buf, int size, const char * const ctl)
{
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index e4b21bf39e59..5811d053ce7a 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -2556,6 +2556,10 @@ int snd_soc_register_card(struct snd_soc_card *card)
if (!card->name || !card->dev)
return -EINVAL;
+ card->dapm = snd_soc_dapm_alloc(card->dev);
+ if (!card->dapm)
+ return -ENOMEM;
+
dev_set_drvdata(card->dev, card);
INIT_LIST_HEAD(&card->widgets);
@@ -2675,6 +2679,8 @@ static inline char *fmt_multiple_name(struct device *dev,
void snd_soc_unregister_dai(struct snd_soc_dai *dai)
{
+ lockdep_assert_held(&client_mutex);
+
dev_dbg(dai->dev, "ASoC: Unregistered DAI '%s'\n", dai->name);
list_del(&dai->list);
}
@@ -2840,6 +2846,10 @@ int snd_soc_component_initialize(struct snd_soc_component *component,
const struct snd_soc_component_driver *driver,
struct device *dev)
{
+ component->dapm = snd_soc_dapm_alloc(dev);
+ if (!component->dapm)
+ return -ENOMEM;
+
INIT_LIST_HEAD(&component->dai_list);
INIT_LIST_HEAD(&component->dobj_list);
INIT_LIST_HEAD(&component->card_list);
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 4d920a59da3c..c23ccf4a602d 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -40,12 +40,33 @@
#include <trace/events/asoc.h>
+/* DAPM context */
+struct snd_soc_dapm_context {
+ enum snd_soc_bias_level bias_level;
+
+ bool idle_bias; /* Use BIAS_OFF instead of STANDBY when false */
+
+ struct snd_soc_component *component; /* parent component */
+ struct snd_soc_card *card; /* parent card */
+
+ /* used during DAPM updates */
+ enum snd_soc_bias_level target_bias_level;
+ struct list_head list;
+
+ struct snd_soc_dapm_widget *wcache_sink;
+ struct snd_soc_dapm_widget *wcache_source;
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *debugfs_dapm;
+#endif
+};
+
#define DAPM_UPDATE_STAT(widget, val) widget->dapm->card->dapm_stats.val++;
-#define SND_SOC_DAPM_DIR_REVERSE(x) ((x == SND_SOC_DAPM_DIR_IN) ? \
+#define DAPM_DIR_REVERSE(x) ((x == SND_SOC_DAPM_DIR_IN) ? \
SND_SOC_DAPM_DIR_OUT : SND_SOC_DAPM_DIR_IN)
-#define snd_soc_dapm_for_each_direction(dir) \
+#define dapm_for_each_direction(dir) \
for ((dir) = SND_SOC_DAPM_DIR_IN; (dir) <= SND_SOC_DAPM_DIR_OUT; \
(dir)++)
@@ -138,14 +159,14 @@ static void dapm_assert_locked(struct snd_soc_dapm_context *dapm)
snd_soc_dapm_mutex_assert_held(dapm);
}
-static void pop_wait(u32 pop_time)
+static void dapm_pop_wait(u32 pop_time)
{
if (pop_time)
schedule_timeout_uninterruptible(msecs_to_jiffies(pop_time));
}
__printf(3, 4)
-static void pop_dbg(struct device *dev, u32 pop_time, const char *fmt, ...)
+static void dapm_pop_dbg(struct device *dev, u32 pop_time, const char *fmt, ...)
{
va_list args;
char *buf;
@@ -165,6 +186,11 @@ static void pop_dbg(struct device *dev, u32 pop_time, const char *fmt, ...)
kfree(buf);
}
+struct snd_soc_dapm_context *snd_soc_dapm_alloc(struct device *dev)
+{
+ return devm_kzalloc(dev, sizeof(struct snd_soc_dapm_context), GFP_KERNEL);
+}
+
struct device *snd_soc_dapm_to_dev(struct snd_soc_dapm_context *dapm)
{
if (dapm->component)
@@ -214,7 +240,7 @@ static void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason)
static __always_inline void dapm_widget_invalidate_paths(
struct snd_soc_dapm_widget *w, enum snd_soc_dapm_direction dir)
{
- enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir);
+ enum snd_soc_dapm_direction rdir = DAPM_DIR_REVERSE(dir);
struct snd_soc_dapm_widget *node;
struct snd_soc_dapm_path *p;
LIST_HEAD(list);
@@ -361,7 +387,7 @@ struct dapm_kcontrol_data {
struct snd_soc_dapm_widget_list *wlist;
};
-static unsigned int soc_dapm_read(struct snd_soc_dapm_context *dapm, int reg)
+static unsigned int dapm_read(struct snd_soc_dapm_context *dapm, int reg)
{
if (!dapm->component)
return -EIO;
@@ -381,7 +407,7 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i,
unsigned int shift = mc->shift;
unsigned int max = mc->max;
unsigned int mask = (1 << fls(max)) - 1;
- unsigned int val = soc_dapm_read(p->sink->dapm, reg);
+ unsigned int val = dapm_read(p->sink->dapm, reg);
/*
* The nth_path argument allows this function to know
@@ -397,7 +423,7 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i,
*/
if (snd_soc_volsw_is_stereo(mc) && nth_path > 0) {
if (reg != mc->rreg)
- val = soc_dapm_read(p->sink->dapm, mc->rreg);
+ val = dapm_read(p->sink->dapm, mc->rreg);
val = (val >> mc->rshift) & mask;
} else {
val = (val >> shift) & mask;
@@ -429,7 +455,7 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
if (e->reg != SND_SOC_NOPM) {
unsigned int val;
- val = soc_dapm_read(dapm, e->reg);
+ val = dapm_read(dapm, e->reg);
val = (val >> e->shift_l) & e->mask;
item = snd_soc_enum_val_to_item(e, val);
} else {
@@ -517,7 +543,7 @@ static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w)
break;
case snd_soc_dapm_line:
ep = 0;
- snd_soc_dapm_for_each_direction(dir) {
+ dapm_for_each_direction(dir) {
if (!list_empty(&w->edges[dir]))
ep |= SND_SOC_DAPM_DIR_TO_EP(dir);
}
@@ -529,7 +555,7 @@ static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w)
w->is_ep = ep;
}
-static int snd_soc_dapm_check_dynamic_path(
+static int dapm_check_dynamic_path(
struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink,
const char *control)
@@ -575,7 +601,7 @@ static int snd_soc_dapm_check_dynamic_path(
return 0;
}
-static int snd_soc_dapm_add_path(
+static int dapm_add_path(
struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink,
const char *control,
@@ -608,7 +634,7 @@ static int snd_soc_dapm_add_path(
return -EINVAL;
}
- ret = snd_soc_dapm_check_dynamic_path(dapm, wsource, wsink, control);
+ ret = dapm_check_dynamic_path(dapm, wsource, wsink, control);
if (ret)
return ret;
@@ -660,10 +686,10 @@ static int snd_soc_dapm_add_path(
list_add(&path->list, &dapm->card->paths);
- snd_soc_dapm_for_each_direction(dir)
+ dapm_for_each_direction(dir)
list_add(&path->list_node[dir], &path->node[dir]->edges[dir]);
- snd_soc_dapm_for_each_direction(dir) {
+ dapm_for_each_direction(dir) {
dapm_update_widget_flags(path->node[dir]);
dapm_mark_dirty(path->node[dir], "Route added");
}
@@ -771,10 +797,10 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,
goto err_data;
}
- snd_soc_dapm_add_path(widget->dapm, data->widget,
- widget, NULL, NULL);
+ dapm_add_path(widget->dapm, data->widget,
+ widget, NULL, NULL);
} else if (e->reg != SND_SOC_NOPM) {
- data->value = soc_dapm_read(widget->dapm, e->reg) &
+ data->value = dapm_read(widget->dapm, e->reg) &
(e->mask << e->shift_l);
}
break;
@@ -954,14 +980,14 @@ static void dapm_reset(struct snd_soc_card *card)
}
}
-static const char *soc_dapm_prefix(struct snd_soc_dapm_context *dapm)
+static const char *dapm_prefix(struct snd_soc_dapm_context *dapm)
{
if (!dapm->component)
return NULL;
return dapm->component->name_prefix;
}
-static int soc_dapm_update_bits(struct snd_soc_dapm_context *dapm,
+static int dapm_update_bits(struct snd_soc_dapm_context *dapm,
int reg, unsigned int mask, unsigned int value)
{
if (!dapm->component)
@@ -970,7 +996,7 @@ static int soc_dapm_update_bits(struct snd_soc_dapm_context *dapm,
mask, value);
}
-static int soc_dapm_test_bits(struct snd_soc_dapm_context *dapm,
+static int dapm_test_bits(struct snd_soc_dapm_context *dapm,
int reg, unsigned int mask, unsigned int value)
{
if (!dapm->component)
@@ -978,7 +1004,7 @@ static int soc_dapm_test_bits(struct snd_soc_dapm_context *dapm,
return snd_soc_component_test_bits(dapm->component, reg, mask, value);
}
-static void soc_dapm_async_complete(struct snd_soc_dapm_context *dapm)
+static void dapm_async_complete(struct snd_soc_dapm_context *dapm)
{
if (dapm->component)
snd_soc_component_async_complete(dapm->component);
@@ -1076,7 +1102,7 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm,
if (ret != 0)
goto out;
- if (dapm != &card->dapm)
+ if (dapm != card->dapm)
ret = snd_soc_dapm_force_bias_level(dapm, level);
if (ret != 0)
@@ -1149,7 +1175,7 @@ static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w,
const char *name;
int ret = 0;
- prefix = soc_dapm_prefix(dapm);
+ prefix = dapm_prefix(dapm);
if (prefix)
prefix_len = strlen(prefix) + 1;
else
@@ -1269,10 +1295,10 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w)
data = snd_kcontrol_chip(w->kcontrols[i]);
if (data->widget)
- snd_soc_dapm_add_path(data->widget->dapm,
- data->widget,
- path->source,
- NULL, NULL);
+ dapm_add_path(data->widget->dapm,
+ data->widget,
+ path->source,
+ NULL, NULL);
}
}
@@ -1376,7 +1402,7 @@ static int dapm_new_dai_link(struct snd_soc_dapm_widget *w)
* the ALSA card - when we are suspending the ALSA state for the card
* is set to D3.
*/
-static int snd_soc_dapm_suspend_check(struct snd_soc_dapm_widget *widget)
+static int dapm_suspend_check(struct snd_soc_dapm_widget *widget)
{
struct device *dev = snd_soc_dapm_to_dev(widget->dapm);
int level = snd_power_get_state(widget->dapm->card->snd_card);
@@ -1428,10 +1454,10 @@ static int dapm_widget_list_create(struct snd_soc_dapm_widget_list **list,
* widget and all widgets that can be reached via incoming or outcoming paths
* from the widget.
*/
-static void invalidate_paths_ep(struct snd_soc_dapm_widget *widget,
+static void dapm_invalidate_paths_ep(struct snd_soc_dapm_widget *widget,
enum snd_soc_dapm_direction dir)
{
- enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir);
+ enum snd_soc_dapm_direction rdir = DAPM_DIR_REVERSE(dir);
struct snd_soc_dapm_path *path;
widget->endpoints[dir] = -1;
@@ -1445,7 +1471,7 @@ static void invalidate_paths_ep(struct snd_soc_dapm_widget *widget,
if (path->connect) {
path->walking = 1;
- invalidate_paths_ep(path->node[dir], dir);
+ dapm_invalidate_paths_ep(path->node[dir], dir);
path->walking = 0;
}
}
@@ -1458,7 +1484,7 @@ static void invalidate_paths_ep(struct snd_soc_dapm_widget *widget,
* generic function and at the same time the fast path of the specialized
* functions is significantly smaller than the generic function.
*/
-static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget,
+static __always_inline int dapm_is_connected_ep(struct snd_soc_dapm_widget *widget,
struct list_head *list, enum snd_soc_dapm_direction dir,
int (*fn)(struct snd_soc_dapm_widget *, struct list_head *,
bool (*custom_stop_condition)(struct snd_soc_dapm_widget *,
@@ -1466,7 +1492,7 @@ static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget,
bool (*custom_stop_condition)(struct snd_soc_dapm_widget *,
enum snd_soc_dapm_direction))
{
- enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir);
+ enum snd_soc_dapm_direction rdir = DAPM_DIR_REVERSE(dir);
struct snd_soc_dapm_path *path;
int con = 0;
@@ -1485,7 +1511,7 @@ static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget,
}
if ((widget->is_ep & SND_SOC_DAPM_DIR_TO_EP(dir)) && widget->connected) {
- widget->endpoints[dir] = snd_soc_dapm_suspend_check(widget);
+ widget->endpoints[dir] = dapm_suspend_check(widget);
return widget->endpoints[dir];
}
@@ -1521,13 +1547,13 @@ static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget,
* direction as an arguments, it should return true if widgets from that point
* in the graph onwards should not be added to the widget list.
*/
-static int is_connected_output_ep(struct snd_soc_dapm_widget *widget,
+static int dapm_is_connected_output_ep(struct snd_soc_dapm_widget *widget,
struct list_head *list,
bool (*custom_stop_condition)(struct snd_soc_dapm_widget *i,
enum snd_soc_dapm_direction))
{
- return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_OUT,
- is_connected_output_ep, custom_stop_condition);
+ return dapm_is_connected_ep(widget, list, SND_SOC_DAPM_DIR_OUT,
+ dapm_is_connected_output_ep, custom_stop_condition);
}
/*
@@ -1539,13 +1565,13 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget,
* direction as an arguments, it should return true if the walk should be
* stopped and false otherwise.
*/
-static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
+static int dapm_is_connected_input_ep(struct snd_soc_dapm_widget *widget,
struct list_head *list,
bool (*custom_stop_condition)(struct snd_soc_dapm_widget *i,
enum snd_soc_dapm_direction))
{
- return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_IN,
- is_connected_input_ep, custom_stop_condition);
+ return dapm_is_connected_ep(widget, list, SND_SOC_DAPM_DIR_IN,
+ dapm_is_connected_input_ep, custom_stop_condition);
}
/**
@@ -1581,12 +1607,12 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
snd_soc_dapm_mutex_lock(card);
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- invalidate_paths_ep(w, SND_SOC_DAPM_DIR_OUT);
- paths = is_connected_output_ep(w, &widgets,
+ dapm_invalidate_paths_ep(w, SND_SOC_DAPM_DIR_OUT);
+ paths = dapm_is_connected_output_ep(w, &widgets,
custom_stop_condition);
} else {
- invalidate_paths_ep(w, SND_SOC_DAPM_DIR_IN);
- paths = is_connected_input_ep(w, &widgets,
+ dapm_invalidate_paths_ep(w, SND_SOC_DAPM_DIR_IN);
+ paths = dapm_is_connected_input_ep(w, &widgets,
custom_stop_condition);
}
@@ -1619,7 +1645,7 @@ int snd_soc_dapm_regulator_event(struct snd_soc_dapm_widget *w,
struct device *dev = snd_soc_dapm_to_dev(w->dapm);
int ret;
- soc_dapm_async_complete(w->dapm);
+ dapm_async_complete(w->dapm);
if (SND_SOC_DAPM_EVENT_ON(event)) {
if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) {
@@ -1679,7 +1705,7 @@ int snd_soc_dapm_clock_event(struct snd_soc_dapm_widget *w,
if (!w->clk)
return -EIO;
- soc_dapm_async_complete(w->dapm);
+ dapm_async_complete(w->dapm);
if (SND_SOC_DAPM_EVENT_ON(event)) {
return clk_prepare_enable(w->clk);
@@ -1714,8 +1740,8 @@ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w)
DAPM_UPDATE_STAT(w, power_checks);
- in = is_connected_input_ep(w, NULL, NULL);
- out = is_connected_output_ep(w, NULL, NULL);
+ in = dapm_is_connected_input_ep(w, NULL, NULL);
+ out = dapm_is_connected_output_ep(w, NULL, NULL);
return out != 0 && in != 0;
}
@@ -1838,9 +1864,9 @@ static void dapm_seq_check_event(struct snd_soc_card *card,
if (w->event && (w->event_flags & event)) {
int ret;
- pop_dbg(dev, card->pop_time, "pop test : %s %s\n",
+ dapm_pop_dbg(dev, card->pop_time, "pop test : %s %s\n",
w->name, ev_name);
- soc_dapm_async_complete(w->dapm);
+ dapm_async_complete(w->dapm);
trace_snd_soc_dapm_widget_event_start(w, event);
ret = w->event(w, NULL, event);
trace_snd_soc_dapm_widget_event_done(w, event);
@@ -1875,7 +1901,7 @@ static void dapm_seq_run_coalesced(struct snd_soc_card *card,
else
value |= w->off_val << w->shift;
- pop_dbg(dev, card->pop_time,
+ dapm_pop_dbg(dev, card->pop_time,
"pop test : Queue %s: reg=0x%x, 0x%x/0x%x\n",
w->name, reg, value, mask);
@@ -1889,11 +1915,11 @@ static void dapm_seq_run_coalesced(struct snd_soc_card *card,
* same register.
*/
- pop_dbg(dev, card->pop_time,
+ dapm_pop_dbg(dev, card->pop_time,
"pop test : Applying 0x%x/0x%x to %x in %dms\n",
value, mask, reg, card->pop_time);
- pop_wait(card->pop_time);
- soc_dapm_update_bits(dapm, reg, mask, value);
+ dapm_pop_wait(card->pop_time);
+ dapm_update_bits(dapm, reg, mask, value);
}
list_for_each_entry(w, pending, power_list) {
@@ -1947,7 +1973,7 @@ static void dapm_seq_run(struct snd_soc_card *card,
}
if (cur_dapm && w->dapm != cur_dapm)
- soc_dapm_async_complete(cur_dapm);
+ dapm_async_complete(cur_dapm);
INIT_LIST_HEAD(&pending);
cur_sort = -1;
@@ -2008,7 +2034,7 @@ static void dapm_seq_run(struct snd_soc_card *card,
}
for_each_card_dapms(card, d)
- soc_dapm_async_complete(d);
+ dapm_async_complete(d);
}
static void dapm_widget_update(struct snd_soc_card *card, struct snd_soc_dapm_update *update)
@@ -2036,14 +2062,14 @@ static void dapm_widget_update(struct snd_soc_card *card, struct snd_soc_dapm_up
if (!w)
return;
- ret = soc_dapm_update_bits(w->dapm, update->reg, update->mask,
+ ret = dapm_update_bits(w->dapm, update->reg, update->mask,
update->val);
if (ret < 0)
dev_err(dev, "ASoC: %s DAPM update failed: %d\n",
w->name, ret);
if (update->has_second_set) {
- ret = soc_dapm_update_bits(w->dapm, update->reg2,
+ ret = dapm_update_bits(w->dapm, update->reg2,
update->mask2, update->val2);
if (ret < 0)
dev_err(dev,
@@ -2358,9 +2384,9 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event,
return ret;
}
- pop_dbg(card->dev, card->pop_time,
+ dapm_pop_dbg(card->dev, card->pop_time,
"DAPM sequencing finished, waiting %dms\n", card->pop_time);
- pop_wait(card->pop_time);
+ dapm_pop_wait(card->pop_time);
trace_snd_soc_dapm_done(card, event);
@@ -2369,7 +2395,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event,
#ifdef CONFIG_DEBUG_FS
-static const char * const snd_soc_dapm_type_name[] = {
+static const char * const dapm_type_name[] = {
[snd_soc_dapm_input] = "input",
[snd_soc_dapm_output] = "output",
[snd_soc_dapm_mux] = "mux",
@@ -2422,7 +2448,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
struct snd_soc_dapm_path *p = NULL;
const char *c_name;
- BUILD_BUG_ON(ARRAY_SIZE(snd_soc_dapm_type_name) != SND_SOC_DAPM_TYPE_COUNT);
+ BUILD_BUG_ON(ARRAY_SIZE(dapm_type_name) != SND_SOC_DAPM_TYPE_COUNT);
buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!buf)
@@ -2430,13 +2456,13 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
snd_soc_dapm_mutex_lock_root(w->dapm);
- /* Supply widgets are not handled by is_connected_{input,output}_ep() */
+ /* Supply widgets are not handled by dapm_is_connected_{input,output}_ep() */
if (w->is_supply) {
in = 0;
out = 0;
} else {
- in = is_connected_input_ep(w, NULL, NULL);
- out = is_connected_output_ep(w, NULL, NULL);
+ in = dapm_is_connected_input_ep(w, NULL, NULL);
+ out = dapm_is_connected_output_ep(w, NULL, NULL);
}
ret = scnprintf(buf, PAGE_SIZE, "%s: %s%s in %d out %d",
@@ -2456,10 +2482,10 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
w->active ? "active" : "inactive");
ret += scnprintf(buf + ret, PAGE_SIZE - ret, " widget-type %s\n",
- snd_soc_dapm_type_name[w->id]);
+ dapm_type_name[w->id]);
- snd_soc_dapm_for_each_direction(dir) {
- rdir = SND_SOC_DAPM_DIR_REVERSE(dir);
+ dapm_for_each_direction(dir) {
+ rdir = DAPM_DIR_REVERSE(dir);
snd_soc_dapm_widget_for_each_path(w, dir, p) {
if (p->connected && !p->connected(p->source, p->sink))
continue;
@@ -2586,13 +2612,13 @@ static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm)
#endif
/*
- * soc_dapm_connect_path() - Connects or disconnects a path
+ * dapm_connect_path() - Connects or disconnects a path
* @path: The path to update
* @connect: The new connect state of the path. True if the path is connected,
* false if it is disconnected.
* @reason: The reason why the path changed (for debugging only)
*/
-static void soc_dapm_connect_path(struct snd_soc_dapm_path *path,
+static void dapm_connect_path(struct snd_soc_dapm_path *path,
bool connect, const char *reason)
{
if (path->connect == connect)
@@ -2605,10 +2631,10 @@ static void soc_dapm_connect_path(struct snd_soc_dapm_path *path,
}
/* test and update the power status of a mux widget */
-static int soc_dapm_mux_update_power(struct snd_soc_card *card,
- struct snd_kcontrol *kcontrol,
- struct snd_soc_dapm_update *update,
- int mux, struct soc_enum *e)
+static int dapm_mux_update_power(struct snd_soc_card *card,
+ struct snd_kcontrol *kcontrol,
+ struct snd_soc_dapm_update *update,
+ int mux, struct soc_enum *e)
{
struct snd_soc_dapm_path *path;
int found = 0;
@@ -2625,7 +2651,7 @@ static int soc_dapm_mux_update_power(struct snd_soc_card *card,
else
connect = false;
- soc_dapm_connect_path(path, connect, "mux update");
+ dapm_connect_path(path, connect, "mux update");
}
if (found)
@@ -2642,7 +2668,7 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm,
int ret;
snd_soc_dapm_mutex_lock(card);
- ret = soc_dapm_mux_update_power(card, kcontrol, update, mux, e);
+ ret = dapm_mux_update_power(card, kcontrol, update, mux, e);
snd_soc_dapm_mutex_unlock(card);
if (ret > 0)
snd_soc_dpcm_runtime_update(card);
@@ -2651,10 +2677,10 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm,
EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power);
/* test and update the power status of a mixer or switch widget */
-static int soc_dapm_mixer_update_power(struct snd_soc_card *card,
- struct snd_kcontrol *kcontrol,
- struct snd_soc_dapm_update *update,
- int connect, int rconnect)
+static int dapm_mixer_update_power(struct snd_soc_card *card,
+ struct snd_kcontrol *kcontrol,
+ struct snd_soc_dapm_update *update,
+ int connect, int rconnect)
{
struct snd_soc_dapm_path *path;
int found = 0;
@@ -2686,9 +2712,9 @@ static int soc_dapm_mixer_update_power(struct snd_soc_card *card,
* channel.
*/
if (found && rconnect >= 0)
- soc_dapm_connect_path(path, rconnect, "mixer update");
+ dapm_connect_path(path, rconnect, "mixer update");
else
- soc_dapm_connect_path(path, connect, "mixer update");
+ dapm_connect_path(path, connect, "mixer update");
found = 1;
}
@@ -2706,7 +2732,7 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm,
int ret;
snd_soc_dapm_mutex_lock(card);
- ret = soc_dapm_mixer_update_power(card, kcontrol, update, connect, -1);
+ ret = dapm_mixer_update_power(card, kcontrol, update, connect, -1);
snd_soc_dapm_mutex_unlock(card);
if (ret > 0)
snd_soc_dpcm_runtime_update(card);
@@ -2836,7 +2862,7 @@ void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w)
* While removing the path, remove reference to it from both
* source and sink widgets so that path is removed only once.
*/
- snd_soc_dapm_for_each_direction(dir) {
+ dapm_for_each_direction(dir) {
snd_soc_dapm_widget_for_each_path_safe(w, dir, p, next_p)
dapm_free_path(p);
}
@@ -2873,7 +2899,7 @@ static struct snd_soc_dapm_widget *dapm_find_widget(
struct snd_soc_dapm_widget *fallback = NULL;
char prefixed_pin[80];
const char *pin_name;
- const char *prefix = soc_dapm_prefix(dapm);
+ const char *prefix = dapm_prefix(dapm);
if (prefix) {
snprintf(prefixed_pin, sizeof(prefixed_pin), "%s %s",
@@ -2903,8 +2929,8 @@ static struct snd_soc_dapm_widget *dapm_find_widget(
* returns 1 when the value has been updated, 0 when unchanged, or a negative
* error code; called from kcontrol put callback
*/
-static int __snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
- const char *pin, int status)
+static int __dapm_set_pin(struct snd_soc_dapm_context *dapm,
+ const char *pin, int status)
{
struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true);
struct device *dev = snd_soc_dapm_to_dev(dapm);
@@ -2932,13 +2958,13 @@ static int __snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
}
/*
- * similar as __snd_soc_dapm_set_pin(), but returns 0 when successful;
+ * similar as __dapm_set_pin(), but returns 0 when successful;
* called from several API functions below
*/
-static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
+static int dapm_set_pin(struct snd_soc_dapm_context *dapm,
const char *pin, int status)
{
- int ret = __snd_soc_dapm_set_pin(dapm, pin, status);
+ int ret = __dapm_set_pin(dapm, pin, status);
return ret < 0 ? ret : 0;
}
@@ -3006,9 +3032,9 @@ static int dapm_update_dai_chan(struct snd_soc_dapm_path *p,
p->source->name, p->sink->name);
if (w->channel < channels)
- soc_dapm_connect_path(p, true, "dai update");
+ dapm_connect_path(p, true, "dai update");
else
- soc_dapm_connect_path(p, false, "dai update");
+ dapm_connect_path(p, false, "dai update");
return 0;
}
@@ -3086,7 +3112,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
unsigned int source_ref = 0;
int ret;
- prefix = soc_dapm_prefix(dapm);
+ prefix = dapm_prefix(dapm);
if (prefix) {
snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s",
prefix, route->sink);
@@ -3155,7 +3181,7 @@ skip_search:
dapm->wcache_sink = wsink;
dapm->wcache_source = wsource;
- ret = snd_soc_dapm_add_path(dapm, wsource, wsink, route->control,
+ ret = dapm_add_path(dapm, wsource, wsink, route->control,
route->connected);
err:
if (ret)
@@ -3185,7 +3211,7 @@ static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm,
return -EINVAL;
}
- prefix = soc_dapm_prefix(dapm);
+ prefix = dapm_prefix(dapm);
if (prefix) {
snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s",
prefix, route->sink);
@@ -3339,7 +3365,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card)
/* Read the initial power state from the device */
if (w->reg >= 0) {
- val = soc_dapm_read(w->dapm, w->reg);
+ val = dapm_read(w->dapm, w->reg);
val = val >> w->shift;
val &= w->mask;
if (val == w->on_val)
@@ -3383,11 +3409,11 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
snd_soc_dapm_mutex_lock(dapm);
if (dapm_kcontrol_is_powered(kcontrol) && reg != SND_SOC_NOPM) {
- reg_val = soc_dapm_read(dapm, reg);
+ reg_val = dapm_read(dapm, reg);
val = (reg_val >> shift) & mask;
if (reg != mc->rreg)
- reg_val = soc_dapm_read(dapm, mc->rreg);
+ reg_val = dapm_read(dapm, mc->rreg);
if (snd_soc_volsw_is_stereo(mc))
rval = (reg_val >> mc->rshift) & mask;
@@ -3471,12 +3497,12 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
val = val << shift;
rval = rval << mc->rshift;
- reg_change = soc_dapm_test_bits(dapm, reg, mask << shift, val);
+ reg_change = dapm_test_bits(dapm, reg, mask << shift, val);
if (snd_soc_volsw_is_stereo(mc))
- reg_change |= soc_dapm_test_bits(dapm, mc->rreg,
- mask << mc->rshift,
- rval);
+ reg_change |= dapm_test_bits(dapm, mc->rreg,
+ mask << mc->rshift,
+ rval);
}
if (change || reg_change) {
@@ -3493,7 +3519,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
update.val = val;
pupdate = &update;
}
- ret = soc_dapm_mixer_update_power(card, kcontrol, pupdate, connect, rconnect);
+ ret = dapm_mixer_update_power(card, kcontrol, pupdate, connect, rconnect);
}
snd_soc_dapm_mutex_unlock(card);
@@ -3523,7 +3549,7 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
snd_soc_dapm_mutex_lock(dapm);
if (e->reg != SND_SOC_NOPM && dapm_kcontrol_is_powered(kcontrol)) {
- reg_val = soc_dapm_read(dapm, e->reg);
+ reg_val = dapm_read(dapm, e->reg);
} else {
reg_val = snd_soc_dapm_kcontrol_get_value(kcontrol);
}
@@ -3580,7 +3606,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
change = dapm_kcontrol_set_value(kcontrol, val);
if (e->reg != SND_SOC_NOPM)
- reg_change = soc_dapm_test_bits(dapm, e->reg, mask, val);
+ reg_change = dapm_test_bits(dapm, e->reg, mask, val);
if (change || reg_change) {
if (reg_change) {
@@ -3590,7 +3616,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
update.val = val;
pupdate = &update;
}
- ret = soc_dapm_mux_update_power(card, kcontrol, pupdate, item[0], e);
+ ret = dapm_mux_update_power(card, kcontrol, pupdate, item[0], e);
}
snd_soc_dapm_mutex_unlock(card);
@@ -3673,14 +3699,14 @@ int snd_soc_dapm_get_component_pin_switch(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_get_component_pin_switch);
-static int __snd_soc_dapm_put_pin_switch(struct snd_soc_dapm_context *dapm,
- const char *pin,
- struct snd_ctl_elem_value *ucontrol)
+static int __dapm_put_pin_switch(struct snd_soc_dapm_context *dapm,
+ const char *pin,
+ struct snd_ctl_elem_value *ucontrol)
{
int ret;
snd_soc_dapm_mutex_lock(dapm);
- ret = __snd_soc_dapm_set_pin(dapm, pin, !!ucontrol->value.integer.value[0]);
+ ret = __dapm_set_pin(dapm, pin, !!ucontrol->value.integer.value[0]);
snd_soc_dapm_mutex_unlock(dapm);
snd_soc_dapm_sync(dapm);
@@ -3704,7 +3730,7 @@ int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol,
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
const char *pin = (const char *)kcontrol->private_value;
- return __snd_soc_dapm_put_pin_switch(dapm, pin, ucontrol);
+ return __dapm_put_pin_switch(dapm, pin, ucontrol);
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_pin_switch);
@@ -3724,7 +3750,7 @@ int snd_soc_dapm_put_component_pin_switch(struct snd_kcontrol *kcontrol,
struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
const char *pin = (const char *)kcontrol->private_value;
- return __snd_soc_dapm_put_pin_switch(dapm, pin, ucontrol);
+ return __dapm_put_pin_switch(dapm, pin, ucontrol);
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_component_pin_switch);
@@ -3737,7 +3763,7 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_widget *w;
int ret = -ENOMEM;
- w = dapm_cnew_widget(widget, soc_dapm_prefix(dapm));
+ w = dapm_cnew_widget(widget, dapm_prefix(dapm));
if (!w)
goto cnew_failed;
@@ -3852,7 +3878,7 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
/* see for_each_card_widgets */
list_add_tail(&w->list, &dapm->card->widgets);
- snd_soc_dapm_for_each_direction(dir) {
+ dapm_for_each_direction(dir) {
INIT_LIST_HEAD(&w->edges[dir]);
w->endpoints[dir] = -1;
}
@@ -3925,9 +3951,8 @@ int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm,
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_new_controls);
-static int
-snd_soc_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w,
- struct snd_pcm_substream *substream)
+static int dapm_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w,
+ struct snd_pcm_substream *substream)
{
struct device *dev = snd_soc_dapm_to_dev(w->dapm);
struct snd_soc_dapm_path *path;
@@ -4042,8 +4067,8 @@ snd_soc_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w,
return 0;
}
-static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+static int dapm_dai_link_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_dapm_path *path;
struct snd_soc_dai *source, *sink;
@@ -4056,7 +4081,7 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- ret = snd_soc_dai_link_event_pre_pmu(w, substream);
+ ret = dapm_dai_link_event_pre_pmu(w, substream);
if (ret < 0)
goto out;
@@ -4134,8 +4159,8 @@ out:
return ret;
}
-static int snd_soc_dapm_dai_link_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int dapm_dai_link_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol);
struct snd_soc_pcm_runtime *rtd = w->priv;
@@ -4145,8 +4170,8 @@ static int snd_soc_dapm_dai_link_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static int snd_soc_dapm_dai_link_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int dapm_dai_link_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol);
struct snd_soc_pcm_runtime *rtd = w->priv;
@@ -4166,11 +4191,10 @@ static int snd_soc_dapm_dai_link_put(struct snd_kcontrol *kcontrol,
return 1;
}
-static void
-snd_soc_dapm_free_kcontrol(struct snd_soc_card *card,
- unsigned long *private_value,
- int num_c2c_params,
- const char **w_param_text)
+static void dapm_free_kcontrol(struct snd_soc_card *card,
+ unsigned long *private_value,
+ int num_c2c_params,
+ const char **w_param_text)
{
int count;
@@ -4185,7 +4209,7 @@ snd_soc_dapm_free_kcontrol(struct snd_soc_card *card,
}
static struct snd_kcontrol_new *
-snd_soc_dapm_alloc_kcontrol(struct snd_soc_card *card,
+dapm_alloc_kcontrol(struct snd_soc_card *card,
char *link_name,
const struct snd_soc_pcm_stream *c2c_params,
int num_c2c_params, const char **w_param_text,
@@ -4196,8 +4220,8 @@ snd_soc_dapm_alloc_kcontrol(struct snd_soc_card *card,
};
struct snd_kcontrol_new kcontrol_dai_link[] = {
SOC_ENUM_EXT(NULL, w_param_enum[0],
- snd_soc_dapm_dai_link_get,
- snd_soc_dapm_dai_link_put),
+ dapm_dai_link_get,
+ dapm_dai_link_put),
};
struct snd_kcontrol_new *kcontrol_news;
const struct snd_soc_pcm_stream *config = c2c_params;
@@ -4248,14 +4272,14 @@ snd_soc_dapm_alloc_kcontrol(struct snd_soc_card *card,
return kcontrol_news;
outfree_w_param:
- snd_soc_dapm_free_kcontrol(card, private_value, num_c2c_params, w_param_text);
+ dapm_free_kcontrol(card, private_value, num_c2c_params, w_param_text);
+
return NULL;
}
-static struct snd_soc_dapm_widget *
-snd_soc_dapm_new_dai(struct snd_soc_card *card,
- struct snd_pcm_substream *substream,
- char *id)
+static struct snd_soc_dapm_widget *dapm_new_dai(struct snd_soc_card *card,
+ struct snd_pcm_substream *substream,
+ char *id)
{
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
@@ -4285,10 +4309,10 @@ snd_soc_dapm_new_dai(struct snd_soc_card *card,
goto param_fail;
num_kcontrols = 1;
- kcontrol_news = snd_soc_dapm_alloc_kcontrol(card, link_name,
- rtd->dai_link->c2c_params,
- rtd->dai_link->num_c2c_params,
- w_param_text, &private_value);
+ kcontrol_news = dapm_alloc_kcontrol(card, link_name,
+ rtd->dai_link->c2c_params,
+ rtd->dai_link->num_c2c_params,
+ w_param_text, &private_value);
if (!kcontrol_news)
goto param_fail;
}
@@ -4297,7 +4321,7 @@ snd_soc_dapm_new_dai(struct snd_soc_card *card,
template.reg = SND_SOC_NOPM;
template.id = snd_soc_dapm_dai_link;
template.name = link_name;
- template.event = snd_soc_dai_link_event;
+ template.event = dapm_dai_link_event;
template.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD;
template.kcontrol_news = kcontrol_news;
@@ -4317,7 +4341,7 @@ snd_soc_dapm_new_dai(struct snd_soc_card *card,
outfree_kcontrol_news:
devm_kfree(card->dev, (void *)template.kcontrol_news);
- snd_soc_dapm_free_kcontrol(card, &private_value,
+ dapm_free_kcontrol(card, &private_value,
rtd->dai_link->num_c2c_params, w_param_text);
param_fail:
devm_kfree(card->dev, link_name);
@@ -4431,7 +4455,7 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card)
sink = dai_w;
}
dev_dbg(dai->dev, "%s -> %s\n", src->name, sink->name);
- snd_soc_dapm_add_path(w->dapm, src, sink, NULL, NULL);
+ dapm_add_path(w->dapm, src, sink, NULL, NULL);
}
}
@@ -4452,11 +4476,11 @@ static void dapm_connect_dai_routes(struct snd_soc_dapm_context *dapm,
sink_dai->component->name, sink->name);
if (dai) {
- snd_soc_dapm_add_path(dapm, src, dai, NULL, NULL);
+ dapm_add_path(dapm, src, dai, NULL, NULL);
src = dai;
}
- snd_soc_dapm_add_path(dapm, src, sink, NULL, NULL);
+ dapm_add_path(dapm, src, sink, NULL, NULL);
}
static void dapm_connect_dai_pair(struct snd_soc_card *card,
@@ -4490,8 +4514,8 @@ static void dapm_connect_dai_pair(struct snd_soc_card *card,
/* special handling for [Codec2Codec] */
if (dai_link->c2c_params && !rtd->c2c_widget[stream]) {
struct snd_pcm_substream *substream = rtd->pcm->streams[stream].substream;
- struct snd_soc_dapm_widget *dai = snd_soc_dapm_new_dai(card, substream,
- widget_name[stream]);
+ struct snd_soc_dapm_widget *dai = dapm_new_dai(card, substream,
+ widget_name[stream]);
if (IS_ERR(dai))
continue;
@@ -4505,8 +4529,7 @@ static void dapm_connect_dai_pair(struct snd_soc_card *card,
}
}
-static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream,
- int event)
+static void dapm_dai_stream_event(struct snd_soc_dai *dai, int stream, int event)
{
struct snd_soc_dapm_widget *w;
@@ -4574,14 +4597,13 @@ void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card)
}
}
-static void soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
- int event)
+static void dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, int event)
{
struct snd_soc_dai *dai;
int i;
for_each_rtd_dais(rtd, i, dai)
- soc_dapm_dai_stream_event(dai, stream, event);
+ dapm_dai_stream_event(dai, stream, event);
dapm_power_widgets(rtd->card, event, NULL);
}
@@ -4603,7 +4625,7 @@ void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
struct snd_soc_card *card = rtd->card;
snd_soc_dapm_mutex_lock(card);
- soc_dapm_stream_event(rtd, stream, event);
+ dapm_stream_event(rtd, stream, event);
snd_soc_dapm_mutex_unlock(card);
}
@@ -4646,7 +4668,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_stop);
int snd_soc_dapm_enable_pin_unlocked(struct snd_soc_dapm_context *dapm,
const char *pin)
{
- return snd_soc_dapm_set_pin(dapm, pin, 1);
+ return dapm_set_pin(dapm, pin, 1);
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin_unlocked);
@@ -4667,7 +4689,7 @@ int snd_soc_dapm_enable_pin(struct snd_soc_dapm_context *dapm, const char *pin)
snd_soc_dapm_mutex_lock(dapm);
- ret = snd_soc_dapm_set_pin(dapm, pin, 1);
+ ret = dapm_set_pin(dapm, pin, 1);
snd_soc_dapm_mutex_unlock(dapm);
@@ -4763,7 +4785,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_force_enable_pin);
int snd_soc_dapm_disable_pin_unlocked(struct snd_soc_dapm_context *dapm,
const char *pin)
{
- return snd_soc_dapm_set_pin(dapm, pin, 0);
+ return dapm_set_pin(dapm, pin, 0);
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin_unlocked);
@@ -4784,7 +4806,7 @@ int snd_soc_dapm_disable_pin(struct snd_soc_dapm_context *dapm,
snd_soc_dapm_mutex_lock(dapm);
- ret = snd_soc_dapm_set_pin(dapm, pin, 0);
+ ret = dapm_set_pin(dapm, pin, 0);
snd_soc_dapm_mutex_unlock(dapm);
@@ -4862,19 +4884,15 @@ void snd_soc_dapm_init(struct snd_soc_dapm_context *dapm,
dapm->component = component;
dapm->bias_level = SND_SOC_BIAS_OFF;
- if (component) {
- dapm->dev = component->dev;
+ if (component)
dapm->idle_bias = component->driver->idle_bias_on;
- } else {
- dapm->dev = card->dev;
- }
INIT_LIST_HEAD(&dapm->list);
/* see for_each_card_dapms */
list_add(&dapm->list, &card->dapm_list);
}
-static void soc_dapm_shutdown_dapm(struct snd_soc_dapm_context *dapm)
+static void dapm_shutdown(struct snd_soc_dapm_context *dapm)
{
struct snd_soc_card *card = dapm->card;
struct snd_soc_dapm_widget *w;
@@ -4919,13 +4937,13 @@ void snd_soc_dapm_shutdown(struct snd_soc_card *card)
for_each_card_dapms(card, dapm) {
if (dapm != card_dapm) {
- soc_dapm_shutdown_dapm(dapm);
+ dapm_shutdown(dapm);
if (dapm->bias_level == SND_SOC_BIAS_STANDBY)
snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_OFF);
}
}
- soc_dapm_shutdown_dapm(card_dapm);
+ dapm_shutdown(card_dapm);
if (card_dapm->bias_level == SND_SOC_BIAS_STANDBY)
snd_soc_dapm_set_bias_level(card_dapm, SND_SOC_BIAS_OFF);
}
diff --git a/sound/soc/sof/amd/acp-ipc.c b/sound/soc/sof/amd/acp-ipc.c
index 22d4b807e1bb..3cd4674dd800 100644
--- a/sound/soc/sof/amd/acp-ipc.c
+++ b/sound/soc/sof/amd/acp-ipc.c
@@ -190,15 +190,13 @@ irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context)
dsp_ack = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_ack_write);
if (dsp_ack) {
if (likely(sdev->fw_state == SOF_FW_BOOT_COMPLETE)) {
- spin_lock_irq(&sdev->ipc_lock);
+ guard(spinlock_irq)(&sdev->ipc_lock);
/* handle immediate reply from DSP core */
acp_dsp_ipc_get_reply(sdev);
snd_sof_ipc_reply(sdev, 0);
/* set the done bit */
acp_dsp_ipc_dsp_done(sdev);
-
- spin_unlock_irq(&sdev->ipc_lock);
} else {
dev_dbg_ratelimited(sdev->dev, "IPC reply before FW_BOOT_COMPLETE: %#x\n",
dsp_ack);
diff --git a/sound/soc/sof/compress.c b/sound/soc/sof/compress.c
index 90b932ae3bab..86d563c864e5 100644
--- a/sound/soc/sof/compress.c
+++ b/sound/soc/sof/compress.c
@@ -195,6 +195,14 @@ static int sof_compr_set_params(struct snd_soc_component *component,
if (sizeof(*pcm) + ext_data_size > sdev->ipc->max_payload_size)
return -EINVAL;
+ /*
+ * Make sure that the DSP is booted up, which might not be the
+ * case if the on-demand DSP boot is used
+ */
+ ret = snd_sof_boot_dsp_firmware(sdev);
+ if (ret)
+ return ret;
+
pcm = kzalloc(sizeof(*pcm) + ext_data_size, GFP_KERNEL);
if (!pcm)
return -ENOMEM;
diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c
index a3fd1d523c09..74d997a4f620 100644
--- a/sound/soc/sof/control.c
+++ b/sound/soc/sof/control.c
@@ -187,14 +187,23 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _
const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
int ret, err;
+ /* ignore the ext_volatile_get call if the callbacks are not provided */
+ if (!tplg_ops || !tplg_ops->control ||
+ !tplg_ops->control->bytes_ext_volatile_get)
+ return 0;
+
ret = pm_runtime_resume_and_get(scomp->dev);
if (ret < 0 && ret != -EACCES) {
dev_err_ratelimited(scomp->dev, "%s: failed to resume %d\n", __func__, ret);
return ret;
}
- if (tplg_ops && tplg_ops->control && tplg_ops->control->bytes_ext_volatile_get)
- ret = tplg_ops->control->bytes_ext_volatile_get(scontrol, binary_data, size);
+ /* Make sure the DSP/firmware is booted up */
+ ret = snd_sof_boot_dsp_firmware(sdev);
+ if (!ret)
+ ret = tplg_ops->control->bytes_ext_volatile_get(scontrol,
+ binary_data,
+ size);
err = pm_runtime_put_autosuspend(scomp->dev);
if (err < 0)
diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c
index b11f408f1366..2d394389c945 100644
--- a/sound/soc/sof/core.c
+++ b/sound/soc/sof/core.c
@@ -680,6 +680,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
mutex_init(&sdev->power_state_access);
mutex_init(&sdev->ipc_client_mutex);
mutex_init(&sdev->client_event_handler_mutex);
+ mutex_init(&sdev->dsp_fw_boot_mutex);
/* set default timeouts if none provided */
if (plat_data->desc->ipc_timeout == 0)
diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c
index b24943a65c89..6b9e1f1ee657 100644
--- a/sound/soc/sof/debug.c
+++ b/sound/soc/sof/debug.c
@@ -216,7 +216,12 @@ static int memory_info_update(struct snd_sof_dev *sdev, char *buf, size_t buff_s
goto error;
}
- ret = sof_ipc_tx_message(sdev->ipc, &msg, msg.size, reply, SOF_IPC_MSG_MAX_SIZE);
+ /* Make sure the DSP/firmware is booted up */
+ ret = snd_sof_boot_dsp_firmware(sdev);
+ if (!ret)
+ ret = sof_ipc_tx_message(sdev->ipc, &msg, msg.size, reply,
+ SOF_IPC_MSG_MAX_SIZE);
+
pm_runtime_put_autosuspend(sdev->dev);
if (ret < 0 || reply->rhdr.error < 0) {
ret = min(ret, reply->rhdr.error);
diff --git a/sound/soc/sof/imx/imx-common.c b/sound/soc/sof/imx/imx-common.c
index e787d3932fbb..7a03c8cc5dd4 100644
--- a/sound/soc/sof/imx/imx-common.c
+++ b/sound/soc/sof/imx/imx-common.c
@@ -81,14 +81,10 @@ EXPORT_SYMBOL(imx8_dump);
static void imx_handle_reply(struct imx_dsp_ipc *ipc)
{
- struct snd_sof_dev *sdev;
- unsigned long flags;
-
- sdev = imx_dsp_get_data(ipc);
+ struct snd_sof_dev *sdev = imx_dsp_get_data(ipc);
- spin_lock_irqsave(&sdev->ipc_lock, flags);
+ guard(spinlock_irqsave)(&sdev->ipc_lock);
snd_sof_ipc_process_reply(sdev, 0);
- spin_unlock_irqrestore(&sdev->ipc_lock, flags);
}
static void imx_handle_request(struct imx_dsp_ipc *ipc)
diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig
index 54cd3807f8c6..e31f4c4061d8 100644
--- a/sound/soc/sof/intel/Kconfig
+++ b/sound/soc/sof/intel/Kconfig
@@ -319,6 +319,7 @@ config SND_SOC_SOF_NOVALAKE
config SND_SOC_SOF_HDA_COMMON
tristate
+ select SND_HDA_EXT_CORE
config SND_SOC_SOF_HDA_GENERIC
tristate
diff --git a/sound/soc/sof/intel/atom.c b/sound/soc/sof/intel/atom.c
index 0d364bcdcfa9..32bf5e5e5978 100644
--- a/sound/soc/sof/intel/atom.c
+++ b/sound/soc/sof/intel/atom.c
@@ -143,9 +143,6 @@ irqreturn_t atom_irq_thread(int irq, void *context)
/* reply message from DSP */
if (ipcx & SHIM_BYT_IPCX_DONE) {
-
- spin_lock_irq(&sdev->ipc_lock);
-
/*
* handle immediate reply from DSP core. If the msg is
* found, set done bit in cmd_done which is called at the
@@ -153,11 +150,9 @@ irqreturn_t atom_irq_thread(int irq, void *context)
* because the done bit can't be set in cmd_done function
* which is triggered by msg
*/
+ guard(spinlock_irq)(&sdev->ipc_lock);
snd_sof_ipc_process_reply(sdev, ipcx);
-
atom_dsp_done(sdev);
-
- spin_unlock_irq(&sdev->ipc_lock);
}
/* new message from DSP */
diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c
index f1287d509835..9534d18be97d 100644
--- a/sound/soc/sof/intel/bdw.c
+++ b/sound/soc/sof/intel/bdw.c
@@ -315,9 +315,6 @@ static irqreturn_t bdw_irq_thread(int irq, void *context)
snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR,
SHIM_IMRX, SHIM_IMRX_DONE,
SHIM_IMRX_DONE);
-
- spin_lock_irq(&sdev->ipc_lock);
-
/*
* handle immediate reply from DSP core. If the msg is
* found, set done bit in cmd_done which is called at the
@@ -325,11 +322,9 @@ static irqreturn_t bdw_irq_thread(int irq, void *context)
* because the done bit can't be set in cmd_done function
* which is triggered by msg
*/
+ guard(spinlock_irq)(&sdev->ipc_lock);
snd_sof_ipc_process_reply(sdev, ipcx);
-
bdw_dsp_done(sdev);
-
- spin_unlock_irq(&sdev->ipc_lock);
}
ipcd = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IPCD);
diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c
index 0cc5725515e7..69376fb5b20d 100644
--- a/sound/soc/sof/intel/cnl.c
+++ b/sound/soc/sof/intel/cnl.c
@@ -69,13 +69,10 @@ irqreturn_t cnl_ipc4_irq_thread(int irq, void *context)
data->primary = primary;
data->extension = extension;
- spin_lock_irq(&sdev->ipc_lock);
-
+ guard(spinlock_irq)(&sdev->ipc_lock);
snd_sof_ipc_get_reply(sdev);
cnl_ipc_host_done(sdev);
snd_sof_ipc_reply(sdev, data->primary);
-
- spin_unlock_irq(&sdev->ipc_lock);
} else {
dev_dbg_ratelimited(sdev->dev,
"IPC reply before FW_READY: %#x|%#x\n",
@@ -141,15 +138,11 @@ irqreturn_t cnl_ipc_irq_thread(int irq, void *context)
CNL_DSP_REG_HIPCCTL_DONE, 0);
if (likely(sdev->fw_state == SOF_FW_BOOT_COMPLETE)) {
- spin_lock_irq(&sdev->ipc_lock);
-
/* handle immediate reply from DSP core */
+ guard(spinlock_irq)(&sdev->ipc_lock);
hda_dsp_ipc_get_reply(sdev);
snd_sof_ipc_reply(sdev, msg);
-
cnl_ipc_dsp_done(sdev);
-
- spin_unlock_irq(&sdev->ipc_lock);
} else {
dev_dbg_ratelimited(sdev->dev, "IPC reply before FW_READY: %#x\n",
msg);
diff --git a/sound/soc/sof/intel/hda-codec.c b/sound/soc/sof/intel/hda-codec.c
index 37674ea452d6..fd371850b0d6 100644
--- a/sound/soc/sof/intel/hda-codec.c
+++ b/sound/soc/sof/intel/hda-codec.c
@@ -451,7 +451,6 @@ int hda_codec_i915_exit(struct snd_sof_dev *sdev)
}
EXPORT_SYMBOL_NS_GPL(hda_codec_i915_exit, "SND_SOC_SOF_HDA_AUDIO_CODEC_I915");
-MODULE_SOFTDEP("pre: snd-hda-codec-hdmi");
#endif
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c
index 92681ca7f24d..b2c559559962 100644
--- a/sound/soc/sof/intel/hda-dai-ops.c
+++ b/sound/soc/sof/intel/hda-dai-ops.c
@@ -58,7 +58,7 @@ hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream
return NULL;
}
- spin_lock_irq(&bus->reg_lock);
+ guard(spinlock_irq)(&bus->reg_lock);
list_for_each_entry(hstream, &bus->stream_list, list) {
struct hdac_ext_stream *hext_stream =
stream_to_hdac_ext_stream(hstream);
@@ -110,7 +110,6 @@ hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream
res->link_locked = 1;
res->link_substream = substream;
}
- spin_unlock_irq(&bus->reg_lock);
return res;
}
@@ -311,7 +310,7 @@ static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cp
if (pipe_widget->instance_id < 0)
return 0;
- mutex_lock(&ipc4_data->pipeline_state_mutex);
+ guard(mutex)(&ipc4_data->pipeline_state_mutex);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -323,16 +322,16 @@ static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cp
ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
SOF_IPC4_PIPE_PAUSED);
if (ret < 0)
- goto out;
+ return ret;
pipeline->state = SOF_IPC4_PIPE_PAUSED;
+
break;
default:
dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
ret = -EINVAL;
}
-out:
- mutex_unlock(&ipc4_data->pipeline_state_mutex);
+
return ret;
}
@@ -388,7 +387,7 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c
if (pipe_widget->instance_id < 0)
return 0;
- mutex_lock(&ipc4_data->pipeline_state_mutex);
+ guard(mutex)(&ipc4_data->pipeline_state_mutex);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -396,14 +395,16 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c
ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
SOF_IPC4_PIPE_PAUSED);
if (ret < 0)
- goto out;
+ return ret;
+
pipeline->state = SOF_IPC4_PIPE_PAUSED;
}
ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
SOF_IPC4_PIPE_RUNNING);
if (ret < 0)
- goto out;
+ return ret;
+
pipeline->state = SOF_IPC4_PIPE_RUNNING;
swidget->spipe->started_count++;
break;
@@ -411,7 +412,8 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c
ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
SOF_IPC4_PIPE_RUNNING);
if (ret < 0)
- goto out;
+ return ret;
+
pipeline->state = SOF_IPC4_PIPE_RUNNING;
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
@@ -429,8 +431,7 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c
ret = -EINVAL;
break;
}
-out:
- mutex_unlock(&ipc4_data->pipeline_state_mutex);
+
return ret;
}
diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c
index 883d0d3bae9e..15faedeec16d 100644
--- a/sound/soc/sof/intel/hda-dai.c
+++ b/sound/soc/sof/intel/hda-dai.c
@@ -70,12 +70,22 @@ static const struct hda_dai_widget_dma_ops *
hda_dai_get_ops(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai)
{
struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
- struct snd_sof_widget *swidget = w->dobj.private;
+ struct snd_sof_widget *swidget;
struct snd_sof_dev *sdev;
struct snd_sof_dai *sdai;
- sdev = widget_to_sdev(w);
+ /*
+ * this is unlikely if the topology and the machine driver DAI links match.
+ * But if there's a missing DAI link in topology, this will prevent a NULL pointer
+ * dereference later on.
+ */
+ if (!w) {
+ dev_err(cpu_dai->dev, "%s: widget is NULL\n", __func__);
+ return NULL;
+ }
+ sdev = widget_to_sdev(w);
+ swidget = w->dobj.private;
if (!swidget) {
dev_err(sdev->dev, "%s: swidget is NULL\n", __func__);
return NULL;
@@ -856,6 +866,14 @@ struct snd_soc_dai_driver skl_dai[] = {
.channels_max = 4,
},
},
+{
+ /* Virtual CPU DAI for Echo reference */
+ .name = "Loopback Virtual Pin",
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+},
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
{
.name = "iDisp1 Pin",
diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c
index 94425c510861..2aef3954f4f7 100644
--- a/sound/soc/sof/intel/hda-ipc.c
+++ b/sound/soc/sof/intel/hda-ipc.c
@@ -204,13 +204,10 @@ irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context)
data->primary = primary;
data->extension = extension;
- spin_lock_irq(&sdev->ipc_lock);
-
+ guard(spinlock_irq)(&sdev->ipc_lock);
snd_sof_ipc_get_reply(sdev);
hda_dsp_ipc_host_done(sdev);
snd_sof_ipc_reply(sdev, data->primary);
-
- spin_unlock_irq(&sdev->ipc_lock);
} else {
dev_dbg_ratelimited(sdev->dev,
"IPC reply before FW_READY: %#x|%#x\n",
@@ -289,16 +286,12 @@ irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context)
* reply.
*/
if (likely(sdev->fw_state == SOF_FW_BOOT_COMPLETE)) {
- spin_lock_irq(&sdev->ipc_lock);
-
/* handle immediate reply from DSP core */
+ guard(spinlock_irq)(&sdev->ipc_lock);
hda_dsp_ipc_get_reply(sdev);
snd_sof_ipc_reply(sdev, msg);
-
/* set the done bit */
hda_dsp_ipc_dsp_done(sdev);
-
- spin_unlock_irq(&sdev->ipc_lock);
} else {
dev_dbg_ratelimited(sdev->dev, "IPC reply before FW_READY: %#x\n",
msg);
diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c
index 2cc11d8b0f70..2b3abcf75d55 100644
--- a/sound/soc/sof/intel/hda-loader.c
+++ b/sound/soc/sof/intel/hda-loader.c
@@ -53,65 +53,8 @@ hda_cl_prepare(struct device *dev, unsigned int format, unsigned int size,
struct snd_dma_buffer *dmab, bool persistent_buffer, int direction,
bool is_iccmax)
{
- struct snd_sof_dev *sdev = dev_get_drvdata(dev);
- struct hdac_ext_stream *hext_stream;
- struct hdac_stream *hstream;
- int ret;
-
- hext_stream = hda_dsp_stream_get(sdev, direction, 0);
-
- if (!hext_stream) {
- dev_err(sdev->dev, "error: no stream available\n");
- return ERR_PTR(-ENODEV);
- }
- hstream = &hext_stream->hstream;
- hstream->substream = NULL;
-
- /*
- * Allocate DMA buffer if it is temporary or if the buffer is intended
- * to be persistent but not yet allocated.
- * We cannot rely solely on !dmab->area as caller might use a struct on
- * stack (when it is temporary) without clearing it to 0.
- */
- if (!persistent_buffer || !dmab->area) {
- ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, dev, size, dmab);
- if (ret < 0) {
- dev_err(sdev->dev, "%s: memory alloc failed: %d\n",
- __func__, ret);
- goto out_put;
- }
- }
-
- hstream->period_bytes = 0;/* initialize period_bytes */
- hstream->format_val = format;
- hstream->bufsize = size;
-
- if (is_iccmax) {
- ret = hda_dsp_iccmax_stream_hw_params(sdev, hext_stream, dmab, NULL);
- if (ret < 0) {
- dev_err(sdev->dev, "error: iccmax stream prepare failed: %d\n", ret);
- goto out_free;
- }
- } else {
- ret = hda_dsp_stream_hw_params(sdev, hext_stream, dmab, NULL);
- if (ret < 0) {
- dev_err(sdev->dev, "error: hdac prepare failed: %d\n", ret);
- goto out_free;
- }
- hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_ENABLE, size);
- }
-
- return hext_stream;
-
-out_free:
- snd_dma_free_pages(dmab);
- dmab->area = NULL;
- dmab->bytes = 0;
- hstream->bufsize = 0;
- hstream->format_val = 0;
-out_put:
- hda_dsp_stream_put(sdev, direction, hstream->stream_tag);
- return ERR_PTR(ret);
+ return hda_data_stream_prepare(dev, format, size, dmab, persistent_buffer,
+ direction, is_iccmax, false);
}
EXPORT_SYMBOL_NS(hda_cl_prepare, "SND_SOC_SOF_INTEL_HDA_COMMON");
@@ -275,38 +218,7 @@ EXPORT_SYMBOL_NS(hda_cl_trigger, "SND_SOC_SOF_INTEL_HDA_COMMON");
int hda_cl_cleanup(struct device *dev, struct snd_dma_buffer *dmab,
bool persistent_buffer, struct hdac_ext_stream *hext_stream)
{
- struct snd_sof_dev *sdev = dev_get_drvdata(dev);
- struct hdac_stream *hstream = &hext_stream->hstream;
- int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
- int ret = 0;
-
- if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK)
- ret = hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0);
- else
- snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset,
- SOF_HDA_SD_CTL_DMA_START, 0);
-
- hda_dsp_stream_put(sdev, hstream->direction, hstream->stream_tag);
- hstream->running = 0;
- hstream->substream = NULL;
-
- /* reset BDL address */
- snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
- sd_offset + SOF_HDA_ADSP_REG_SD_BDLPL, 0);
- snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
- sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU, 0);
-
- snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, sd_offset, 0);
-
- if (!persistent_buffer) {
- snd_dma_free_pages(dmab);
- dmab->area = NULL;
- dmab->bytes = 0;
- hstream->bufsize = 0;
- hstream->format_val = 0;
- }
-
- return ret;
+ return hda_data_stream_cleanup(dev, dmab, persistent_buffer, hext_stream, false);
}
EXPORT_SYMBOL_NS(hda_cl_cleanup, "SND_SOC_SOF_INTEL_HDA_COMMON");
diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c
index ce561fe52bd5..6f15213937a3 100644
--- a/sound/soc/sof/intel/hda-mlink.c
+++ b/sound/soc/sof/intel/hda-mlink.c
@@ -524,11 +524,8 @@ void hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, boo
hlink = &h2link->hext_link;
- mutex_lock(&h2link->eml_lock);
-
- hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
-
- mutex_unlock(&h2link->eml_lock);
+ scoped_guard(mutex, &h2link->eml_lock)
+ hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
}
EXPORT_SYMBOL_NS(hdac_bus_eml_enable_interrupt, "SND_SOC_SOF_HDA_MLINK");
@@ -837,11 +834,8 @@ int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num)
hlink = &h2link->hext_link;
- mutex_lock(&h2link->eml_lock);
-
- hdaml_link_set_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink), dev_num);
-
- mutex_unlock(&h2link->eml_lock);
+ scoped_guard(mutex, &h2link->eml_lock)
+ hdaml_link_set_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink), dev_num);
return 0;
} EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_lsdiid, "SND_SOC_SOF_HDA_MLINK");
@@ -875,12 +869,8 @@ int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y,
lchan = 0;
}
- mutex_lock(&h2link->eml_lock);
-
- hdaml_shim_map_stream_ch(pcmsycm, lchan, hchan,
- stream_id, dir);
-
- mutex_unlock(&h2link->eml_lock);
+ scoped_guard(mutex, &h2link->eml_lock)
+ hdaml_shim_map_stream_ch(pcmsycm, lchan, hchan, stream_id, dir);
val = readw(pcmsycm);
@@ -1012,11 +1002,8 @@ int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool e
hlink = &h2link->hext_link;
- mutex_lock(&h2link->eml_lock);
-
- hdaml_lctl_offload_enable(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
-
- mutex_unlock(&h2link->eml_lock);
+ scoped_guard(mutex, &h2link->eml_lock)
+ hdaml_lctl_offload_enable(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
return 0;
}
diff --git a/sound/soc/sof/intel/hda-sdw-bpt.c b/sound/soc/sof/intel/hda-sdw-bpt.c
index e45dd051ab8c..728ffe7ae54d 100644
--- a/sound/soc/sof/intel/hda-sdw-bpt.c
+++ b/sound/soc/sof/intel/hda-sdw-bpt.c
@@ -98,6 +98,17 @@ static int hda_sdw_bpt_dma_prepare(struct device *dev, struct hdac_ext_stream **
struct hdac_ext_stream *bpt_stream;
unsigned int format = HDA_CL_STREAM_FORMAT;
+ if (!sdev->dspless_mode_selected) {
+ int ret;
+
+ /*
+ * Make sure that the DSP is booted up, which might not be the
+ * case if the on-demand DSP boot is used
+ */
+ ret = snd_sof_boot_dsp_firmware(sdev);
+ if (ret)
+ return ret;
+ }
/*
* the baseline format needs to be adjusted to
* bandwidth requirements
@@ -107,7 +118,8 @@ static int hda_sdw_bpt_dma_prepare(struct device *dev, struct hdac_ext_stream **
dev_dbg(dev, "direction %d format_val %#x\n", direction, format);
- bpt_stream = hda_cl_prepare(dev, format, bpt_num_bytes, dmab_bdl, false, direction, false);
+ bpt_stream = hda_data_stream_prepare(dev, format, bpt_num_bytes, dmab_bdl,
+ false, direction, false, true);
if (IS_ERR(bpt_stream)) {
dev_err(sdev->dev, "%s: SDW BPT DMA prepare failed: dir %d\n",
__func__, direction);
@@ -151,7 +163,7 @@ static int hda_sdw_bpt_dma_deprepare(struct device *dev, struct hdac_ext_stream
u32 mask;
int ret;
- ret = hda_cl_cleanup(sdev->dev, dmab_bdl, false, sdw_bpt_stream);
+ ret = hda_data_stream_cleanup(sdev->dev, dmab_bdl, false, sdw_bpt_stream, true);
if (ret < 0) {
dev_err(sdev->dev, "%s: SDW BPT DMA cleanup failed\n",
__func__);
diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c
index 9c3b3a9aaf83..1c04b5d9c0d8 100644
--- a/sound/soc/sof/intel/hda-stream.c
+++ b/sound/soc/sof/intel/hda-stream.c
@@ -210,8 +210,8 @@ int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev,
}
/* get next unused stream */
-struct hdac_ext_stream *
-hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags)
+static struct hdac_ext_stream *
+_hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags, bool pair)
{
const struct sof_intel_dsp_desc *chip_info = get_chip_info(sdev->pdata);
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
@@ -233,7 +233,14 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags)
if (hda_stream->host_reserved)
continue;
+ if (pair && hext_stream->link_locked)
+ continue;
+
s->opened = true;
+
+ if (pair)
+ hext_stream->link_locked = true;
+
break;
}
}
@@ -264,14 +271,27 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags)
return hext_stream;
}
+struct hdac_ext_stream *
+hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags)
+{
+ return _hda_dsp_stream_get(sdev, direction, flags, false);
+}
+
+struct hdac_ext_stream *
+hda_dsp_stream_pair_get(struct snd_sof_dev *sdev, int direction, u32 flags)
+{
+ return _hda_dsp_stream_get(sdev, direction, flags, true);
+}
+
/* free a stream */
-int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag)
+static int _hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag, bool pair)
{
const struct sof_intel_dsp_desc *chip_info = get_chip_info(sdev->pdata);
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
struct hdac_bus *bus = sof_to_bus(sdev);
struct sof_intel_hda_stream *hda_stream;
struct hdac_ext_stream *hext_stream;
+ struct hdac_ext_stream *link_stream;
struct hdac_stream *s;
bool dmi_l1_enable = true;
bool found = false;
@@ -292,6 +312,8 @@ int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag)
if (s->direction == direction && s->stream_tag == stream_tag) {
s->opened = false;
found = true;
+ if (pair)
+ link_stream = hext_stream;
} else if (!(hda_stream->flags & SOF_HDA_STREAM_DMI_L1_COMPATIBLE)) {
dmi_l1_enable = false;
}
@@ -312,9 +334,22 @@ int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag)
return -ENODEV;
}
+ if (pair)
+ snd_hdac_ext_stream_release(link_stream, HDAC_EXT_STREAM_TYPE_LINK);
+
return 0;
}
+int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag)
+{
+ return _hda_dsp_stream_put(sdev, direction, stream_tag, false);
+}
+
+int hda_dsp_stream_pair_put(struct snd_sof_dev *sdev, int direction, int stream_tag)
+{
+ return _hda_dsp_stream_put(sdev, direction, stream_tag, true);
+}
+
static int hda_dsp_stream_reset(struct snd_sof_dev *sdev, struct hdac_stream *hstream)
{
int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
@@ -724,12 +759,12 @@ int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev,
struct hdac_bus *bus = sof_to_bus(sdev);
u32 mask = BIT(hstream->index);
- spin_lock_irq(&bus->reg_lock);
+ guard(spinlock_irq)(&bus->reg_lock);
+
/* couple host and link DMA if link DMA channel is idle */
if (!hext_stream->link_locked)
snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR,
SOF_HDA_REG_PP_PPCTL, mask, 0);
- spin_unlock_irq(&bus->reg_lock);
}
hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0);
@@ -747,7 +782,7 @@ bool hda_dsp_check_stream_irq(struct snd_sof_dev *sdev)
u32 status;
/* The function can be called at irq thread, so use spin_lock_irq */
- spin_lock_irq(&bus->reg_lock);
+ guard(spinlock_irq)(&bus->reg_lock);
status = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS);
@@ -757,8 +792,6 @@ bool hda_dsp_check_stream_irq(struct snd_sof_dev *sdev)
if (status != 0xffffffff)
ret = true;
- spin_unlock_irq(&bus->reg_lock);
-
return ret;
}
EXPORT_SYMBOL_NS(hda_dsp_check_stream_irq, "SND_SOC_SOF_INTEL_HDA_COMMON");
@@ -842,7 +875,7 @@ irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context)
* unsolicited responses from the codec
*/
for (i = 0, active = true; i < 10 && active; i++) {
- spin_lock_irq(&bus->reg_lock);
+ guard(spinlock_irq)(&bus->reg_lock);
status = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS);
@@ -853,7 +886,6 @@ irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context)
if (status & AZX_INT_CTRL_EN) {
active |= hda_codec_check_rirb_status(sdev);
}
- spin_unlock_irq(&bus->reg_lock);
}
return IRQ_HANDLED;
@@ -1211,3 +1243,119 @@ u64 hda_dsp_get_stream_ldp(struct snd_sof_dev *sdev,
return ((u64)ldp_u << 32) | ldp_l;
}
EXPORT_SYMBOL_NS(hda_dsp_get_stream_ldp, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+struct hdac_ext_stream *
+hda_data_stream_prepare(struct device *dev, unsigned int format, unsigned int size,
+ struct snd_dma_buffer *dmab, bool persistent_buffer, int direction,
+ bool is_iccmax, bool pair)
+{
+ struct snd_sof_dev *sdev = dev_get_drvdata(dev);
+ struct hdac_ext_stream *hext_stream;
+ struct hdac_stream *hstream;
+ int ret;
+
+ if (pair)
+ hext_stream = hda_dsp_stream_pair_get(sdev, direction, 0);
+ else
+ hext_stream = hda_dsp_stream_get(sdev, direction, 0);
+
+ if (!hext_stream) {
+ dev_err(sdev->dev, "%s: no stream available\n", __func__);
+ return ERR_PTR(-ENODEV);
+ }
+ hstream = &hext_stream->hstream;
+ hstream->substream = NULL;
+
+ /*
+ * Allocate DMA buffer if it is temporary or if the buffer is intended
+ * to be persistent but not yet allocated.
+ * We cannot rely solely on !dmab->area as caller might use a struct on
+ * stack (when it is temporary) without clearing it to 0.
+ */
+ if (!persistent_buffer || !dmab->area) {
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, dev, size, dmab);
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: memory alloc failed: %d\n",
+ __func__, ret);
+ goto out_put;
+ }
+ }
+
+ hstream->period_bytes = 0; /* initialize period_bytes */
+ hstream->format_val = format;
+ hstream->bufsize = size;
+
+ if (is_iccmax) {
+ ret = hda_dsp_iccmax_stream_hw_params(sdev, hext_stream, dmab, NULL);
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: iccmax stream prepare failed: %d\n",
+ __func__, ret);
+ goto out_free;
+ }
+ } else {
+ ret = hda_dsp_stream_hw_params(sdev, hext_stream, dmab, NULL);
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: hdac prepare failed: %d\n", __func__, ret);
+ goto out_free;
+ }
+ hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_ENABLE, size);
+ }
+
+ return hext_stream;
+
+out_free:
+ snd_dma_free_pages(dmab);
+ dmab->area = NULL;
+ dmab->bytes = 0;
+ hstream->bufsize = 0;
+ hstream->format_val = 0;
+out_put:
+ if (pair)
+ hda_dsp_stream_pair_put(sdev, direction, hstream->stream_tag);
+ else
+ hda_dsp_stream_put(sdev, direction, hstream->stream_tag);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_NS(hda_data_stream_prepare, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+int hda_data_stream_cleanup(struct device *dev, struct snd_dma_buffer *dmab,
+ bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool pair)
+{
+ struct snd_sof_dev *sdev = dev_get_drvdata(dev);
+ struct hdac_stream *hstream = hdac_stream(hext_stream);
+ int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
+ int ret = 0;
+
+ if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0);
+ else
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset,
+ SOF_HDA_SD_CTL_DMA_START, 0);
+
+ if (pair)
+ hda_dsp_stream_pair_put(sdev, hstream->direction, hstream->stream_tag);
+ else
+ hda_dsp_stream_put(sdev, hstream->direction, hstream->stream_tag);
+
+ hstream->running = 0;
+ hstream->substream = NULL;
+
+ /* reset BDL address */
+ snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_SD_BDLPL, 0);
+ snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU, 0);
+
+ snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, sd_offset, 0);
+
+ if (!persistent_buffer) {
+ snd_dma_free_pages(dmab);
+ dmab->area = NULL;
+ dmab->bytes = 0;
+ hstream->bufsize = 0;
+ hstream->format_val = 0;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_NS(hda_data_stream_cleanup, "SND_SOC_SOF_INTEL_HDA_COMMON");
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c
index 686ecc040867..c0cc7d3ce526 100644
--- a/sound/soc/sof/intel/hda.c
+++ b/sound/soc/sof/intel/hda.c
@@ -482,7 +482,7 @@ static int mclk_id_override = -1;
module_param_named(mclk_id, mclk_id_override, int, 0444);
MODULE_PARM_DESC(mclk_id, "SOF SSP mclk_id");
-static int bt_link_mask_override;
+static int bt_link_mask_override = -1;
module_param_named(bt_link_mask, bt_link_mask_override, int, 0444);
MODULE_PARM_DESC(bt_link_mask, "SOF BT offload link mask");
@@ -1138,6 +1138,12 @@ static bool is_endpoint_present(struct sdw_slave *sdw_device,
{
int i;
+ /* If SDCA is not present, assume the endpoint is present */
+ if (!sdw_device->sdca_data.interface_revision) {
+ dev_warn(&sdw_device->dev, "SDCA properties not found in BIOS\n");
+ return true;
+ }
+
for (i = 0; i < sdw_device->sdca_data.num_functions; i++) {
if (dai_type == dai_info->dais[i].dai_type)
return true;
@@ -1531,7 +1537,7 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev)
mach->mach_params.bt_link_mask);
/* allow for module parameter override */
- if (bt_link_mask_override) {
+ if (bt_link_mask_override != -1) {
dev_dbg(sdev->dev, "overriding BT link detected in NHLT tables %#x by kernel param %#x\n",
mach->mach_params.bt_link_mask, bt_link_mask_override);
mach->mach_params.bt_link_mask = bt_link_mask_override;
@@ -1622,7 +1628,6 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev)
mach->tplg_quirk_mask & SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER &&
mach->mach_params.i2s_link_mask) {
int ssp_num;
- int mclk_mask;
if (hweight_long(mach->mach_params.i2s_link_mask) > 1 &&
!(mach->tplg_quirk_mask & SND_SOC_ACPI_TPLG_INTEL_SSP_MSB))
@@ -1647,19 +1652,28 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev)
sof_pdata->tplg_filename = tplg_filename;
- mclk_mask = check_nhlt_ssp_mclk_mask(sdev, ssp_num);
-
- if (mclk_mask < 0) {
- dev_err(sdev->dev, "Invalid MCLK configuration\n");
- return NULL;
- }
-
- dev_dbg(sdev->dev, "MCLK mask %#x found in NHLT\n", mclk_mask);
-
- if (mclk_mask) {
- dev_info(sdev->dev, "Overriding topology with MCLK mask %#x from NHLT\n", mclk_mask);
- sdev->mclk_id_override = true;
- sdev->mclk_id_quirk = (mclk_mask & BIT(0)) ? 0 : 1;
+ if (sof_pdata->ipc_type == SOF_IPC_TYPE_3) {
+ int mclk_mask = check_nhlt_ssp_mclk_mask(sdev,
+ ssp_num);
+
+ if (mclk_mask < 0) {
+ dev_err(sdev->dev,
+ "Invalid MCLK configuration for SSP%d\n",
+ ssp_num);
+ return NULL;
+ }
+
+ if (mclk_mask) {
+ sdev->mclk_id_override = true;
+ sdev->mclk_id_quirk = (mclk_mask & BIT(0)) ? 0 : 1;
+ dev_info(sdev->dev,
+ "SSP%d to use MCLK id %d (mask: %#x)\n",
+ ssp_num, sdev->mclk_id_quirk, mclk_mask);
+ } else {
+ dev_dbg(sdev->dev,
+ "MCLK mask is empty for SSP%d in NHLT\n",
+ ssp_num);
+ }
}
}
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index 562fe8be79c1..3f0966477ace 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -418,10 +418,10 @@
(HDA_DSP_BDL_SIZE / sizeof(struct sof_intel_dsp_bdl))
/* Number of DAIs */
-#define SOF_SKL_NUM_DAIS_NOCODEC 8
+#define SOF_SKL_NUM_DAIS_NOCODEC 9
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
-#define SOF_SKL_NUM_DAIS 15
+#define SOF_SKL_NUM_DAIS 16
#else
#define SOF_SKL_NUM_DAIS SOF_SKL_NUM_DAIS_NOCODEC
#endif
@@ -694,7 +694,10 @@ u64 hda_dsp_get_stream_ldp(struct snd_sof_dev *sdev,
struct hdac_ext_stream *
hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags);
+struct hdac_ext_stream *
+ hda_dsp_stream_pair_get(struct snd_sof_dev *sdev, int direction, u32 flags);
int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag);
+int hda_dsp_stream_pair_put(struct snd_sof_dev *sdev, int direction, int stream_tag);
int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev,
struct hdac_ext_stream *hext_stream,
int enable, u32 size);
@@ -902,6 +905,14 @@ int sdw_hda_dai_hw_free(struct snd_pcm_substream *substream,
int sdw_hda_dai_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *cpu_dai);
+struct hdac_ext_stream *
+hda_data_stream_prepare(struct device *dev, unsigned int format, unsigned int size,
+ struct snd_dma_buffer *dmab, bool persistent_buffer, int direction,
+ bool is_iccmax, bool pair);
+
+int hda_data_stream_cleanup(struct device *dev, struct snd_dma_buffer *dmab,
+ bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool pair);
+
/* common dai driver */
extern struct snd_soc_dai_driver skl_dai[];
int hda_dsp_dais_suspend(struct snd_sof_dev *sdev);
@@ -936,6 +947,7 @@ extern const struct sof_intel_dsp_desc arl_s_chip_info;
extern const struct sof_intel_dsp_desc lnl_chip_info;
extern const struct sof_intel_dsp_desc ptl_chip_info;
extern const struct sof_intel_dsp_desc wcl_chip_info;
+extern const struct sof_intel_dsp_desc nvl_chip_info;
extern const struct sof_intel_dsp_desc nvl_s_chip_info;
/* Probes support */
diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c
index 095dcf1a18e4..4ac81537ca05 100644
--- a/sound/soc/sof/intel/mtl.c
+++ b/sound/soc/sof/intel/mtl.c
@@ -596,13 +596,10 @@ static irqreturn_t mtl_ipc_irq_thread(int irq, void *context)
data->primary = primary;
data->extension = extension;
- spin_lock_irq(&sdev->ipc_lock);
-
+ guard(spinlock_irq)(&sdev->ipc_lock);
snd_sof_ipc_get_reply(sdev);
mtl_ipc_host_done(sdev);
snd_sof_ipc_reply(sdev, data->primary);
-
- spin_unlock_irq(&sdev->ipc_lock);
} else {
dev_dbg_ratelimited(sdev->dev,
"IPC reply before FW_READY: %#x|%#x\n",
diff --git a/sound/soc/sof/intel/nvl.c b/sound/soc/sof/intel/nvl.c
index ff215151af2a..0d763998558f 100644
--- a/sound/soc/sof/intel/nvl.c
+++ b/sound/soc/sof/intel/nvl.c
@@ -26,6 +26,30 @@ int sof_nvl_set_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *dsp_ops)
};
EXPORT_SYMBOL_NS(sof_nvl_set_ops, "SND_SOC_SOF_INTEL_NVL");
+const struct sof_intel_dsp_desc nvl_chip_info = {
+ .cores_num = 4,
+ .init_core_mask = BIT(0),
+ .host_managed_cores_mask = BIT(0),
+ .ipc_req = MTL_DSP_REG_HFIPCXIDR,
+ .ipc_req_mask = MTL_DSP_REG_HFIPCXIDR_BUSY,
+ .ipc_ack = MTL_DSP_REG_HFIPCXIDA,
+ .ipc_ack_mask = MTL_DSP_REG_HFIPCXIDA_DONE,
+ .ipc_ctl = MTL_DSP_REG_HFIPCXCTL,
+ .rom_status_reg = LNL_DSP_REG_HFDSC,
+ .rom_init_timeout = 300,
+ .ssp_count = MTL_SSP_COUNT,
+ .d0i3_offset = MTL_HDA_VS_D0I3C,
+ .read_sdw_lcount = hda_sdw_check_lcount_ext,
+ .check_sdw_irq = lnl_dsp_check_sdw_irq,
+ .check_sdw_wakeen_irq = lnl_sdw_check_wakeen_irq,
+ .sdw_process_wakeen = hda_sdw_process_wakeen_common,
+ .check_ipc_irq = mtl_dsp_check_ipc_irq,
+ .cl_init = mtl_dsp_cl_init,
+ .power_down_dsp = mtl_power_down_dsp,
+ .disable_interrupts = lnl_dsp_disable_interrupts,
+ .hw_ip_version = SOF_INTEL_ACE_4_0,
+};
+
const struct sof_intel_dsp_desc nvl_s_chip_info = {
.cores_num = 2,
.init_core_mask = BIT(0),
diff --git a/sound/soc/sof/intel/pci-apl.c b/sound/soc/sof/intel/pci-apl.c
index 0bf7ee753bc3..3241403efa60 100644
--- a/sound/soc/sof/intel/pci-apl.c
+++ b/sound/soc/sof/intel/pci-apl.c
@@ -86,7 +86,7 @@ static const struct sof_dev_desc glk_desc = {
/* PCI IDs */
static const struct pci_device_id sof_pci_ids[] = {
{ PCI_DEVICE_DATA(INTEL, HDA_APL, &bxt_desc) },
- { PCI_DEVICE_DATA(INTEL, HDA_GML, &glk_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_GLK, &glk_desc) },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, sof_pci_ids);
diff --git a/sound/soc/sof/intel/pci-lnl.c b/sound/soc/sof/intel/pci-lnl.c
index ae379c23f008..acb4429df9ec 100644
--- a/sound/soc/sof/intel/pci-lnl.c
+++ b/sound/soc/sof/intel/pci-lnl.c
@@ -40,6 +40,7 @@ static const struct sof_dev_desc lnl_desc = {
.ipc_supported_mask = BIT(SOF_IPC_TYPE_4),
.ipc_default = SOF_IPC_TYPE_4,
.dspless_mode_supported = true,
+ .on_demand_dsp_boot = true,
.default_fw_path = {
[SOF_IPC_TYPE_4] = "intel/sof-ipc4/lnl",
},
diff --git a/sound/soc/sof/intel/pci-nvl.c b/sound/soc/sof/intel/pci-nvl.c
index c499c14b93d5..bb3c29ef5477 100644
--- a/sound/soc/sof/intel/pci-nvl.c
+++ b/sound/soc/sof/intel/pci-nvl.c
@@ -26,6 +26,36 @@ static int sof_nvl_ops_init(struct snd_sof_dev *sdev)
return sof_nvl_set_ops(sdev, &sof_nvl_ops);
}
+static const struct sof_dev_desc nvl_desc = {
+ .use_acpi_target_states = true,
+ .machines = snd_soc_acpi_intel_nvl_machines,
+ .alt_machines = snd_soc_acpi_intel_nvl_sdw_machines,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = -1,
+ .irqindex_host_ipc = -1,
+ .chip_info = &nvl_chip_info,
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_4),
+ .ipc_default = SOF_IPC_TYPE_4,
+ .dspless_mode_supported = true,
+ .on_demand_dsp_boot = true,
+ .default_fw_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4/nvl",
+ },
+ .default_lib_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/nvl",
+ },
+ .default_tplg_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC_TYPE_4] = "sof-nvl.ri",
+ },
+ .nocodec_tplg_filename = "sof-nvl-nocodec.tplg",
+ .ops = &sof_nvl_ops,
+ .ops_init = sof_nvl_ops_init,
+};
+
static const struct sof_dev_desc nvl_s_desc = {
.use_acpi_target_states = true,
.machines = snd_soc_acpi_intel_nvl_machines,
@@ -38,6 +68,7 @@ static const struct sof_dev_desc nvl_s_desc = {
.ipc_supported_mask = BIT(SOF_IPC_TYPE_4),
.ipc_default = SOF_IPC_TYPE_4,
.dspless_mode_supported = true,
+ .on_demand_dsp_boot = true,
.default_fw_path = {
[SOF_IPC_TYPE_4] = "intel/sof-ipc4/nvl-s",
},
@@ -57,6 +88,7 @@ static const struct sof_dev_desc nvl_s_desc = {
/* PCI IDs */
static const struct pci_device_id sof_pci_ids[] = {
+ { PCI_DEVICE_DATA(INTEL, HDA_NVL, &nvl_desc) }, /* NVL */
{ PCI_DEVICE_DATA(INTEL, HDA_NVL_S, &nvl_s_desc) }, /* NVL-S */
{ 0, }
};
diff --git a/sound/soc/sof/intel/pci-ptl.c b/sound/soc/sof/intel/pci-ptl.c
index 68f6a9841633..9cb785ef763f 100644
--- a/sound/soc/sof/intel/pci-ptl.c
+++ b/sound/soc/sof/intel/pci-ptl.c
@@ -38,6 +38,7 @@ static const struct sof_dev_desc ptl_desc = {
.ipc_supported_mask = BIT(SOF_IPC_TYPE_4),
.ipc_default = SOF_IPC_TYPE_4,
.dspless_mode_supported = true,
+ .on_demand_dsp_boot = true,
.default_fw_path = {
[SOF_IPC_TYPE_4] = "intel/sof-ipc4/ptl",
},
@@ -67,6 +68,7 @@ static const struct sof_dev_desc wcl_desc = {
.ipc_supported_mask = BIT(SOF_IPC_TYPE_4),
.ipc_default = SOF_IPC_TYPE_4,
.dspless_mode_supported = true,
+ .on_demand_dsp_boot = true,
.default_fw_path = {
[SOF_IPC_TYPE_4] = "intel/sof-ipc4/wcl",
},
diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c
index 3fb8d3e9dc6a..e6d8894b8ef6 100644
--- a/sound/soc/sof/ipc.c
+++ b/sound/soc/sof/ipc.c
@@ -47,7 +47,7 @@ int sof_ipc_send_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes,
* The spin-lock is needed to protect message objects against other
* atomic contexts.
*/
- spin_lock_irq(&sdev->ipc_lock);
+ guard(spinlock_irq)(&sdev->ipc_lock);
/* initialise the message */
msg = &ipc->msg;
@@ -66,8 +66,6 @@ int sof_ipc_send_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes,
if (!ret)
msg->ipc_complete = false;
- spin_unlock_irq(&sdev->ipc_lock);
-
return ret;
}
@@ -225,9 +223,8 @@ void snd_sof_ipc_free(struct snd_sof_dev *sdev)
return;
/* disable sending of ipc's */
- mutex_lock(&ipc->tx_mutex);
- ipc->disable_ipc_tx = true;
- mutex_unlock(&ipc->tx_mutex);
+ scoped_guard(mutex, &ipc->tx_mutex)
+ ipc->disable_ipc_tx = true;
if (ipc->ops->exit)
ipc->ops->exit(sdev);
diff --git a/sound/soc/sof/ipc3-dtrace.c b/sound/soc/sof/ipc3-dtrace.c
index 6ec391fd39a9..50700f5cb0ef 100644
--- a/sound/soc/sof/ipc3-dtrace.c
+++ b/sound/soc/sof/ipc3-dtrace.c
@@ -171,7 +171,12 @@ static int ipc3_trace_update_filter(struct snd_sof_dev *sdev, int num_elems,
dev_err(sdev->dev, "enabling device failed: %d\n", ret);
goto error;
}
- ret = sof_ipc_tx_message_no_reply(sdev->ipc, msg, msg->hdr.size);
+
+ /* Make sure the DSP/firmware is booted up */
+ ret = snd_sof_boot_dsp_firmware(sdev);
+ if (!ret)
+ ret = sof_ipc_tx_message_no_reply(sdev->ipc, msg, msg->hdr.size);
+
pm_runtime_put_autosuspend(sdev->dev);
error:
diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c
index f449362a2905..743f42fb26c0 100644
--- a/sound/soc/sof/ipc3-topology.c
+++ b/sound/soc/sof/ipc3-topology.c
@@ -2427,9 +2427,9 @@ static int sof_ipc3_free_widgets_in_list(struct snd_sof_dev *sdev, bool include_
/* Do not free widgets for static pipelines with FW older than SOF2.2 */
if (!verify && !swidget->dynamic_pipeline_widget &&
SOF_FW_VER(v->major, v->minor, v->micro) < SOF_FW_VER(2, 2, 0)) {
- mutex_lock(&swidget->setup_mutex);
- swidget->use_count = 0;
- mutex_unlock(&swidget->setup_mutex);
+ scoped_guard(mutex, &swidget->setup_mutex)
+ swidget->use_count = 0;
+
if (swidget->spipe)
swidget->spipe->complete = 0;
continue;
diff --git a/sound/soc/sof/ipc3.c b/sound/soc/sof/ipc3.c
index 4a194a705ace..85bb22bbe18d 100644
--- a/sound/soc/sof/ipc3.c
+++ b/sound/soc/sof/ipc3.c
@@ -378,7 +378,7 @@ static int sof_ipc3_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_
}
/* Serialise IPC TX */
- mutex_lock(&ipc->tx_mutex);
+ guard(mutex)(&ipc->tx_mutex);
ret = ipc3_tx_msg_unlocked(ipc, msg_data, msg_bytes, reply_data, reply_bytes);
@@ -405,8 +405,6 @@ static int sof_ipc3_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_
}
}
- mutex_unlock(&ipc->tx_mutex);
-
return ret;
}
@@ -477,7 +475,7 @@ static int sof_ipc3_set_get_data(struct snd_sof_dev *sdev, void *data, size_t da
memcpy(cdata_chunk, cdata, hdr_bytes);
/* Serialise IPC TX */
- mutex_lock(&sdev->ipc->tx_mutex);
+ guard(mutex)(&ipc->tx_mutex);
/* copy the payload data in a loop */
for (i = 0; i < num_msg; i++) {
@@ -511,8 +509,6 @@ static int sof_ipc3_set_get_data(struct snd_sof_dev *sdev, void *data, size_t da
sof_ipc3_dump_payload(sdev, payload, data_bytes - header_bytes);
}
- mutex_unlock(&sdev->ipc->tx_mutex);
-
kfree(cdata_chunk);
return ret;
diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c
index 976a4794d610..596c3d77a34e 100644
--- a/sound/soc/sof/ipc4-control.c
+++ b/sound/soc/sof/ipc4-control.c
@@ -66,7 +66,7 @@ static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol,
* configuration
*/
memcpy(scontrol->ipc_control_data, scontrol->old_ipc_control_data,
- scontrol->max_size);
+ scontrol->size);
kfree(scontrol->old_ipc_control_data);
scontrol->old_ipc_control_data = NULL;
/* Send the last known good configuration to firmware */
@@ -284,6 +284,106 @@ static void sof_ipc4_refresh_generic_control(struct snd_sof_control *scontrol)
kfree(data);
}
+static int
+sof_ipc4_set_bytes_control_data(struct snd_sof_control *scontrol, bool lock)
+{
+ struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
+ struct snd_soc_component *scomp = scontrol->scomp;
+ struct sof_ipc4_control_msg_payload *msg_data;
+ struct sof_abi_hdr *data = cdata->data;
+ struct sof_ipc4_msg *msg = &cdata->msg;
+ size_t data_size;
+ int ret;
+
+ data_size = struct_size(msg_data, data, data->size);
+ msg_data = kzalloc(data_size, GFP_KERNEL);
+ if (!msg_data)
+ return -ENOMEM;
+
+ msg_data->id = cdata->index;
+ msg_data->num_elems = data->size;
+ memcpy(msg_data->data, data->data, data->size);
+
+ msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(data->type);
+
+ msg->data_ptr = msg_data;
+ msg->data_size = data_size;
+
+ ret = sof_ipc4_set_get_kcontrol_data(scontrol, true, lock);
+ msg->data_ptr = NULL;
+ msg->data_size = 0;
+ if (ret < 0)
+ dev_err(scomp->dev, "%s: Failed to set control update for %s\n",
+ __func__, scontrol->name);
+
+ kfree(msg_data);
+
+ return ret;
+}
+
+static int
+sof_ipc4_refresh_bytes_control(struct snd_sof_control *scontrol, bool lock)
+{
+ struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
+ struct snd_soc_component *scomp = scontrol->scomp;
+ struct sof_ipc4_control_msg_payload *msg_data;
+ struct sof_abi_hdr *data = cdata->data;
+ struct sof_ipc4_msg *msg = &cdata->msg;
+ size_t data_size;
+ int ret = 0;
+
+ if (!scontrol->comp_data_dirty)
+ return 0;
+
+ if (!pm_runtime_active(scomp->dev))
+ return 0;
+
+ data_size = scontrol->max_size - sizeof(*data);
+ if (data_size < sizeof(*msg_data))
+ data_size = sizeof(*msg_data);
+
+ msg_data = kzalloc(data_size, GFP_KERNEL);
+ if (!msg_data)
+ return -ENOMEM;
+
+ msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(data->type);
+
+ msg_data->id = cdata->index;
+ msg_data->num_elems = 0; /* ignored for bytes */
+
+ msg->data_ptr = msg_data;
+ msg->data_size = data_size;
+
+ scontrol->comp_data_dirty = false;
+ ret = sof_ipc4_set_get_kcontrol_data(scontrol, false, lock);
+ if (!ret) {
+ if (msg->data_size > scontrol->max_size - sizeof(*data)) {
+ dev_err(scomp->dev,
+ "%s: no space for data in %s (%zu, %zu)\n",
+ __func__, scontrol->name, msg->data_size,
+ scontrol->max_size - sizeof(*data));
+ ret = -EINVAL;
+ goto out;
+ }
+
+ data->size = msg->data_size;
+ scontrol->size = sizeof(*cdata) + sizeof(*data) + data->size;
+ memcpy(data->data, msg->data_ptr, data->size);
+ } else {
+ dev_err(scomp->dev, "Failed to read control data for %s\n",
+ scontrol->name);
+ scontrol->comp_data_dirty = true;
+ }
+
+out:
+ msg->data_ptr = NULL;
+ msg->data_size = 0;
+
+ kfree(msg_data);
+
+ return ret;
+}
+
static bool sof_ipc4_switch_put(struct snd_sof_control *scontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -412,19 +512,42 @@ static int sof_ipc4_set_get_bytes_data(struct snd_sof_dev *sdev,
int ret = 0;
/* Send the new data to the firmware only if it is powered up */
- if (set && !pm_runtime_active(sdev->dev))
- return 0;
+ if (set) {
+ if (!pm_runtime_active(sdev->dev))
+ return 0;
+
+ if (!data->size) {
+ dev_dbg(sdev->dev, "%s: No data to be sent.\n",
+ scontrol->name);
+ return 0;
+ }
+ }
+
+ if (data->type == SOF_IPC4_BYTES_CONTROL_PARAM_ID) {
+ if (set)
+ return sof_ipc4_set_bytes_control_data(scontrol, lock);
+ else
+ return sof_ipc4_refresh_bytes_control(scontrol, lock);
+ }
msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(data->type);
msg->data_ptr = data->data;
- msg->data_size = data->size;
+ if (set)
+ msg->data_size = data->size;
+ else
+ msg->data_size = scontrol->max_size - sizeof(*data);
ret = sof_ipc4_set_get_kcontrol_data(scontrol, set, lock);
- if (ret < 0)
+ if (ret < 0) {
dev_err(sdev->dev, "Failed to %s for %s\n",
set ? "set bytes update" : "get bytes",
scontrol->name);
+ } else if (!set) {
+ /* Update the sizes according to the received payload data */
+ data->size = msg->data_size;
+ scontrol->size = sizeof(*cdata) + sizeof(*data) + data->size;
+ }
msg->data_ptr = NULL;
msg->data_size = 0;
@@ -440,6 +563,7 @@ static int sof_ipc4_bytes_put(struct snd_sof_control *scontrol,
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct sof_abi_hdr *data = cdata->data;
size_t size;
+ int ret;
if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) {
dev_err_ratelimited(scomp->dev,
@@ -461,9 +585,12 @@ static int sof_ipc4_bytes_put(struct snd_sof_control *scontrol,
/* copy from kcontrol */
memcpy(data, ucontrol->value.bytes.data, size);
- sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true);
+ ret = sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true);
+ if (!ret)
+ /* Update the cdata size */
+ scontrol->size = sizeof(*cdata) + size;
- return 0;
+ return ret;
}
static int sof_ipc4_bytes_get(struct snd_sof_control *scontrol,
@@ -487,6 +614,8 @@ static int sof_ipc4_bytes_get(struct snd_sof_control *scontrol,
return -EINVAL;
}
+ sof_ipc4_refresh_bytes_control(scontrol, true);
+
size = data->size + sizeof(*data);
/* copy back to kcontrol */
@@ -559,7 +688,7 @@ static int sof_ipc4_bytes_ext_put(struct snd_sof_control *scontrol,
if (!scontrol->old_ipc_control_data) {
/* Create a backup of the current, valid bytes control */
scontrol->old_ipc_control_data = kmemdup(scontrol->ipc_control_data,
- scontrol->max_size, GFP_KERNEL);
+ scontrol->size, GFP_KERNEL);
if (!scontrol->old_ipc_control_data)
return -ENOMEM;
}
@@ -567,12 +696,15 @@ static int sof_ipc4_bytes_ext_put(struct snd_sof_control *scontrol,
/* Copy the whole binary data which includes the ABI header and the payload */
if (copy_from_user(data, tlvd->tlv, header.length)) {
memcpy(scontrol->ipc_control_data, scontrol->old_ipc_control_data,
- scontrol->max_size);
+ scontrol->size);
kfree(scontrol->old_ipc_control_data);
scontrol->old_ipc_control_data = NULL;
return -EFAULT;
}
+ /* Update the cdata size */
+ scontrol->size = sizeof(*cdata) + header.length;
+
return sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true);
}
@@ -638,6 +770,8 @@ static int sof_ipc4_bytes_ext_get(struct snd_sof_control *scontrol,
const unsigned int __user *binary_data,
unsigned int size)
{
+ sof_ipc4_refresh_bytes_control(scontrol, true);
+
return _sof_ipc4_bytes_ext_get(scontrol, binary_data, size, false);
}
@@ -691,6 +825,9 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message)
case SOF_IPC4_ENUM_CONTROL_PARAM_ID:
type = SND_SOC_TPLG_TYPE_ENUM;
break;
+ case SOF_IPC4_BYTES_CONTROL_PARAM_ID:
+ type = SND_SOC_TPLG_TYPE_BYTES;
+ break;
default:
dev_err(sdev->dev,
"%s: Invalid control type for module %u.%u: %u\n",
@@ -741,23 +878,38 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message)
* The message includes the updated value/data, update the
* control's local cache using the received notification
*/
- for (i = 0; i < msg_data->num_elems; i++) {
- u32 channel = msg_data->chanv[i].channel;
+ if (type == SND_SOC_TPLG_TYPE_BYTES) {
+ struct sof_abi_hdr *data = cdata->data;
- if (channel >= scontrol->num_channels) {
+ if (msg_data->num_elems > scontrol->max_size - sizeof(*data)) {
dev_warn(sdev->dev,
- "Invalid channel index for %s: %u\n",
- scontrol->name, i);
-
- /*
- * Mark the scontrol as dirty to force a refresh
- * on next read
- */
- scontrol->comp_data_dirty = true;
- break;
+ "%s: no space for data in %s (%u, %zu)\n",
+ __func__, scontrol->name, msg_data->num_elems,
+ scontrol->max_size - sizeof(*data));
+ } else {
+ memcpy(data->data, msg_data->data, msg_data->num_elems);
+ data->size = msg_data->num_elems;
+ scontrol->size = sizeof(*cdata) + sizeof(*data) + data->size;
+ }
+ } else {
+ for (i = 0; i < msg_data->num_elems; i++) {
+ u32 channel = msg_data->chanv[i].channel;
+
+ if (channel >= scontrol->num_channels) {
+ dev_warn(sdev->dev,
+ "Invalid channel index for %s: %u\n",
+ scontrol->name, i);
+
+ /*
+ * Mark the scontrol as dirty to force a refresh
+ * on next read
+ */
+ scontrol->comp_data_dirty = true;
+ break;
+ }
+
+ cdata->chanv[channel].value = msg_data->chanv[i].value;
}
-
- cdata->chanv[channel].value = msg_data->chanv[i].value;
}
} else {
/*
diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c
index b0d293f62d1c..07a78cb3c25c 100644
--- a/sound/soc/sof/ipc4-loader.c
+++ b/sound/soc/sof/ipc4-loader.c
@@ -175,7 +175,7 @@ static int sof_ipc4_load_library(struct snd_sof_dev *sdev, unsigned long lib_id,
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
struct sof_ipc4_fw_library *fw_lib;
ssize_t payload_offset;
- int ret, i, err;
+ int ret, i;
if (!ipc4_data->load_library) {
dev_err(sdev->dev, "Library loading is not supported on this platform\n");
@@ -223,24 +223,7 @@ static int sof_ipc4_load_library(struct snd_sof_dev *sdev, unsigned long lib_id,
for (i = 0; i < fw_lib->num_modules; i++)
fw_lib->modules[i].man4_module_entry.id |= (lib_id << SOF_IPC4_MOD_LIB_ID_SHIFT);
- /*
- * Make sure that the DSP is booted and stays up while attempting the
- * loading the library for the first time
- */
- ret = pm_runtime_resume_and_get(sdev->dev);
- if (ret < 0 && ret != -EACCES) {
- dev_err_ratelimited(sdev->dev, "%s: pm_runtime resume failed: %d\n",
- __func__, ret);
- goto release;
- }
-
ret = ipc4_data->load_library(sdev, fw_lib, false);
-
- err = pm_runtime_put_autosuspend(sdev->dev);
- if (err < 0)
- dev_err_ratelimited(sdev->dev, "%s: pm_runtime idle failed: %d\n",
- __func__, err);
-
if (ret)
goto release;
diff --git a/sound/soc/sof/ipc4-mtrace.c b/sound/soc/sof/ipc4-mtrace.c
index aa5b78604db6..cfd999b42458 100644
--- a/sound/soc/sof/ipc4-mtrace.c
+++ b/sound/soc/sof/ipc4-mtrace.c
@@ -118,22 +118,19 @@ static int sof_ipc4_mtrace_dfs_open(struct inode *inode, struct file *file)
struct sof_mtrace_core_data *core_data = inode->i_private;
int ret;
- mutex_lock(&core_data->buffer_lock);
+ guard(mutex)(&core_data->buffer_lock);
- if (core_data->log_buffer) {
- ret = -EBUSY;
- goto out;
- }
+ if (core_data->log_buffer)
+ return -EBUSY;
ret = debugfs_file_get(file->f_path.dentry);
if (unlikely(ret))
- goto out;
+ return ret;
core_data->log_buffer = kmalloc(SOF_IPC4_DEBUG_SLOT_SIZE, GFP_KERNEL);
if (!core_data->log_buffer) {
debugfs_file_put(file->f_path.dentry);
- ret = -ENOMEM;
- goto out;
+ return -ENOMEM;
}
ret = simple_open(inode, file);
@@ -142,9 +139,6 @@ static int sof_ipc4_mtrace_dfs_open(struct inode *inode, struct file *file)
debugfs_file_put(file->f_path.dentry);
}
-out:
- mutex_unlock(&core_data->buffer_lock);
-
return ret;
}
@@ -281,10 +275,10 @@ static int sof_ipc4_mtrace_dfs_release(struct inode *inode, struct file *file)
debugfs_file_put(file->f_path.dentry);
- mutex_lock(&core_data->buffer_lock);
- kfree(core_data->log_buffer);
- core_data->log_buffer = NULL;
- mutex_unlock(&core_data->buffer_lock);
+ scoped_guard(mutex, &core_data->buffer_lock) {
+ kfree(core_data->log_buffer);
+ core_data->log_buffer = NULL;
+ }
return 0;
}
diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c
index 6d81969e181c..c3337c3f08c1 100644
--- a/sound/soc/sof/ipc4-pcm.c
+++ b/sound/soc/sof/ipc4-pcm.c
@@ -487,7 +487,7 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
return -ENOMEM;
}
- mutex_lock(&ipc4_data->pipeline_state_mutex);
+ guard(mutex)(&ipc4_data->pipeline_state_mutex);
/*
* IPC4 requires pipelines to be triggered in order starting at the sink and
@@ -580,7 +580,6 @@ skip_pause_transition:
}
free:
- mutex_unlock(&ipc4_data->pipeline_state_mutex);
kfree(trigger_list);
kfree(pipe_priority);
return ret;
diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c
index d621e7914a73..622bffb50a1c 100644
--- a/sound/soc/sof/ipc4-topology.c
+++ b/sound/soc/sof/ipc4-topology.c
@@ -76,6 +76,10 @@ static const struct sof_topology_token ipc4_sched_tokens[] = {
offsetof(struct sof_ipc4_pipeline, core_id)},
{SOF_TKN_SCHED_PRIORITY, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
offsetof(struct sof_ipc4_pipeline, priority)},
+ {SOF_TKN_SCHED_DIRECTION, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc4_pipeline, direction)},
+ {SOF_TKN_SCHED_DIRECTION, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16,
+ offsetof(struct sof_ipc4_pipeline, direction_valid)},
};
static const struct sof_topology_token pipeline_tokens[] = {
@@ -158,6 +162,12 @@ static const struct sof_topology_token comp_ext_tokens[] = {
offsetof(struct snd_sof_widget, core)},
{SOF_TKN_COMP_SCHED_DOMAIN, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_domain,
offsetof(struct snd_sof_widget, comp_domain)},
+ {SOF_TKN_COMP_DOMAIN_ID, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct snd_sof_widget, dp_domain_id)},
+ {SOF_TKN_COMP_HEAP_BYTES_REQUIREMENT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct snd_sof_widget, dp_heap_bytes)},
+ {SOF_TKN_COMP_STACK_BYTES_REQUIREMENT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct snd_sof_widget, dp_stack_bytes)},
};
static const struct sof_topology_token gain_tokens[] = {
@@ -933,6 +943,10 @@ static int sof_ipc4_widget_setup_comp_pipeline(struct snd_sof_widget *swidget)
swidget->core = pipeline->core_id;
spipe->core_mask |= BIT(pipeline->core_id);
+ if (pipeline->direction_valid) {
+ spipe->direction = pipeline->direction;
+ spipe->direction_valid = true;
+ }
if (pipeline->use_chain_dma) {
dev_dbg(scomp->dev, "Set up chain DMA for %s\n", swidget->widget->name);
@@ -948,9 +962,9 @@ static int sof_ipc4_widget_setup_comp_pipeline(struct snd_sof_widget *swidget)
goto err;
}
- dev_dbg(scomp->dev, "pipeline '%s': id %d, pri %d, core_id %u, lp mode %d\n",
+ dev_dbg(scomp->dev, "pipeline '%s': id %d, pri %d, core_id %u, lp mode %d direction %d\n",
swidget->widget->name, swidget->pipeline_id,
- pipeline->priority, pipeline->core_id, pipeline->lp_mode);
+ pipeline->priority, pipeline->core_id, pipeline->lp_mode, pipeline->direction);
swidget->private = pipeline;
@@ -1998,6 +2012,25 @@ sof_ipc4_prepare_dai_copier(struct snd_sof_dev *sdev, struct snd_sof_dai *dai,
return ret;
}
+static void sof_ipc4_host_config(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
+ struct snd_sof_platform_stream_params *platform_params)
+{
+ struct sof_ipc4_copier *ipc4_copier = (struct sof_ipc4_copier *)swidget->private;
+ struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
+ struct sof_ipc4_copier_data *copier_data = &ipc4_copier->data;
+ struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
+ u32 host_dma_id = platform_params->stream_tag - 1;
+
+ if (pipeline->use_chain_dma) {
+ pipeline->msg.primary &= ~SOF_IPC4_GLB_CHAIN_DMA_HOST_ID_MASK;
+ pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_HOST_ID(host_dma_id);
+ return;
+ }
+
+ copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
+ copier_data->gtw_cfg.node_id |= SOF_IPC4_NODE_INDEX(host_dma_id);
+}
+
static int
sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
struct snd_pcm_hw_params *fe_params,
@@ -2720,12 +2753,14 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget,
int input_fmt_index = 0;
int ret;
- input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget,
- &process->base_config,
- pipeline_params,
- available_fmt);
- if (input_fmt_index < 0)
- return input_fmt_index;
+ if (available_fmt->num_input_formats) {
+ input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget,
+ &process->base_config,
+ pipeline_params,
+ available_fmt);
+ if (input_fmt_index < 0)
+ return input_fmt_index;
+ }
/* Configure output audio format only if the module supports output */
if (available_fmt->num_output_formats) {
@@ -2734,12 +2769,28 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget,
u32 out_ref_rate, out_ref_channels;
int out_ref_valid_bits, out_ref_type;
- in_fmt = &available_fmt->input_pin_fmts[input_fmt_index].audio_fmt;
+ if (available_fmt->num_input_formats) {
+ in_fmt = &available_fmt->input_pin_fmts[input_fmt_index].audio_fmt;
- out_ref_rate = in_fmt->sampling_frequency;
- out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
- out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);
- out_ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg);
+ out_ref_rate = in_fmt->sampling_frequency;
+ out_ref_channels =
+ SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
+ out_ref_valid_bits =
+ SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);
+ out_ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg);
+ } else {
+ /* for modules without input formats, use FE params as reference */
+ out_ref_rate = params_rate(fe_params);
+ out_ref_channels = params_channels(fe_params);
+ ret = sof_ipc4_get_sample_type(sdev, fe_params);
+ if (ret < 0)
+ return ret;
+ out_ref_type = (u32)ret;
+
+ out_ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params);
+ if (out_ref_valid_bits < 0)
+ return out_ref_valid_bits;
+ }
output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget,
&process->base_config,
@@ -2767,6 +2818,16 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget,
if (ret)
return ret;
}
+
+ /* set base cfg to match the first output format if there are no input formats */
+ if (!available_fmt->num_input_formats) {
+ struct sof_ipc4_audio_format *out_fmt;
+
+ out_fmt = &available_fmt->output_pin_fmts[0].audio_fmt;
+
+ /* copy output format */
+ memcpy(&process->base_config.audio_fmt, out_fmt, sizeof(*out_fmt));
+ }
}
sof_ipc4_dbg_module_audio_format(sdev->dev, swidget, available_fmt,
@@ -2870,22 +2931,41 @@ static int sof_ipc4_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_
struct sof_ipc4_msg *msg;
int ret;
- if (scontrol->max_size < (sizeof(*control_data) + sizeof(struct sof_abi_hdr))) {
- dev_err(sdev->dev, "insufficient size for a bytes control %s: %zu.\n",
+ /*
+ * The max_size is coming from topology and indicates the maximum size
+ * of sof_abi_hdr plus the payload, which excludes the local only
+ * 'struct sof_ipc4_control_data'
+ */
+ if (scontrol->max_size < sizeof(struct sof_abi_hdr)) {
+ dev_err(sdev->dev,
+ "insufficient maximum size for a bytes control %s: %zu.\n",
scontrol->name, scontrol->max_size);
return -EINVAL;
}
- if (scontrol->priv_size > scontrol->max_size - sizeof(*control_data)) {
- dev_err(sdev->dev, "scontrol %s bytes data size %zu exceeds max %zu.\n",
- scontrol->name, scontrol->priv_size,
- scontrol->max_size - sizeof(*control_data));
+ if (scontrol->priv_size > scontrol->max_size) {
+ dev_err(sdev->dev,
+ "bytes control %s initial data size %zu exceeds max %zu.\n",
+ scontrol->name, scontrol->priv_size, scontrol->max_size);
return -EINVAL;
}
- scontrol->size = sizeof(struct sof_ipc4_control_data) + scontrol->priv_size;
+ if (scontrol->priv_size < sizeof(struct sof_abi_hdr)) {
+ dev_err(sdev->dev,
+ "bytes control %s initial data size %zu is insufficient.\n",
+ scontrol->name, scontrol->priv_size);
+ return -EINVAL;
+ }
- scontrol->ipc_control_data = kzalloc(scontrol->max_size, GFP_KERNEL);
+ /*
+ * The used size behind the cdata pointer, which can be smaller than
+ * the maximum size
+ */
+ scontrol->size = sizeof(*control_data) + scontrol->priv_size;
+
+ /* Allocate the cdata: local struct size + maximum payload size */
+ scontrol->ipc_control_data = kzalloc(sizeof(*control_data) + scontrol->max_size,
+ GFP_KERNEL);
if (!scontrol->ipc_control_data)
return -ENOMEM;
@@ -2920,6 +3000,7 @@ static int sof_ipc4_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_
msg->primary = SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_SET);
msg->primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
msg->primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
+ msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(control_data->data->type);
return 0;
@@ -2948,6 +3029,77 @@ static int sof_ipc4_control_setup(struct snd_sof_dev *sdev, struct snd_sof_contr
return 0;
}
+static int sof_ipc4_widget_setup_msg_payload(struct snd_sof_dev *sdev,
+ struct snd_sof_widget *swidget,
+ struct sof_ipc4_msg *msg,
+ void *ipc_data, u32 ipc_size,
+ void **new_data)
+{
+ struct sof_ipc4_mod_init_ext_dp_memory_data *dp_mem_data;
+ struct sof_ipc4_module_init_ext_init *ext_init;
+ struct sof_ipc4_module_init_ext_object *hdr;
+ int new_size;
+ u32 *payload;
+ u32 ext_pos;
+
+ /* For the moment the only reason for adding init_ext_init payload is DP
+ * memory data. If both stack and heap size are 0 (= use default), then
+ * there is no need for init_ext_init payload.
+ */
+ if (swidget->comp_domain != SOF_COMP_DOMAIN_DP) {
+ msg->extension &= ~SOF_IPC4_MOD_EXT_EXTENDED_INIT_MASK;
+ return 0;
+ }
+
+ payload = kzalloc(sdev->ipc->max_payload_size, GFP_KERNEL);
+ if (!payload)
+ return -ENOMEM;
+
+ /* Add ext_init first and set objects array flag to 1 */
+ ext_init = (struct sof_ipc4_module_init_ext_init *)payload;
+ ext_init->word0 |= SOF_IPC4_MOD_INIT_EXT_OBJ_ARRAY_MASK;
+ ext_pos = DIV_ROUND_UP(sizeof(*ext_init), sizeof(u32));
+
+ /* Add object array objects after ext_init */
+
+ /* Add dp_memory_data if comp_domain indicates DP */
+ if (swidget->comp_domain == SOF_COMP_DOMAIN_DP) {
+ hdr = (struct sof_ipc4_module_init_ext_object *)&payload[ext_pos];
+ hdr->header = SOF_IPC4_MOD_INIT_EXT_OBJ_LAST_MASK |
+ SOF_IPC4_MOD_INIT_EXT_OBJ_ID(SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA) |
+ SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS(DIV_ROUND_UP(sizeof(*dp_mem_data),
+ sizeof(u32)));
+ ext_pos += DIV_ROUND_UP(sizeof(*hdr), sizeof(u32));
+ dp_mem_data = (struct sof_ipc4_mod_init_ext_dp_memory_data *)&payload[ext_pos];
+ dp_mem_data->domain_id = swidget->dp_domain_id;
+ dp_mem_data->stack_bytes = swidget->dp_stack_bytes;
+ dp_mem_data->heap_bytes = swidget->dp_heap_bytes;
+ ext_pos += DIV_ROUND_UP(sizeof(*dp_mem_data), sizeof(u32));
+ }
+
+ /* If another array object is added, remember clear previous OBJ_LAST bit */
+
+ /* Calculate final size and check that it fits to max payload size */
+ new_size = ext_pos * sizeof(u32) + ipc_size;
+ if (new_size > sdev->ipc->max_payload_size) {
+ dev_err(sdev->dev, "Max ipc payload size %zu exceeded: %u",
+ sdev->ipc->max_payload_size, new_size);
+ kfree(payload);
+ return -EINVAL;
+ }
+ *new_data = payload;
+
+ /* Copy module specific ipc_payload to end */
+ memcpy(&payload[ext_pos], ipc_data, ipc_size);
+
+ /* Update msg extension bits according to the payload changes */
+ msg->extension |= SOF_IPC4_MOD_EXT_EXTENDED_INIT_MASK;
+ msg->extension &= ~SOF_IPC4_MOD_EXT_PARAM_SIZE_MASK;
+ msg->extension |= SOF_IPC4_MOD_EXT_PARAM_SIZE(DIV_ROUND_UP(new_size, sizeof(u32)));
+
+ return new_size;
+}
+
static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
{
struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
@@ -2955,6 +3107,7 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
struct sof_ipc4_pipeline *pipeline;
struct sof_ipc4_msg *msg;
void *ipc_data = NULL;
+ void *ext_data = NULL;
u32 ipc_size = 0;
int ret;
@@ -3099,6 +3252,16 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
dev_dbg(sdev->dev, "Create widget %s (pipe %d) - ID %d, instance %d, core %d\n",
swidget->widget->name, swidget->pipeline_id, module_id,
swidget->instance_id, swidget->core);
+
+ ret = sof_ipc4_widget_setup_msg_payload(sdev, swidget, msg, ipc_data, ipc_size,
+ &ext_data);
+ if (ret < 0)
+ goto fail;
+
+ if (ret > 0) {
+ ipc_size = ret;
+ ipc_data = ext_data;
+ }
} else {
dev_dbg(sdev->dev, "Create pipeline %s (pipe %d) - instance %d, core %d\n",
swidget->widget->name, swidget->pipeline_id,
@@ -3109,6 +3272,8 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
msg->data_ptr = ipc_data;
ret = sof_ipc_tx_message_no_reply(sdev->ipc, msg, ipc_size);
+
+fail:
if (ret < 0) {
dev_err(sdev->dev, "failed to create module %s\n", swidget->widget->name);
@@ -3121,6 +3286,7 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
}
}
+ kfree(ext_data);
return ret;
}
@@ -3130,7 +3296,7 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
int ret = 0;
- mutex_lock(&ipc4_data->pipeline_state_mutex);
+ guard(mutex)(&ipc4_data->pipeline_state_mutex);
/* freeing a pipeline frees all the widgets associated with it */
if (swidget->id == snd_soc_dapm_scheduler) {
@@ -3141,7 +3307,6 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget
if (pipeline->use_chain_dma) {
dev_warn(sdev->dev, "use_chain_dma set for scheduler %s",
swidget->widget->name);
- mutex_unlock(&ipc4_data->pipeline_state_mutex);
return 0;
}
@@ -3169,8 +3334,6 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget
ida_free(&fw_module->m_ida, swidget->instance_id);
}
- mutex_unlock(&ipc4_data->pipeline_state_mutex);
-
return ret;
}
@@ -3821,4 +3984,5 @@ const struct sof_ipc_tplg_ops ipc4_tplg_ops = {
.dai_get_param = sof_ipc4_dai_get_param,
.tear_down_all_pipelines = sof_ipc4_tear_down_all_pipelines,
.link_setup = sof_ipc4_link_setup,
+ .host_config = sof_ipc4_host_config,
};
diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h
index 191b51d97993..a289c1d8f3ff 100644
--- a/sound/soc/sof/ipc4-topology.h
+++ b/sound/soc/sof/ipc4-topology.h
@@ -150,6 +150,8 @@ struct sof_ipc4_copier_config_set_sink_format {
* @use_chain_dma: flag to indicate if the firmware shall use chained DMA
* @msg: message structure for pipeline
* @skip_during_fe_trigger: skip triggering this pipeline during the FE DAI trigger
+ * @direction_valid: flag indicating if valid direction is set in topology
+ * @direction: pipeline direction set in topology if direction_valid is true
*/
struct sof_ipc4_pipeline {
uint32_t priority;
@@ -160,6 +162,8 @@ struct sof_ipc4_pipeline {
bool use_chain_dma;
struct sof_ipc4_msg msg;
bool skip_during_fe_trigger;
+ bool direction_valid;
+ u32 direction;
};
/**
@@ -368,19 +372,24 @@ struct sof_ipc4_control_data {
#define SOF_IPC4_SWITCH_CONTROL_PARAM_ID 200
#define SOF_IPC4_ENUM_CONTROL_PARAM_ID 201
+#define SOF_IPC4_BYTES_CONTROL_PARAM_ID 202
/**
* struct sof_ipc4_control_msg_payload - IPC payload for kcontrol parameters
* @id: unique id of the control
- * @num_elems: Number of elements in the chanv array
+ * @num_elems: Number of elements in the chanv array or number of bytes in data
* @reserved: reserved for future use, must be set to 0
* @chanv: channel ID and value array
+ * @data: binary payload
*/
struct sof_ipc4_control_msg_payload {
uint16_t id;
uint16_t num_elems;
uint32_t reserved[4];
- DECLARE_FLEX_ARRAY(struct sof_ipc4_ctrl_value_chan, chanv);
+ union {
+ DECLARE_FLEX_ARRAY(struct sof_ipc4_ctrl_value_chan, chanv);
+ DECLARE_FLEX_ARRAY(uint8_t, data);
+ };
} __packed;
/**
diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c
index a4a090e6724a..c9c6c0c52c62 100644
--- a/sound/soc/sof/ipc4.c
+++ b/sound/soc/sof/ipc4.c
@@ -15,6 +15,7 @@
#include "sof-audio.h"
#include "ipc4-fw-reg.h"
#include "ipc4-priv.h"
+#include "ipc4-topology.h"
#include "ipc4-telemetry.h"
#include "ops.h"
@@ -411,7 +412,7 @@ static int sof_ipc4_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_
}
/* Serialise IPC TX */
- mutex_lock(&ipc->tx_mutex);
+ guard(mutex)(&ipc->tx_mutex);
ret = ipc4_tx_msg_unlocked(ipc, msg_data, msg_bytes, reply_data, reply_bytes);
@@ -428,11 +429,27 @@ static int sof_ipc4_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_
sof_ipc4_dump_payload(sdev, msg->data_ptr, msg->data_size);
}
- mutex_unlock(&ipc->tx_mutex);
-
return ret;
}
+static bool sof_ipc4_tx_payload_for_get_data(struct sof_ipc4_msg *tx)
+{
+ /*
+ * Messages that require TX payload with LARGE_CONFIG_GET.
+ * The TX payload is placed into the IPC message data section by caller,
+ * which needs to be copied to temporary buffer since the received data
+ * will overwrite it.
+ */
+ switch (tx->extension & SOF_IPC4_MOD_EXT_MSG_PARAM_ID_MASK) {
+ case SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_SWITCH_CONTROL_PARAM_ID):
+ case SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_ENUM_CONTROL_PARAM_ID):
+ case SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_BYTES_CONTROL_PARAM_ID):
+ return true;
+ default:
+ return false;
+ }
+}
+
static int sof_ipc4_set_get_data(struct snd_sof_dev *sdev, void *data,
size_t payload_bytes, bool set)
{
@@ -444,6 +461,8 @@ static int sof_ipc4_set_get_data(struct snd_sof_dev *sdev, void *data,
struct sof_ipc4_msg tx = {{ 0 }};
struct sof_ipc4_msg rx = {{ 0 }};
size_t remaining = payload_bytes;
+ void *tx_payload_for_get = NULL;
+ size_t tx_data_size = 0;
size_t offset = 0;
size_t chunk_size;
int ret;
@@ -469,13 +488,23 @@ static int sof_ipc4_set_get_data(struct snd_sof_dev *sdev, void *data,
tx.extension |= SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK(1);
+ if (sof_ipc4_tx_payload_for_get_data(&tx)) {
+ tx_data_size = min(ipc4_msg->data_size, payload_limit);
+ tx_payload_for_get = kmemdup(ipc4_msg->data_ptr, tx_data_size,
+ GFP_KERNEL);
+ if (!tx_payload_for_get)
+ return -ENOMEM;
+ }
+
/* ensure the DSP is in D0i0 before sending IPC */
ret = snd_sof_dsp_set_power_state(sdev, &target_state);
- if (ret < 0)
+ if (ret < 0) {
+ kfree(tx_payload_for_get);
return ret;
+ }
/* Serialise IPC TX */
- mutex_lock(&sdev->ipc->tx_mutex);
+ guard(mutex)(&sdev->ipc->tx_mutex);
do {
size_t tx_size, rx_size;
@@ -506,7 +535,15 @@ static int sof_ipc4_set_get_data(struct snd_sof_dev *sdev, void *data,
rx.data_size = chunk_size;
rx.data_ptr = ipc4_msg->data_ptr + offset;
- tx_size = 0;
+ if (tx_payload_for_get) {
+ tx_size = tx_data_size;
+ tx.data_size = tx_size;
+ tx.data_ptr = tx_payload_for_get;
+ } else {
+ tx_size = 0;
+ tx.data_size = 0;
+ tx.data_ptr = NULL;
+ }
rx_size = chunk_size;
}
@@ -551,7 +588,7 @@ out:
if (sof_debug_check_flag(SOF_DBG_DUMP_IPC_MESSAGE_PAYLOAD))
sof_ipc4_dump_payload(sdev, ipc4_msg->data_ptr, ipc4_msg->data_size);
- mutex_unlock(&sdev->ipc->tx_mutex);
+ kfree(tx_payload_for_get);
return ret;
}
@@ -892,6 +929,19 @@ void sof_ipc4_mic_privacy_state_change(struct snd_sof_dev *sdev, bool state)
struct sof_ipc4_msg msg;
u32 data = state;
+ /*
+ * The mic privacy change notification's role is to notify the running
+ * firmware that there is a change in mic privacy state from whatever
+ * the state was before - since the firmware booted up or since the
+ * previous change during runtime.
+ *
+ * If the firmware has not been booted up, there is no need to send
+ * change notification (the firmware is not booted up).
+ * The firmware checks the current state during its boot.
+ */
+ if (sdev->fw_state != SOF_FW_BOOT_COMPLETE)
+ return;
+
msg.primary = SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
msg.primary |= SOF_IPC4_MOD_ID(SOF_IPC4_MOD_INIT_BASEFW_MOD_ID);
diff --git a/sound/soc/sof/mediatek/mtk-adsp-common.c b/sound/soc/sof/mediatek/mtk-adsp-common.c
index 01bbadb160ff..75b4af4b5111 100644
--- a/sound/soc/sof/mediatek/mtk-adsp-common.c
+++ b/sound/soc/sof/mediatek/mtk-adsp-common.c
@@ -107,11 +107,9 @@ EXPORT_SYMBOL(mtk_adsp_send_msg);
void mtk_adsp_handle_reply(struct mtk_adsp_ipc *ipc)
{
struct adsp_priv *priv = mtk_adsp_ipc_get_data(ipc);
- unsigned long flags;
- spin_lock_irqsave(&priv->sdev->ipc_lock, flags);
+ guard(spinlock_irqsave)(&priv->sdev->ipc_lock);
snd_sof_ipc_process_reply(priv->sdev, 0);
- spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags);
}
EXPORT_SYMBOL(mtk_adsp_handle_reply);
diff --git a/sound/soc/sof/ops.c b/sound/soc/sof/ops.c
index bd52e7ec6883..74c04dcf4167 100644
--- a/sound/soc/sof/ops.c
+++ b/sound/soc/sof/ops.c
@@ -38,13 +38,8 @@ bool snd_sof_pci_update_bits_unlocked(struct snd_sof_dev *sdev, u32 offset,
bool snd_sof_pci_update_bits(struct snd_sof_dev *sdev, u32 offset,
u32 mask, u32 value)
{
- unsigned long flags;
- bool change;
-
- spin_lock_irqsave(&sdev->hw_lock, flags);
- change = snd_sof_pci_update_bits_unlocked(sdev, offset, mask, value);
- spin_unlock_irqrestore(&sdev->hw_lock, flags);
- return change;
+ guard(spinlock_irqsave)(&sdev->hw_lock);
+ return snd_sof_pci_update_bits_unlocked(sdev, offset, mask, value);
}
EXPORT_SYMBOL(snd_sof_pci_update_bits);
@@ -90,28 +85,16 @@ EXPORT_SYMBOL(snd_sof_dsp_update_bits64_unlocked);
bool snd_sof_dsp_update_bits(struct snd_sof_dev *sdev, u32 bar, u32 offset,
u32 mask, u32 value)
{
- unsigned long flags;
- bool change;
-
- spin_lock_irqsave(&sdev->hw_lock, flags);
- change = snd_sof_dsp_update_bits_unlocked(sdev, bar, offset, mask,
- value);
- spin_unlock_irqrestore(&sdev->hw_lock, flags);
- return change;
+ guard(spinlock_irqsave)(&sdev->hw_lock);
+ return snd_sof_dsp_update_bits_unlocked(sdev, bar, offset, mask, value);
}
EXPORT_SYMBOL(snd_sof_dsp_update_bits);
bool snd_sof_dsp_update_bits64(struct snd_sof_dev *sdev, u32 bar, u32 offset,
u64 mask, u64 value)
{
- unsigned long flags;
- bool change;
-
- spin_lock_irqsave(&sdev->hw_lock, flags);
- change = snd_sof_dsp_update_bits64_unlocked(sdev, bar, offset, mask,
- value);
- spin_unlock_irqrestore(&sdev->hw_lock, flags);
- return change;
+ guard(spinlock_irqsave)(&sdev->hw_lock);
+ return snd_sof_dsp_update_bits64_unlocked(sdev, bar, offset, mask, value);
}
EXPORT_SYMBOL(snd_sof_dsp_update_bits64);
@@ -134,11 +117,8 @@ void snd_sof_dsp_update_bits_forced_unlocked(struct snd_sof_dev *sdev, u32 bar,
void snd_sof_dsp_update_bits_forced(struct snd_sof_dev *sdev, u32 bar,
u32 offset, u32 mask, u32 value)
{
- unsigned long flags;
-
- spin_lock_irqsave(&sdev->hw_lock, flags);
+ guard(spinlock_irqsave)(&sdev->hw_lock);
snd_sof_dsp_update_bits_forced_unlocked(sdev, bar, offset, mask, value);
- spin_unlock_irqrestore(&sdev->hw_lock, flags);
}
EXPORT_SYMBOL(snd_sof_dsp_update_bits_forced);
diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h
index d73644e85b6e..72af1f4ff620 100644
--- a/sound/soc/sof/ops.h
+++ b/sound/soc/sof/ops.h
@@ -287,16 +287,12 @@ static inline int
snd_sof_dsp_set_power_state(struct snd_sof_dev *sdev,
const struct sof_dsp_power_state *target_state)
{
- int ret = 0;
-
- mutex_lock(&sdev->power_state_access);
+ guard(mutex)(&sdev->power_state_access);
if (sof_ops(sdev)->set_power_state)
- ret = sof_ops(sdev)->set_power_state(sdev, target_state);
-
- mutex_unlock(&sdev->power_state_access);
+ return sof_ops(sdev)->set_power_state(sdev, target_state);
- return ret;
+ return 0;
}
/* debug */
diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c
index cee04574264e..5b598d0940eb 100644
--- a/sound/soc/sof/pcm.c
+++ b/sound/soc/sof/pcm.c
@@ -88,9 +88,9 @@ sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_run
spcm->stream[dir].list = list;
- ret = sof_widget_list_setup(sdev, spcm, params, platform_params, dir);
+ ret = sof_widget_list_prepare(sdev, spcm, params, platform_params, dir);
if (ret < 0) {
- spcm_err(spcm, dir, "Widget list set up failed\n");
+ spcm_err(spcm, dir, "widget list prepare failed\n");
spcm->stream[dir].list = NULL;
snd_soc_dapm_dai_free_widgets(&list);
return ret;
@@ -100,15 +100,30 @@ sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_run
return 0;
}
+static struct snd_sof_widget *snd_sof_find_swidget_by_comp_id(struct snd_sof_dev *sdev,
+ int comp_id)
+{
+ struct snd_sof_widget *swidget;
+
+ list_for_each_entry(swidget, &sdev->widget_list, list) {
+ if (comp_id == swidget->comp_id)
+ return swidget;
+ }
+
+ return NULL;
+}
+
static int sof_pcm_hw_params(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
- struct snd_sof_platform_stream_params platform_params = { 0 };
+ struct snd_sof_platform_stream_params *platform_params;
struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_sof_widget *host_widget;
struct snd_sof_pcm *spcm;
int ret;
@@ -122,6 +137,16 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
spcm_dbg(spcm, substream->stream, "Entry: hw_params\n");
+ if (!sdev->dspless_mode_selected) {
+ /*
+ * Make sure that the DSP is booted up, which might not be the
+ * case if the on-demand DSP boot is used
+ */
+ ret = snd_sof_boot_dsp_firmware(sdev);
+ if (ret)
+ return ret;
+ }
+
/*
* Handle repeated calls to hw_params() without free_pcm() in
* between. At least ALSA OSS emulation depends on this.
@@ -134,7 +159,8 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
spcm->prepared[substream->stream] = false;
}
- ret = snd_sof_pcm_platform_hw_params(sdev, substream, params, &platform_params);
+ platform_params = &spcm->platform_params[substream->stream];
+ ret = snd_sof_pcm_platform_hw_params(sdev, substream, params, platform_params);
if (ret < 0) {
spcm_err(spcm, substream->stream, "platform hw params failed\n");
return ret;
@@ -142,12 +168,27 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
/* if this is a repeated hw_params without hw_free, skip setting up widgets */
if (!spcm->stream[substream->stream].list) {
- ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, params, &platform_params,
+ ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, params, platform_params,
substream->stream);
if (ret < 0)
return ret;
}
+ if (!sdev->dspless_mode_selected) {
+ int host_comp_id = spcm->stream[substream->stream].comp_id;
+
+ host_widget = snd_sof_find_swidget_by_comp_id(sdev, host_comp_id);
+ if (!host_widget) {
+ spcm_err(spcm, substream->stream,
+ "failed to find host widget with comp_id %d\n", host_comp_id);
+ return -EINVAL;
+ }
+
+ /* set the host DMA ID */
+ if (tplg_ops && tplg_ops->host_config)
+ tplg_ops->host_config(sdev, host_widget, platform_params);
+ }
+
/* create compressed page table for audio firmware */
if (runtime->buffer_changed) {
struct snd_dma_buffer *dmab = snd_pcm_get_dma_buf(substream);
@@ -159,14 +200,6 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
return ret;
}
- if (pcm_ops && pcm_ops->hw_params) {
- ret = pcm_ops->hw_params(component, substream, params, &platform_params);
- if (ret < 0)
- return ret;
- }
-
- spcm->prepared[substream->stream] = true;
-
/* save pcm hw_params */
memcpy(&spcm->params[substream->stream], params, sizeof(*params));
@@ -271,6 +304,9 @@ static int sof_pcm_hw_free(struct snd_soc_component *component,
ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, true);
+ /* unprepare and free the list of DAPM widgets */
+ sof_widget_list_unprepare(sdev, spcm, substream->stream);
+
cancel_work_sync(&spcm->stream[substream->stream].period_elapsed_work);
return ret;
@@ -281,7 +317,12 @@ static int sof_pcm_prepare(struct snd_soc_component *component,
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
+ const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
+ struct snd_sof_platform_stream_params *platform_params;
+ struct snd_soc_dapm_widget_list *list;
+ struct snd_pcm_hw_params *params;
struct snd_sof_pcm *spcm;
+ int dir = substream->stream;
int ret;
/* nothing to do for BE */
@@ -307,15 +348,33 @@ static int sof_pcm_prepare(struct snd_soc_component *component,
return ret;
}
- /* set hw_params */
- ret = sof_pcm_hw_params(component,
- substream, &spcm->params[substream->stream]);
+ ret = sof_pcm_hw_params(component, substream, &spcm->params[substream->stream]);
if (ret < 0) {
spcm_err(spcm, substream->stream,
"failed to set hw_params after resume\n");
return ret;
}
+ list = spcm->stream[dir].list;
+ params = &spcm->params[substream->stream];
+ platform_params = &spcm->platform_params[substream->stream];
+ ret = sof_widget_list_setup(sdev, spcm, params, platform_params, dir);
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed widget list set up for pcm %d dir %d\n",
+ spcm->pcm.pcm_id, dir);
+ spcm->stream[dir].list = NULL;
+ snd_soc_dapm_dai_free_widgets(&list);
+ return ret;
+ }
+
+ if (pcm_ops && pcm_ops->hw_params) {
+ ret = pcm_ops->hw_params(component, substream, params, platform_params);
+ if (ret < 0)
+ return ret;
+ }
+
+ spcm->prepared[substream->stream] = true;
+
return 0;
}
diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c
index 8e3bcf602beb..dd7cd87f1fa5 100644
--- a/sound/soc/sof/pm.c
+++ b/sound/soc/sof/pm.c
@@ -8,10 +8,15 @@
// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
//
+#include <linux/module.h>
#include "ops.h"
#include "sof-priv.h"
#include "sof-audio.h"
+static int override_on_demand_boot = -1;
+module_param_named(on_demand_boot, override_on_demand_boot, int, 0444);
+MODULE_PARM_DESC(on_demand_boot, "Force on-demand DSP boot: 0 - disabled, 1 - enabled");
+
/*
* Helper function to determine the target DSP state during
* system suspend. This function only cares about the device
@@ -70,67 +75,28 @@ static void sof_cache_debugfs(struct snd_sof_dev *sdev)
}
#endif
-static int sof_resume(struct device *dev, bool runtime_resume)
+int snd_sof_boot_dsp_firmware(struct snd_sof_dev *sdev)
{
- struct snd_sof_dev *sdev = dev_get_drvdata(dev);
const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm);
const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
- u32 old_state = sdev->dsp_power_state.state;
int ret;
- /* do nothing if dsp resume callbacks are not set */
- if (!runtime_resume && !sof_ops(sdev)->resume)
- return 0;
-
- if (runtime_resume && !sof_ops(sdev)->runtime_resume)
- return 0;
-
- /* DSP was never successfully started, nothing to resume */
- if (sdev->first_boot)
- return 0;
-
- /*
- * if the runtime_resume flag is set, call the runtime_resume routine
- * or else call the system resume routine
- */
- if (runtime_resume)
- ret = snd_sof_dsp_runtime_resume(sdev);
- else
- ret = snd_sof_dsp_resume(sdev);
- if (ret < 0) {
- dev_err(sdev->dev,
- "error: failed to power up DSP after resume\n");
- return ret;
- }
+ guard(mutex)(&sdev->dsp_fw_boot_mutex);
- if (sdev->dspless_mode_selected) {
- sof_set_fw_state(sdev, SOF_DSPLESS_MODE);
+ if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) {
+ /* Firmware already booted, just return */
return 0;
}
- /*
- * Nothing further to be done for platforms that support the low power
- * D0 substate. Resume trace and return when resuming from
- * low-power D0 substate
- */
- if (!runtime_resume && sof_ops(sdev)->set_power_state &&
- old_state == SOF_DSP_PM_D0) {
- ret = sof_fw_trace_resume(sdev);
- if (ret < 0)
- /* non fatal */
- dev_warn(sdev->dev,
- "failed to enable trace after resume %d\n", ret);
- return 0;
- }
+ dev_dbg(sdev->dev, "Booting DSP firmware\n");
sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE);
/* load the firmware */
ret = snd_sof_load_firmware(sdev);
if (ret < 0) {
- dev_err(sdev->dev,
- "error: failed to load DSP firmware after resume %d\n",
- ret);
+ dev_err(sdev->dev, "%s: failed to load DSP firmware: %d\n",
+ __func__, ret);
sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED);
return ret;
}
@@ -143,9 +109,8 @@ static int sof_resume(struct device *dev, bool runtime_resume)
*/
ret = snd_sof_run_firmware(sdev);
if (ret < 0) {
- dev_err(sdev->dev,
- "error: failed to boot DSP firmware after resume %d\n",
- ret);
+ dev_err(sdev->dev, "%s: failed to boot DSP firmware: %d\n",
+ __func__, ret);
sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED);
return ret;
}
@@ -154,16 +119,16 @@ static int sof_resume(struct device *dev, bool runtime_resume)
ret = sof_fw_trace_resume(sdev);
if (ret < 0) {
/* non fatal */
- dev_warn(sdev->dev,
- "warning: failed to init trace after resume %d\n",
- ret);
+ dev_warn(sdev->dev, "%s: failed to resume trace: %d\n",
+ __func__, ret);
}
/* restore pipelines */
if (tplg_ops && tplg_ops->set_up_all_pipelines) {
ret = tplg_ops->set_up_all_pipelines(sdev, false);
if (ret < 0) {
- dev_err(sdev->dev, "Failed to restore pipeline after resume %d\n", ret);
+ dev_err(sdev->dev, "%s: failed to restore pipeline: %d\n",
+ __func__, ret);
goto setup_fail;
}
}
@@ -175,7 +140,8 @@ static int sof_resume(struct device *dev, bool runtime_resume)
if (pm_ops && pm_ops->ctx_restore) {
ret = pm_ops->ctx_restore(sdev);
if (ret < 0)
- dev_err(sdev->dev, "ctx_restore IPC error during resume: %d\n", ret);
+ dev_err(sdev->dev, "%s: ctx_restore IPC failed: %d\n",
+ __func__, ret);
}
setup_fail:
@@ -192,6 +158,73 @@ setup_fail:
return ret;
}
+EXPORT_SYMBOL(snd_sof_boot_dsp_firmware);
+
+static int sof_resume(struct device *dev, bool runtime_resume)
+{
+ struct snd_sof_dev *sdev = dev_get_drvdata(dev);
+ u32 old_state = sdev->dsp_power_state.state;
+ bool on_demand_boot;
+ int ret;
+
+ /* do nothing if dsp resume callbacks are not set */
+ if (!runtime_resume && !sof_ops(sdev)->resume)
+ return 0;
+
+ if (runtime_resume && !sof_ops(sdev)->runtime_resume)
+ return 0;
+
+ /* DSP was never successfully started, nothing to resume */
+ if (sdev->first_boot)
+ return 0;
+
+ /*
+ * if the runtime_resume flag is set, call the runtime_resume routine
+ * or else call the system resume routine
+ */
+ if (runtime_resume)
+ ret = snd_sof_dsp_runtime_resume(sdev);
+ else
+ ret = snd_sof_dsp_resume(sdev);
+ if (ret < 0) {
+ dev_err(sdev->dev,
+ "error: failed to power up DSP after resume\n");
+ return ret;
+ }
+
+ if (sdev->dspless_mode_selected) {
+ sof_set_fw_state(sdev, SOF_DSPLESS_MODE);
+ return 0;
+ }
+
+ /*
+ * Nothing further to be done for platforms that support the low power
+ * D0 substate. Resume trace and return when resuming from
+ * low-power D0 substate
+ */
+ if (!runtime_resume && sof_ops(sdev)->set_power_state &&
+ old_state == SOF_DSP_PM_D0) {
+ ret = sof_fw_trace_resume(sdev);
+ if (ret < 0)
+ /* non fatal */
+ dev_warn(sdev->dev,
+ "failed to enable trace after resume %d\n", ret);
+ return 0;
+ }
+
+ if (override_on_demand_boot > -1)
+ on_demand_boot = override_on_demand_boot ? true : false;
+ else
+ on_demand_boot = sdev->pdata->desc->on_demand_dsp_boot;
+
+ if (on_demand_boot) {
+ /* Only change the fw_state to PREPARE but skip booting */
+ sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE);
+ return 0;
+ }
+
+ return snd_sof_boot_dsp_firmware(sdev);
+}
static int sof_suspend(struct device *dev, bool runtime_suspend)
{
@@ -297,8 +330,12 @@ int snd_sof_dsp_power_down_notify(struct snd_sof_dev *sdev)
{
const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm);
- /* Notify DSP of upcoming power down */
- if (sof_ops(sdev)->remove && pm_ops && pm_ops->ctx_save)
+ /*
+ * Notify DSP of upcoming power down only if the firmware has been
+ * booted up
+ */
+ if (sdev->fw_state == SOF_FW_BOOT_COMPLETE && sof_ops(sdev)->remove &&
+ pm_ops && pm_ops->ctx_save)
return pm_ops->ctx_save(sdev);
return 0;
diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c
index a9664b4cf43f..acf56607bc9c 100644
--- a/sound/soc/sof/sof-audio.c
+++ b/sound/soc/sof/sof-audio.c
@@ -13,6 +13,21 @@
#include "sof-audio.h"
#include "ops.h"
+/*
+ * Check if a DAI widget is an aggregated DAI. Aggregated DAI's have names ending in numbers
+ * starting with 0. For example: in the case of a SDW speaker with 2 amps, the topology contains
+ * 2 DAI's names alh-copier.SDW1.Playback.0 and alh-copier-SDW1.Playback.1. In this case, only the
+ * DAI alh-copier.SDW1.Playback.0 is set up in the firmware. The other DAI,
+ * alh-copier.SDW1.Playback.1 in topology is for the sake of completeness to show aggregation for
+ * the speaker amp and does not need any firmware configuration.
+ */
+static bool is_aggregated_dai(struct snd_sof_widget *swidget)
+{
+ return (WIDGET_IS_DAI(swidget->id) &&
+ isdigit(swidget->widget->name[strlen(swidget->widget->name) - 1]) &&
+ swidget->widget->name[strlen(swidget->widget->name) - 1] != '0');
+}
+
static bool is_virtual_widget(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
const char *func)
{
@@ -121,13 +136,8 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev,
int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
{
- int ret;
-
- mutex_lock(&swidget->setup_mutex);
- ret = sof_widget_free_unlocked(sdev, swidget);
- mutex_unlock(&swidget->setup_mutex);
-
- return ret;
+ guard(mutex)(&swidget->setup_mutex);
+ return sof_widget_free_unlocked(sdev, swidget);
}
EXPORT_SYMBOL(sof_widget_free);
@@ -240,13 +250,8 @@ use_count_dec:
int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
{
- int ret;
-
- mutex_lock(&swidget->setup_mutex);
- ret = sof_widget_setup_unlocked(sdev, swidget);
- mutex_unlock(&swidget->setup_mutex);
-
- return ret;
+ guard(mutex)(&swidget->setup_mutex);
+ return sof_widget_setup_unlocked(sdev, swidget);
}
EXPORT_SYMBOL(sof_widget_setup);
@@ -264,6 +269,10 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc
is_virtual_widget(sdev, sink_widget->widget, __func__))
return 0;
+ /* skip route if source/sink widget is not set up */
+ if (!src_widget->use_count || !sink_widget->use_count)
+ return 0;
+
/* find route matching source and sink widgets */
list_for_each_entry(sroute, &sdev->route_list, list)
if (sroute->src_widget == src_widget && sroute->sink_widget == sink_widget) {
@@ -292,10 +301,34 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc
return 0;
}
+static bool sof_widget_in_same_direction(struct snd_sof_widget *swidget, int dir)
+{
+ return swidget->spipe->direction == dir;
+}
+
+static int sof_set_up_same_dir_widget_routes(struct snd_sof_dev *sdev,
+ struct snd_soc_dapm_widget *wsource,
+ struct snd_soc_dapm_widget *wsink)
+{
+ struct snd_sof_widget *src_widget = wsource->dobj.private;
+ struct snd_sof_widget *sink_widget = wsink->dobj.private;
+
+ /*
+ * skip setting up route if source and sink are in different directions (ex. playback and
+ * echo ref) if the direction is set in topology. These will be set up later. It is enough
+ * to check if the direction_valid is set for one of the widgets as all widgets will have
+ * the direction set in topology if one is set.
+ */
+ if (sink_widget->spipe && sink_widget->spipe->direction_valid &&
+ !sof_widget_in_same_direction(sink_widget, src_widget->spipe->direction))
+ return 0;
+
+ return sof_route_setup(sdev, wsource, wsink);
+}
+
static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
struct snd_soc_dapm_widget_list *list, int dir)
{
- const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
struct snd_soc_dapm_widget *widget;
struct snd_sof_route *sroute;
struct snd_soc_dapm_path *p;
@@ -318,7 +351,8 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
continue;
if (p->sink->dobj.private) {
- ret = sof_route_setup(sdev, widget, p->sink);
+ ret = sof_set_up_same_dir_widget_routes(sdev, widget,
+ p->sink);
if (ret < 0)
return ret;
}
@@ -334,7 +368,8 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
continue;
if (p->source->dobj.private) {
- ret = sof_route_setup(sdev, p->source, widget);
+ ret = sof_set_up_same_dir_widget_routes(sdev, p->source,
+ widget);
if (ret < 0)
return ret;
}
@@ -350,7 +385,6 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
*/
list_for_each_entry(sroute, &sdev->route_list, list) {
bool src_widget_in_dapm_list, sink_widget_in_dapm_list;
- struct snd_sof_widget *swidget;
if (sroute->setup)
continue;
@@ -359,41 +393,36 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
sink_widget_in_dapm_list = widget_in_list(list, sroute->sink_widget->widget);
/*
- * if both source and sink are in the DAPM list, the route must already have been
- * set up above. And if neither are in the DAPM list, the route shouldn't be
- * handled now.
+ * no need to set up the route if both the source and sink widgets are not in the
+ * DAPM list
*/
- if (src_widget_in_dapm_list == sink_widget_in_dapm_list)
+ if (!src_widget_in_dapm_list && !sink_widget_in_dapm_list)
continue;
/*
- * At this point either the source widget or the sink widget is in the DAPM list
- * with a route that might need to be set up. Check the use_count of the widget
- * that is not in the DAPM list to confirm if it is in use currently before setting
- * up the route.
+ * set up the route only if both the source and sink widgets are in the DAPM list
+ * but are in different directions. The ones in the same direction would already
+ * have been set up in the previous loop.
*/
- if (src_widget_in_dapm_list)
- swidget = sroute->sink_widget;
- else
- swidget = sroute->src_widget;
-
- mutex_lock(&swidget->setup_mutex);
- if (!swidget->use_count) {
- mutex_unlock(&swidget->setup_mutex);
- continue;
- }
+ if (src_widget_in_dapm_list && sink_widget_in_dapm_list) {
+ struct snd_sof_widget *src_widget, *sink_widget;
+
+ src_widget = sroute->src_widget->widget->dobj.private;
+ sink_widget = sroute->sink_widget->widget->dobj.private;
- if (tplg_ops && tplg_ops->route_setup) {
/*
- * this route will get freed when either the source widget or the sink
- * widget is freed during hw_free
+ * it is enough to check if the direction_valid is set for one of the
+ * widgets as all widgets will have the direction set in topology if one
+ * is set.
*/
- ret = tplg_ops->route_setup(sdev, sroute);
- if (!ret)
- sroute->setup = true;
+ if (src_widget && sink_widget &&
+ src_widget->spipe && src_widget->spipe->direction_valid &&
+ sof_widget_in_same_direction(sink_widget, src_widget->spipe->direction))
+ continue;
}
- mutex_unlock(&swidget->setup_mutex);
+ ret = sof_route_setup(sdev, sroute->src_widget->widget,
+ sroute->sink_widget->widget);
if (ret < 0)
return ret;
@@ -404,7 +433,7 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
static void
sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
- struct snd_soc_dapm_widget_list *list)
+ struct snd_soc_dapm_widget_list *list, int dir)
{
const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
struct snd_sof_widget *swidget = widget->dobj.private;
@@ -414,8 +443,15 @@ sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widg
if (is_virtual_widget(sdev, widget, __func__))
return;
- /* skip if the widget is in use or if it is already unprepared */
- if (!swidget || !swidget->prepared || swidget->use_count > 0)
+ if (!swidget)
+ goto sink_unprepare;
+
+ if (swidget->spipe && swidget->spipe->direction_valid &&
+ !sof_widget_in_same_direction(swidget, dir))
+ return;
+
+ /* skip widgets in use, those already unprepared or aggregated DAIs */
+ if (!swidget->prepared || swidget->use_count > 0 || is_aggregated_dai(swidget))
goto sink_unprepare;
widget_ops = tplg_ops ? tplg_ops->widget : NULL;
@@ -430,9 +466,10 @@ sink_unprepare:
snd_soc_dapm_widget_for_each_sink_path(widget, p) {
if (!widget_in_list(list, p->sink))
continue;
+
if (!p->walking && p->sink->dobj.private) {
p->walking = true;
- sof_unprepare_widgets_in_path(sdev, p->sink, list);
+ sof_unprepare_widgets_in_path(sdev, p->sink, list, dir);
p->walking = false;
}
}
@@ -454,11 +491,20 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget
if (is_virtual_widget(sdev, widget, __func__))
return 0;
+ if (!swidget)
+ goto sink_prepare;
+
widget_ops = tplg_ops ? tplg_ops->widget : NULL;
if (!widget_ops)
return 0;
- if (!swidget || !widget_ops[widget->id].ipc_prepare || swidget->prepared)
+ if (swidget->spipe && swidget->spipe->direction_valid &&
+ !sof_widget_in_same_direction(swidget, dir))
+ return 0;
+
+ /* skip widgets already prepared or aggregated DAI widgets*/
+ if (!widget_ops[widget->id].ipc_prepare || swidget->prepared ||
+ is_aggregated_dai(swidget))
goto sink_prepare;
/* prepare the source widget */
@@ -476,6 +522,7 @@ sink_prepare:
snd_soc_dapm_widget_for_each_sink_path(widget, p) {
if (!widget_in_list(list, p->sink))
continue;
+
if (!p->walking && p->sink->dobj.private) {
p->walking = true;
ret = sof_prepare_widgets_in_path(sdev, p->sink, fe_params,
@@ -505,6 +552,7 @@ static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dap
int dir, struct snd_sof_pcm *spcm)
{
struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
+ struct snd_sof_widget *swidget = widget->dobj.private;
struct snd_soc_dapm_path *p;
int err;
int ret = 0;
@@ -512,12 +560,21 @@ static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dap
if (is_virtual_widget(sdev, widget, __func__))
return 0;
- if (widget->dobj.private) {
- err = sof_widget_free(sdev, widget->dobj.private);
- if (err < 0)
- ret = err;
- }
+ if (!swidget)
+ goto sink_free;
+ if (swidget->spipe && swidget->spipe->direction_valid &&
+ !sof_widget_in_same_direction(swidget, dir))
+ return 0;
+
+ /* skip aggregated DAIs */
+ if (is_aggregated_dai(swidget))
+ goto sink_free;
+
+ err = sof_widget_free(sdev, widget->dobj.private);
+ if (err < 0)
+ ret = err;
+sink_free:
/* free all widgets in the sink paths even in case of error to keep use counts balanced */
snd_soc_dapm_widget_for_each_sink_path(widget, p) {
if (!p->walking) {
@@ -557,7 +614,15 @@ static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_d
if (swidget) {
int i;
- ret = sof_widget_setup(sdev, widget->dobj.private);
+ if (swidget->spipe && swidget->spipe->direction_valid &&
+ !sof_widget_in_same_direction(swidget, dir))
+ return 0;
+
+ /* skip aggregated DAIs */
+ if (is_aggregated_dai(swidget))
+ goto sink_setup;
+
+ ret = sof_widget_setup(sdev, swidget);
if (ret < 0)
return ret;
@@ -619,15 +684,13 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
return 0;
for_each_dapm_widgets(list, i, widget) {
- if (is_virtual_widget(sdev, widget, __func__))
- continue;
-
- /* starting widget for playback is AIF type */
+ /* starting widget for playback is of AIF type */
if (dir == SNDRV_PCM_STREAM_PLAYBACK && widget->id != snd_soc_dapm_aif_in)
continue;
/* starting widget for capture is DAI type */
- if (dir == SNDRV_PCM_STREAM_CAPTURE && widget->id != snd_soc_dapm_dai_out)
+ if (dir == SNDRV_PCM_STREAM_CAPTURE && widget->id != snd_soc_dapm_dai_out &&
+ widget->id != snd_soc_dapm_output)
continue;
switch (op) {
@@ -657,7 +720,7 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
break;
}
case SOF_WIDGET_UNPREPARE:
- sof_unprepare_widgets_in_path(sdev, widget, list);
+ sof_unprepare_widgets_in_path(sdev, widget, list, dir);
break;
default:
dev_err(sdev->dev, "Invalid widget op %d\n", op);
@@ -672,6 +735,30 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
return 0;
}
+int sof_widget_list_prepare(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
+ struct snd_pcm_hw_params *fe_params,
+ struct snd_sof_platform_stream_params *platform_params,
+ int dir)
+{
+ /*
+ * Prepare widgets for set up. The prepare step is used to allocate memory, assign
+ * instance ID and pick the widget configuration based on the runtime PCM params.
+ */
+ return sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params,
+ dir, SOF_WIDGET_PREPARE);
+}
+
+void sof_widget_list_unprepare(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir)
+{
+ struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
+
+ /* unprepare the widget */
+ sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
+
+ snd_soc_dapm_dai_free_widgets(&list);
+ spcm->stream[dir].list = NULL;
+}
+
int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
struct snd_pcm_hw_params *fe_params,
struct snd_sof_platform_stream_params *platform_params,
@@ -682,19 +769,10 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
struct snd_soc_dapm_widget *widget;
int i, ret;
- /* nothing to set up */
- if (!list)
+ /* nothing to set up or setup has been already done */
+ if (!list || spcm->setup_done[dir])
return 0;
- /*
- * Prepare widgets for set up. The prepare step is used to allocate memory, assign
- * instance ID and pick the widget configuration based on the runtime PCM params.
- */
- ret = sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params,
- dir, SOF_WIDGET_PREPARE);
- if (ret < 0)
- return ret;
-
/* Set up is used to send the IPC to the DSP to create the widget */
ret = sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params,
dir, SOF_WIDGET_SETUP);
@@ -749,6 +827,8 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
}
}
+ spcm->setup_done[dir] = true;
+
return 0;
widget_free:
@@ -766,18 +846,13 @@ int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int
int ret;
/* nothing to free */
- if (!list)
+ if (!list || !spcm->setup_done[dir])
return 0;
/* send IPC to free widget in the DSP */
ret = sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_FREE);
- /* unprepare the widget */
- sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
-
- snd_soc_dapm_dai_free_widgets(&list);
- spcm->stream[dir].list = NULL;
-
+ spcm->setup_done[dir] = false;
pipeline_list->count = 0;
return ret;
diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h
index a8b93a2eec9c..36082e764bf9 100644
--- a/sound/soc/sof/sof-audio.h
+++ b/sound/soc/sof/sof-audio.h
@@ -209,6 +209,7 @@ struct sof_ipc_tplg_widget_ops {
* @widget_setup: Function pointer for setting up setup in the DSP
* @widget_free: Function pointer for freeing widget in the DSP
* @dai_config: Function pointer for sending DAI config IPC to the DSP
+ * @host_config: Function pointer for setting the DMA ID for host widgets
* @dai_get_param: Function pointer for getting the DAI parameter
* @set_up_all_pipelines: Function pointer for setting up all topology pipelines
* @tear_down_all_pipelines: Function pointer for tearing down all topology pipelines
@@ -230,6 +231,8 @@ struct sof_ipc_tplg_ops {
int (*widget_free)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget);
int (*dai_config)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
unsigned int flags, struct snd_sof_dai_config_data *data);
+ void (*host_config)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
+ struct snd_sof_platform_stream_params *platform_params);
int (*dai_get_param)(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int param_type);
int (*set_up_all_pipelines)(struct snd_sof_dev *sdev, bool verify);
int (*tear_down_all_pipelines)(struct snd_sof_dev *sdev, bool verify);
@@ -351,7 +354,9 @@ struct snd_sof_pcm {
struct snd_sof_pcm_stream stream[2];
struct list_head list; /* list in sdev pcm list */
struct snd_pcm_hw_params params[2];
+ struct snd_sof_platform_stream_params platform_params[2];
bool prepared[2]; /* PCM_PARAMS set successfully */
+ bool setup_done[2]; /* the setup of the SOF PCM device is done */
bool pending_stop[2]; /* only used if (!pcm_ops->platform_stop_during_hw_free) */
/* Must be last - ends in a flex-array member. */
@@ -454,6 +459,11 @@ struct snd_sof_widget {
/* Scheduling domain (enum sof_comp_domain), unset, Low Latency, or Data Processing */
u32 comp_domain;
+ /* The values below are added to mod_init pay load if comp_domain indicates DP component */
+ u32 dp_domain_id; /* DP process userspace domain ID */
+ u32 dp_stack_bytes; /* DP process stack size requirement in bytes */
+ u32 dp_heap_bytes; /* DP process heap size requirement in bytes */
+
struct snd_soc_dapm_widget *widget;
struct list_head list; /* list in sdev widget list */
struct snd_sof_pipeline *spipe;
@@ -502,6 +512,9 @@ struct snd_sof_widget {
* @complete: flag used to indicate that pipeline set up is complete.
* @core_mask: Mask containing target cores for all modules in the pipeline
* @list: List item in sdev pipeline_list
+ * @direction_valid: flag indicating if the direction is set in topology
+ * @direction: pipeline direction set in topology, valid is direction_valid is true
+ *
*/
struct snd_sof_pipeline {
struct snd_sof_widget *pipe_widget;
@@ -510,6 +523,8 @@ struct snd_sof_pipeline {
int complete;
unsigned long core_mask;
struct list_head list;
+ bool direction_valid;
+ u32 direction;
};
/* ASoC SOF DAPM route */
@@ -668,6 +683,11 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
struct snd_pcm_hw_params *fe_params,
struct snd_sof_platform_stream_params *platform_params,
int dir);
+int sof_widget_list_prepare(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
+ struct snd_pcm_hw_params *fe_params,
+ struct snd_sof_platform_stream_params *platform_params,
+ int dir);
+void sof_widget_list_unprepare(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir);
int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir);
int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev *sdev,
struct snd_sof_pcm *spcm);
diff --git a/sound/soc/sof/sof-client-ipc-flood-test.c b/sound/soc/sof/sof-client-ipc-flood-test.c
index 373f3a125372..7b72d1c9c739 100644
--- a/sound/soc/sof/sof-client-ipc-flood-test.c
+++ b/sound/soc/sof/sof-client-ipc-flood-test.c
@@ -219,9 +219,10 @@ static ssize_t sof_ipc_flood_dfs_write(struct file *file, const char __user *buf
goto out;
}
- /* flood test */
- ret = sof_debug_ipc_flood_test(cdev, flood_duration_test,
- ipc_duration_ms, ipc_count);
+ ret = sof_client_boot_dsp(cdev);
+ if (!ret)
+ ret = sof_debug_ipc_flood_test(cdev, flood_duration_test,
+ ipc_duration_ms, ipc_count);
err = pm_runtime_put_autosuspend(dev);
if (err < 0)
diff --git a/sound/soc/sof/sof-client-ipc-kernel-injector.c b/sound/soc/sof/sof-client-ipc-kernel-injector.c
index 249bd2d6c8d2..d5984990098a 100644
--- a/sound/soc/sof/sof-client-ipc-kernel-injector.c
+++ b/sound/soc/sof/sof-client-ipc-kernel-injector.c
@@ -63,7 +63,9 @@ static ssize_t sof_kernel_msg_inject_dfs_write(struct file *file, const char __u
return ret;
}
- sof_client_ipc_rx_message(cdev, hdr, priv->kernel_buffer);
+ ret = sof_client_boot_dsp(cdev);
+ if (!ret)
+ sof_client_ipc_rx_message(cdev, hdr, priv->kernel_buffer);
ret = pm_runtime_put_autosuspend(dev);
if (ret < 0)
diff --git a/sound/soc/sof/sof-client-ipc-msg-injector.c b/sound/soc/sof/sof-client-ipc-msg-injector.c
index 9c8a0fbfb8df..c28f106de6ba 100644
--- a/sound/soc/sof/sof-client-ipc-msg-injector.c
+++ b/sound/soc/sof/sof-client-ipc-msg-injector.c
@@ -131,11 +131,15 @@ static int sof_msg_inject_send_message(struct sof_client_dev *cdev)
return ret;
}
- /* send the message */
- ret = sof_client_ipc_tx_message(cdev, priv->tx_buffer, priv->rx_buffer,
- priv->max_msg_size);
- if (ret)
- dev_err(dev, "IPC message send failed: %d\n", ret);
+ ret = sof_client_boot_dsp(cdev);
+ if (!ret) {
+ /* send the message */
+ ret = sof_client_ipc_tx_message(cdev, priv->tx_buffer,
+ priv->rx_buffer,
+ priv->max_msg_size);
+ if (ret)
+ dev_err(dev, "IPC message send failed: %d\n", ret);
+ }
err = pm_runtime_put_autosuspend(dev);
if (err < 0)
diff --git a/sound/soc/sof/sof-client-probes.c b/sound/soc/sof/sof-client-probes.c
index f753e0faff99..124f55508159 100644
--- a/sound/soc/sof/sof-client-probes.c
+++ b/sound/soc/sof/sof-client-probes.c
@@ -123,6 +123,10 @@ static int sof_probes_compr_set_params(struct snd_compr_stream *cstream,
if (ret)
return ret;
+ ret = sof_client_boot_dsp(cdev);
+ if (ret)
+ return ret;
+
ret = ipc->init(cdev, priv->extractor_stream_tag, rtd->dma_bytes);
if (ret < 0) {
dev_err(dai->dev, "Failed to init probe: %d\n", ret);
@@ -224,6 +228,10 @@ static ssize_t sof_probes_dfs_points_read(struct file *file, char __user *to,
goto exit;
}
+ ret = sof_client_boot_dsp(cdev);
+ if (ret)
+ goto pm_error;
+
ret = ipc->points_info(cdev, &desc, &num_desc, type);
if (ret < 0)
goto pm_error;
@@ -312,9 +320,12 @@ sof_probes_dfs_points_write(struct file *file, const char __user *from,
goto exit;
}
- ret = ipc->points_add(cdev, desc, bytes / sizeof(*desc));
- if (!ret)
- ret = count;
+ ret = sof_client_boot_dsp(cdev);
+ if (!ret) {
+ ret = ipc->points_add(cdev, desc, bytes / sizeof(*desc));
+ if (!ret)
+ ret = count;
+ }
err = pm_runtime_put_autosuspend(dev);
if (err < 0)
@@ -367,9 +378,12 @@ sof_probes_dfs_points_remove_write(struct file *file, const char __user *from,
goto exit;
}
- ret = ipc->points_remove(cdev, &array[1], array[0]);
- if (!ret)
- ret = count;
+ ret = sof_client_boot_dsp(cdev);
+ if (!ret) {
+ ret = ipc->points_remove(cdev, &array[1], array[0]);
+ if (!ret)
+ ret = count;
+ }
err = pm_runtime_put_autosuspend(dev);
if (err < 0)
diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c
index 2dbfc7699c73..38f1d7cec470 100644
--- a/sound/soc/sof/sof-client.c
+++ b/sound/soc/sof/sof-client.c
@@ -265,9 +265,8 @@ int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id,
}
/* add to list of SOF client devices */
- mutex_lock(&sdev->ipc_client_mutex);
- list_add(&centry->list, &sdev->ipc_client_list);
- mutex_unlock(&sdev->ipc_client_mutex);
+ scoped_guard(mutex, &sdev->ipc_client_mutex)
+ list_add(&centry->list, &sdev->ipc_client_list);
return 0;
@@ -285,7 +284,7 @@ void sof_client_dev_unregister(struct snd_sof_dev *sdev, const char *name, u32 i
{
struct sof_client_dev_entry *centry;
- mutex_lock(&sdev->ipc_client_mutex);
+ guard(mutex)(&sdev->ipc_client_mutex);
/*
* sof_client_auxdev_release() will be invoked to free up memory
@@ -301,8 +300,6 @@ void sof_client_dev_unregister(struct snd_sof_dev *sdev, const char *name, u32 i
break;
}
}
-
- mutex_unlock(&sdev->ipc_client_mutex);
}
EXPORT_SYMBOL_NS_GPL(sof_client_dev_unregister, "SND_SOC_SOF_CLIENT");
@@ -400,7 +397,7 @@ int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state)
const struct auxiliary_driver *adrv;
struct sof_client_dev_entry *centry;
- mutex_lock(&sdev->ipc_client_mutex);
+ guard(mutex)(&sdev->ipc_client_mutex);
list_for_each_entry(centry, &sdev->ipc_client_list, list) {
struct sof_client_dev *cdev = &centry->client_dev;
@@ -414,8 +411,6 @@ int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state)
adrv->suspend(&cdev->auxdev, state);
}
- mutex_unlock(&sdev->ipc_client_mutex);
-
return 0;
}
EXPORT_SYMBOL_NS_GPL(sof_suspend_clients, "SND_SOC_SOF_CLIENT");
@@ -425,7 +420,7 @@ int sof_resume_clients(struct snd_sof_dev *sdev)
const struct auxiliary_driver *adrv;
struct sof_client_dev_entry *centry;
- mutex_lock(&sdev->ipc_client_mutex);
+ guard(mutex)(&sdev->ipc_client_mutex);
list_for_each_entry(centry, &sdev->ipc_client_list, list) {
struct sof_client_dev *cdev = &centry->client_dev;
@@ -439,8 +434,6 @@ int sof_resume_clients(struct snd_sof_dev *sdev)
adrv->resume(&cdev->auxdev);
}
- mutex_unlock(&sdev->ipc_client_mutex);
-
return 0;
}
EXPORT_SYMBOL_NS_GPL(sof_resume_clients, "SND_SOC_SOF_CLIENT");
@@ -486,6 +479,12 @@ enum sof_ipc_type sof_client_get_ipc_type(struct sof_client_dev *cdev)
}
EXPORT_SYMBOL_NS_GPL(sof_client_get_ipc_type, "SND_SOC_SOF_CLIENT");
+int sof_client_boot_dsp(struct sof_client_dev *cdev)
+{
+ return snd_sof_boot_dsp_firmware(sof_client_dev_to_sof_dev(cdev));
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_boot_dsp, "SND_SOC_SOF_CLIENT");
+
/* module refcount management of SOF core */
int sof_client_core_module_get(struct sof_client_dev *cdev)
{
@@ -526,14 +525,11 @@ void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *msg_buf)
return;
}
- mutex_lock(&sdev->client_event_handler_mutex);
-
+ guard(mutex)(&sdev->client_event_handler_mutex);
list_for_each_entry(event, &sdev->ipc_rx_handler_list, list) {
if (event->ipc_msg_type == msg_type)
event->callback(event->cdev, msg_buf);
}
-
- mutex_unlock(&sdev->client_event_handler_mutex);
}
int sof_client_register_ipc_rx_handler(struct sof_client_dev *cdev,
@@ -567,9 +563,8 @@ int sof_client_register_ipc_rx_handler(struct sof_client_dev *cdev,
event->callback = callback;
/* add to list of SOF client devices */
- mutex_lock(&sdev->client_event_handler_mutex);
+ guard(mutex)(&sdev->client_event_handler_mutex);
list_add(&event->list, &sdev->ipc_rx_handler_list);
- mutex_unlock(&sdev->client_event_handler_mutex);
return 0;
}
@@ -581,7 +576,7 @@ void sof_client_unregister_ipc_rx_handler(struct sof_client_dev *cdev,
struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
struct sof_ipc_event_entry *event;
- mutex_lock(&sdev->client_event_handler_mutex);
+ guard(mutex)(&sdev->ipc_client_mutex);
list_for_each_entry(event, &sdev->ipc_rx_handler_list, list) {
if (event->cdev == cdev && event->ipc_msg_type == ipc_msg_type) {
@@ -590,8 +585,6 @@ void sof_client_unregister_ipc_rx_handler(struct sof_client_dev *cdev,
break;
}
}
-
- mutex_unlock(&sdev->client_event_handler_mutex);
}
EXPORT_SYMBOL_NS_GPL(sof_client_unregister_ipc_rx_handler, "SND_SOC_SOF_CLIENT");
@@ -600,12 +593,10 @@ void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev)
{
struct sof_state_event_entry *event;
- mutex_lock(&sdev->client_event_handler_mutex);
+ guard(mutex)(&sdev->ipc_client_mutex);
list_for_each_entry(event, &sdev->fw_state_handler_list, list)
event->callback(event->cdev, sdev->fw_state);
-
- mutex_unlock(&sdev->client_event_handler_mutex);
}
int sof_client_register_fw_state_handler(struct sof_client_dev *cdev,
@@ -625,9 +616,8 @@ int sof_client_register_fw_state_handler(struct sof_client_dev *cdev,
event->callback = callback;
/* add to list of SOF client devices */
- mutex_lock(&sdev->client_event_handler_mutex);
+ guard(mutex)(&sdev->client_event_handler_mutex);
list_add(&event->list, &sdev->fw_state_handler_list);
- mutex_unlock(&sdev->client_event_handler_mutex);
return 0;
}
@@ -638,7 +628,7 @@ void sof_client_unregister_fw_state_handler(struct sof_client_dev *cdev)
struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
struct sof_state_event_entry *event;
- mutex_lock(&sdev->client_event_handler_mutex);
+ guard(mutex)(&sdev->ipc_client_mutex);
list_for_each_entry(event, &sdev->fw_state_handler_list, list) {
if (event->cdev == cdev) {
@@ -647,8 +637,6 @@ void sof_client_unregister_fw_state_handler(struct sof_client_dev *cdev)
break;
}
}
-
- mutex_unlock(&sdev->client_event_handler_mutex);
}
EXPORT_SYMBOL_NS_GPL(sof_client_unregister_fw_state_handler, "SND_SOC_SOF_CLIENT");
diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h
index 1a9015e38474..3b02506c03f1 100644
--- a/sound/soc/sof/sof-client.h
+++ b/sound/soc/sof/sof-client.h
@@ -50,6 +50,9 @@ const struct sof_ipc_fw_version *sof_client_get_fw_version(struct sof_client_dev
size_t sof_client_get_ipc_max_payload_size(struct sof_client_dev *cdev);
enum sof_ipc_type sof_client_get_ipc_type(struct sof_client_dev *cdev);
+/* DSP/firmware boot request */
+int sof_client_boot_dsp(struct sof_client_dev *cdev);
+
/* module refcount management of SOF core */
int sof_client_core_module_get(struct sof_client_dev *cdev);
void sof_client_core_module_put(struct sof_client_dev *cdev);
diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h
index 0f624d8cde20..693d063830fa 100644
--- a/sound/soc/sof/sof-priv.h
+++ b/sound/soc/sof/sof-priv.h
@@ -580,6 +580,8 @@ struct snd_sof_dev {
wait_queue_head_t boot_wait;
enum sof_fw_state fw_state;
bool first_boot;
+ /* mutex to protect DSP firmware boot (except initial, probe time boot */
+ struct mutex dsp_fw_boot_mutex;
/* work queue in case the probe is implemented in two steps */
struct work_struct probe_work;
@@ -703,6 +705,7 @@ int snd_sof_suspend(struct device *dev);
int snd_sof_dsp_power_down_notify(struct snd_sof_dev *sdev);
int snd_sof_prepare(struct device *dev);
void snd_sof_complete(struct device *dev);
+int snd_sof_boot_dsp_firmware(struct snd_sof_dev *sdev);
void snd_sof_new_platform_drv(struct snd_sof_dev *sdev);
diff --git a/sound/soc/sophgo/Kconfig b/sound/soc/sophgo/Kconfig
new file mode 100644
index 000000000000..9b454261bcfd
--- /dev/null
+++ b/sound/soc/sophgo/Kconfig
@@ -0,0 +1,47 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# SoC audio configuration for cv1800b
+#
+
+menu "Sophgo"
+ depends on COMPILE_TEST || ARCH_SOPHGO
+
+config SND_SOC_CV1800B_TDM
+ tristate "Sophgo CV1800B I2S/TDM support"
+ depends on SND_SOC && OF
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ help
+ This option enables the I2S/TDM audio controller found in Sophgo
+ CV1800B / SG2002 SoCs. The controller supports standard I2S
+ audio modes for playback and capture.
+
+ The driver integrates with the ASoC framework and uses the DMA
+ engine for audio data transfer. It is intended to be configured
+ via Device Tree along with simple-audio-card module.
+
+ To compile the driver as a module, choose M here: the module will
+ be called cv1800b_tdm.
+
+config SND_SOC_CV1800B_ADC_CODEC
+ tristate "Sophgo CV1800B/SG2002 internal ADC codec"
+ depends on SND_SOC
+ help
+ This driver provides an ASoC codec DAI for capture and basic
+ control of the RXADC registers.
+
+ Say Y or M to build support for the Sophgo CV1800B
+ internal analog ADC codec block (RXADC).
+ The module will be called cv1800b-sound-adc
+
+config SND_SOC_CV1800B_DAC_CODEC
+ tristate "Sophgo CV1800B/SG2002 internal DAC codec"
+ depends on SND_SOC
+ help
+ This driver provides an ASoC codec DAI for playback and basic
+ control of the TXDAC registers.
+
+ Say Y or M to build support for the Sophgo CV1800B
+ internal analog DAC codec block (TXDAC).
+ The module will be called cv1800b-sound-dac
+
+endmenu
diff --git a/sound/soc/sophgo/Makefile b/sound/soc/sophgo/Makefile
new file mode 100644
index 000000000000..ec8dd31efddd
--- /dev/null
+++ b/sound/soc/sophgo/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+# Sophgo Platform Support
+obj-$(CONFIG_SND_SOC_CV1800B_TDM) += cv1800b-tdm.o
+obj-$(CONFIG_SND_SOC_CV1800B_ADC_CODEC) += cv1800b-sound-adc.o
+obj-$(CONFIG_SND_SOC_CV1800B_DAC_CODEC) += cv1800b-sound-dac.o
diff --git a/sound/soc/sophgo/cv1800b-sound-adc.c b/sound/soc/sophgo/cv1800b-sound-adc.c
new file mode 100644
index 000000000000..b66761156b99
--- /dev/null
+++ b/sound/soc/sophgo/cv1800b-sound-adc.c
@@ -0,0 +1,319 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Internal adc codec for cv1800b compatible SoC
+ *
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/soc.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <sound/tlv.h>
+#include <sound/soc-component.h>
+#include <sound/control.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/bitops.h>
+
+#define CV1800B_RXADC_WORD_LEN 16
+#define CV1800B_RXADC_CHANNELS 2
+
+#define CV1800B_RXADC_CTRL0 0x00
+#define CV1800B_RXADCC_CTRL1 0x04
+#define CV1800B_RXADC_STATUS 0x08
+#define CV1800B_RXADC_CLK 0x0c
+#define CV1800B_RXADC_ANA0 0x10
+#define CV1800B_RXADC_ANA1 0x14
+#define CV1800B_RXADC_ANA2 0x18
+#define CV1800B_RXADC_ANA3 0x1c
+#define CV1800B_RXADC_ANA4 0x20
+
+/* CV1800B_RXADC_CTRL0 */
+#define REG_RXADC_EN GENMASK(0, 0)
+#define REG_I2S_TX_EN GENMASK(1, 1)
+
+/* CV1800B_RXADCC_CTRL1 */
+#define REG_RXADC_CIC_OPT GENMASK(1, 0)
+#define REG_RXADC_IGR_INIT GENMASK(8, 8)
+
+/* CV1800B_RXADC_ANA0 */
+#define REG_GSTEPL_RXPGA GENMASK(12, 0)
+#define REG_G6DBL_RXPGA GENMASK(13, 13)
+#define REG_GAINL_RXADC GENMASK(15, 14)
+#define REG_GSTEPR_RXPGA GENMASK(28, 16)
+#define REG_G6DBR_RXPGA GENMASK(29, 29)
+#define REG_GAINR_RXADC GENMASK(31, 30)
+#define REG_COMB_LEFT_VOLUME GENMASK(15, 0)
+#define REG_COMB_RIGHT_VOLUME GENMASK(31, 16)
+
+/* CV1800B_RXADC_ANA2 */
+#define REG_MUTEL_RXPGA GENMASK(0, 0)
+#define REG_MUTER_RXPGA GENMASK(1, 1)
+
+/* CV1800B_RXADC_CLK */
+#define REG_RXADC_CLK_INV GENMASK(0, 0)
+#define REG_RXADC_SCK_DIV GENMASK(15, 8)
+#define REG_RXADC_DLYEN GENMASK(23, 16)
+
+enum decimation_values {
+ DECIMATION_64 = 0,
+ DECIMATION_128,
+ DECIMATION_256,
+ DECIMATION_512,
+};
+
+static const u32 cv1800b_gains[] = {
+ 0x0001, /* 0dB */
+ 0x0002, /* 2dB */
+ 0x0004, /* 4dB */
+ 0x0008, /* 6dB */
+ 0x0010, /* 8dB */
+ 0x0020, /* 10dB */
+ 0x0040, /* 12dB */
+ 0x0080, /* 14dB */
+ 0x0100, /* 16dB */
+ 0x0200, /* 18dB */
+ 0x0400, /* 20dB */
+ 0x0800, /* 22dB */
+ 0x1000, /* 24dB */
+ 0x2400, /* 26dB */
+ 0x2800, /* 28dB */
+ 0x3000, /* 30dB */
+ 0x6400, /* 32dB */
+ 0x6800, /* 34dB */
+ 0x7000, /* 36dB */
+ 0xA400, /* 38dB */
+ 0xA800, /* 40dB */
+ 0xB000, /* 42dB */
+ 0xE400, /* 44dB */
+ 0xE800, /* 46dB */
+ 0xF000, /* 48dB */
+};
+
+struct cv1800b_priv {
+ void __iomem *regs;
+ struct device *dev;
+ unsigned int mclk_rate;
+};
+
+static int cv1800b_adc_setbclk_div(struct cv1800b_priv *priv, unsigned int rate)
+{
+ u32 val;
+ u32 bclk_div;
+ u64 tmp;
+
+ if (!priv->mclk_rate || !rate)
+ return -EINVAL;
+
+ tmp = div_u64(priv->mclk_rate, CV1800B_RXADC_WORD_LEN *
+ CV1800B_RXADC_CHANNELS * rate * 2);
+
+ if (!tmp) {
+ dev_err(priv->dev, "computed BCLK divider is zero\n");
+ return -EINVAL;
+ }
+
+ if (tmp > 256) {
+ dev_err(priv->dev, "BCLK divider %llu out of range\n", tmp);
+ return -EINVAL;
+ }
+
+ bclk_div = tmp - 1;
+ val = readl(priv->regs + CV1800B_RXADC_CLK);
+ val = u32_replace_bits(val, bclk_div, REG_RXADC_SCK_DIV);
+ /* Vendor value for 48kHz, tested on SG2000/SG2002 */
+ val = u32_replace_bits(val, 0x19, REG_RXADC_DLYEN);
+ writel(val, priv->regs + CV1800B_RXADC_CLK);
+
+ return 0;
+}
+
+static void cv1800b_adc_enable(struct cv1800b_priv *priv, bool enable)
+{
+ u32 val;
+
+ val = readl(priv->regs + CV1800B_RXADC_CTRL0);
+ val = u32_replace_bits(val, enable, REG_RXADC_EN);
+ val = u32_replace_bits(val, enable, REG_I2S_TX_EN);
+ writel(val, priv->regs + CV1800B_RXADC_CTRL0);
+}
+
+static unsigned int cv1800b_adc_calc_db(u32 ana0, bool right)
+{
+ u32 step_mask = right ? FIELD_GET(REG_GSTEPR_RXPGA, ana0) :
+ FIELD_GET(REG_GSTEPL_RXPGA, ana0);
+ u32 coarse = right ? FIELD_GET(REG_GAINR_RXADC, ana0) :
+ FIELD_GET(REG_GAINL_RXADC, ana0);
+ bool g6db = right ? FIELD_GET(REG_G6DBR_RXPGA, ana0) :
+ FIELD_GET(REG_G6DBL_RXPGA, ana0);
+
+ u32 step = step_mask ? __ffs(step_mask) : 0;
+
+ step = min(step, 12U);
+ coarse = min(coarse, 3U);
+
+ return 2 * step + 6 * coarse + (g6db ? 6 : 0);
+}
+
+static int cv1800b_adc_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cv1800b_priv *priv = snd_soc_dai_get_drvdata(dai);
+ unsigned int rate = params_rate(params);
+ u32 val;
+ int ret;
+
+ ret = cv1800b_adc_setbclk_div(priv, rate);
+ if (ret) {
+ dev_err(priv->dev,
+ "could not set rate, check DT node for fixed clock\n");
+ return ret;
+ }
+
+ /* init adc */
+ val = readl(priv->regs + CV1800B_RXADCC_CTRL1);
+ val = u32_replace_bits(val, 1, REG_RXADC_IGR_INIT);
+ val = u32_replace_bits(val, DECIMATION_64, REG_RXADC_CIC_OPT);
+ writel(val, priv->regs + CV1800B_RXADCC_CTRL1);
+ return 0;
+}
+
+static int cv1800b_adc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct cv1800b_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ cv1800b_adc_enable(priv, true);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ cv1800b_adc_enable(priv, false);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cv1800b_adc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct cv1800b_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+ priv->mclk_rate = freq;
+ dev_dbg(priv->dev, "mclk is set to %u\n", freq);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cv1800b_adc_dai_ops = {
+ .hw_params = cv1800b_adc_hw_params,
+ .set_sysclk = cv1800b_adc_dai_set_sysclk,
+ .trigger = cv1800b_adc_dai_trigger,
+};
+
+static struct snd_soc_dai_driver cv1800b_adc_dai = {
+ .name = "adc-hifi",
+ .capture = { .stream_name = "ADC Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE },
+ .ops = &cv1800b_adc_dai_ops,
+};
+
+static int cv1800b_adc_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cv1800b_priv *priv = snd_soc_component_get_drvdata(component);
+ u32 ana0 = readl(priv->regs + CV1800B_RXADC_ANA0);
+
+ unsigned int left = cv1800b_adc_calc_db(ana0, false);
+ unsigned int right = cv1800b_adc_calc_db(ana0, true);
+
+ ucontrol->value.integer.value[0] = min(left / 2, 24U);
+ ucontrol->value.integer.value[1] = min(right / 2, 24U);
+ return 0;
+}
+
+static int cv1800b_adc_volume_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cv1800b_priv *priv = snd_soc_component_get_drvdata(component);
+
+ u32 v_left = clamp_t(u32, ucontrol->value.integer.value[0], 0, 24);
+ u32 v_right = clamp_t(u32, ucontrol->value.integer.value[1], 0, 24);
+ u32 val;
+
+ val = readl(priv->regs + CV1800B_RXADC_ANA0);
+ val = u32_replace_bits(val, cv1800b_gains[v_left],
+ REG_COMB_LEFT_VOLUME);
+ val = u32_replace_bits(val, cv1800b_gains[v_right],
+ REG_COMB_RIGHT_VOLUME);
+ writel(val, priv->regs + CV1800B_RXADC_ANA0);
+
+ return 0;
+}
+
+static DECLARE_TLV_DB_SCALE(cv1800b_volume_tlv, 0, 200, 0);
+
+static const struct snd_kcontrol_new cv1800b_adc_controls[] = {
+ SOC_DOUBLE_EXT_TLV("Internal I2S Capture Volume", SND_SOC_NOPM, 0, 16, 24, false,
+ cv1800b_adc_volume_get, cv1800b_adc_volume_set,
+ cv1800b_volume_tlv),
+};
+
+static const struct snd_soc_component_driver cv1800b_adc_component = {
+ .name = "cv1800b-adc-codec",
+ .controls = cv1800b_adc_controls,
+ .num_controls = ARRAY_SIZE(cv1800b_adc_controls),
+};
+
+static int cv1800b_adc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct cv1800b_priv *priv;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = dev;
+ priv->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->regs))
+ return PTR_ERR(priv->regs);
+
+ platform_set_drvdata(pdev, priv);
+ return devm_snd_soc_register_component(&pdev->dev,
+ &cv1800b_adc_component,
+ &cv1800b_adc_dai, 1);
+}
+
+static const struct of_device_id cv1800b_adc_of_match[] = {
+ { .compatible = "sophgo,cv1800b-sound-adc" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, cv1800b_adc_of_match);
+
+static struct platform_driver cv1800b_adc_driver = {
+ .probe = cv1800b_adc_probe,
+ .driver = {
+ .name = "cv1800b-sound-adc",
+ .of_match_table = cv1800b_adc_of_match,
+ },
+};
+
+module_platform_driver(cv1800b_adc_driver);
+
+MODULE_DESCRIPTION("ADC codec for CV1800B");
+MODULE_AUTHOR("Anton D. Stavinskii <stavinsky@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/sophgo/cv1800b-sound-dac.c b/sound/soc/sophgo/cv1800b-sound-dac.c
new file mode 100644
index 000000000000..135322bcf6ad
--- /dev/null
+++ b/sound/soc/sophgo/cv1800b-sound-dac.c
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Internal DAC codec for cv1800b based CPUs
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <sound/soc.h>
+#include <linux/io.h>
+
+#define CV1800B_TXDAC_CTRL0 0x00
+#define CV1800B_TXDAC_CTRL1 0x04
+#define CV1800B_TXDAC_STATUS 0x08
+#define CV1800B_TXDAC_AFE0 0x0c
+#define CV1800B_TXDAC_AFE1 0x10
+#define CV1800B_TXDAC_ANA0 0x20
+#define CV1800B_TXDAC_ANA1 0x24
+#define CV1800B_TXDAC_ANA2 0x28
+
+/* cv1800b_TXDAC_CTRL0 */
+#define REG_TXDAC_EN GENMASK(0, 0)
+#define REG_I2S_RX_EN GENMASK(1, 1)
+
+/* cv1800b_TXDAC_CTRL1 */
+#define REG_TXDAC_CIC_OPT GENMASK(1, 0)
+
+/* cv1800b_TXDAC_AFE0 */
+#define REG_TXDAC_INIT_DLY_CNT GENMASK(5, 0)
+
+/* cv1800b_TXDAC_ANA2 */
+#define TXDAC_OW_VAL_L_MASK GENMASK(7, 0)
+#define TXDAC_OW_VAL_R_MASK GENMASK(15, 8)
+#define TXDAC_OW_EN_L_MASK GENMASK(16, 16)
+#define TXDAC_OW_EN_R_MASK GENMASK(17, 17)
+
+struct cv1800b_priv {
+ void __iomem *regs;
+ struct device *dev;
+};
+
+enum decimation_values {
+ DECIMATION_64 = 0,
+ DECIMATION_128,
+ DECIMATION_256,
+ DECIMATION_512,
+};
+
+static void cv1800b_dac_enable(struct cv1800b_priv *priv, bool enable)
+{
+ u32 val;
+
+ val = readl(priv->regs + CV1800B_TXDAC_CTRL0);
+ val = u32_replace_bits(val, enable, REG_TXDAC_EN);
+ val = u32_replace_bits(val, enable, REG_I2S_RX_EN);
+ writel(val, priv->regs + CV1800B_TXDAC_CTRL0);
+}
+
+/*
+ * Control the DAC overwrite bits. When enabled, the DAC outputs the fixed
+ * overwrite value instead of samples from the I2S input.
+ */
+static void cv1800b_dac_mute(struct cv1800b_priv *priv, bool enable)
+{
+ u32 val;
+
+ val = readl(priv->regs + CV1800B_TXDAC_ANA2);
+ val = u32_replace_bits(val, enable, TXDAC_OW_EN_L_MASK);
+ val = u32_replace_bits(val, enable, TXDAC_OW_EN_R_MASK);
+ writel(val, priv->regs + CV1800B_TXDAC_ANA2);
+}
+
+static int cv1800b_dac_decimation(struct cv1800b_priv *priv, u8 dec)
+{
+ u32 val;
+
+ if (dec > 3)
+ return -EINVAL;
+
+ val = readl(priv->regs + CV1800B_TXDAC_CTRL1);
+ val = u32_replace_bits(val, dec, REG_TXDAC_CIC_OPT);
+ writel(val, priv->regs + CV1800B_TXDAC_CTRL1);
+ return 0;
+}
+
+static int cv1800b_dac_dly(struct cv1800b_priv *priv, u32 dly)
+{
+ u32 val;
+
+ if (dly > 63)
+ return -EINVAL;
+
+ val = readl(priv->regs + CV1800B_TXDAC_AFE0);
+ val = u32_replace_bits(val, dly, REG_TXDAC_INIT_DLY_CNT);
+ writel(val, priv->regs + CV1800B_TXDAC_AFE0);
+ return 0;
+}
+
+static int cv1800b_dac_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cv1800b_priv *priv = snd_soc_dai_get_drvdata(dai);
+ int ret;
+ unsigned int rate = params_rate(params);
+
+ if (rate != 48000) {
+ dev_err(priv->dev, "rate %u is not supported\n", rate);
+ return -EINVAL;
+ }
+ /* Clear DAC overwrite so playback uses I2S data. */
+ cv1800b_dac_mute(priv, false);
+ /* minimal decimation for 48kHz is 64*/
+ ret = cv1800b_dac_decimation(priv, DECIMATION_64);
+ if (ret)
+ return ret;
+
+ /* value is taken from vendors driver 48kHz
+ * tested on sg2000 and sg2002.
+ */
+ ret = cv1800b_dac_dly(priv, 0x19);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int cv1800b_dac_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct cv1800b_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ cv1800b_dac_enable(priv, true);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ cv1800b_dac_enable(priv, false);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cv1800b_dac_dai_ops = {
+ .hw_params = cv1800b_dac_hw_params,
+ .trigger = cv1800b_dac_dai_trigger,
+};
+
+static struct snd_soc_dai_driver cv1800b_dac_dai = {
+ .name = "dac-hifi",
+ .playback = { .stream_name = "DAC Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE },
+ .ops = &cv1800b_dac_dai_ops,
+};
+
+static const struct snd_soc_component_driver cv1800b_dac_component = {
+ .name = "cv1800b-dac-codec",
+};
+
+static int cv1800b_dac_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct cv1800b_priv *priv;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = dev;
+ priv->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->regs))
+ return PTR_ERR(priv->regs);
+
+ platform_set_drvdata(pdev, priv);
+ return devm_snd_soc_register_component(&pdev->dev,
+ &cv1800b_dac_component,
+ &cv1800b_dac_dai, 1);
+}
+
+static const struct of_device_id cv1800b_dac_of_match[] = {
+ { .compatible = "sophgo,cv1800b-sound-dac" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, cv1800b_dac_of_match);
+
+static struct platform_driver cv1800b_dac_driver = {
+ .probe = cv1800b_dac_probe,
+ .driver = {
+ .name = "cv1800b-dac-codec",
+ .of_match_table = cv1800b_dac_of_match,
+ },
+};
+module_platform_driver(cv1800b_dac_driver);
+
+MODULE_DESCRIPTION("DAC codec for CV1800B");
+MODULE_AUTHOR("Anton D. Stavinskii <stavinsky@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/sophgo/cv1800b-tdm.c b/sound/soc/sophgo/cv1800b-tdm.c
new file mode 100644
index 000000000000..4cbac8c1160f
--- /dev/null
+++ b/sound/soc/sophgo/cv1800b-tdm.c
@@ -0,0 +1,716 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/string.h>
+#include <linux/dev_printk.h>
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/limits.h>
+#include <linux/overflow.h>
+
+#define TX_FIFO_SIZE (1024)
+#define RX_FIFO_SIZE (1024)
+#define TX_MAX_BURST (8)
+#define RX_MAX_BURST (8)
+
+#define CV1800B_DEF_FREQ 24576000
+#define CV1800B_DEF_MCLK_FS_RATIO 256
+
+/* tdm registers */
+#define CV1800B_BLK_MODE_SETTING 0x000
+#define CV1800B_FRAME_SETTING 0x004
+#define CV1800B_SLOT_SETTING1 0x008
+#define CV1800B_SLOT_SETTING2 0x00C
+#define CV1800B_DATA_FORMAT 0x010
+#define CV1800B_BLK_CFG 0x014
+#define CV1800B_I2S_ENABLE 0x018
+#define CV1800B_I2S_RESET 0x01C
+#define CV1800B_I2S_INT_EN 0x020
+#define CV1800B_I2S_INT 0x024
+#define CV1800B_FIFO_THRESHOLD 0x028
+#define CV1800B_LRCK_MASTER 0x02C /* special clock only mode */
+#define CV1800B_FIFO_RESET 0x030
+#define CV1800B_RX_STATUS 0x040
+#define CV1800B_TX_STATUS 0x048
+#define CV1800B_CLK_CTRL0 0x060
+#define CV1800B_CLK_CTRL1 0x064
+#define CV1800B_PCM_SYNTH 0x068
+#define CV1800B_RX_RD_PORT 0x080
+#define CV1800B_TX_WR_PORT 0x0C0
+
+/* CV1800B_BLK_MODE_SETTING (0x000) */
+#define BLK_TX_MODE_MASK GENMASK(0, 0)
+#define BLK_MASTER_MODE_MASK GENMASK(1, 1)
+#define BLK_DMA_MODE_MASK GENMASK(7, 7)
+
+/* CV1800B_CLK_CTRL1 (0x064) */
+#define CLK_MCLK_DIV_MASK GENMASK(15, 0)
+#define CLK_BCLK_DIV_MASK GENMASK(31, 16)
+
+/* CV1800B_CLK_CTRL0 (0x060) */
+#define CLK_AUD_CLK_SEL_MASK GENMASK(0, 0)
+#define CLK_BCLK_OUT_CLK_FORCE_EN_MASK GENMASK(6, 6)
+#define CLK_MCLK_OUT_EN_MASK GENMASK(7, 7)
+#define CLK_AUD_EN_MASK GENMASK(8, 8)
+
+/* CV1800B_I2S_RESET (0x01C) */
+#define RST_I2S_RESET_RX_MASK GENMASK(0, 0)
+#define RST_I2S_RESET_TX_MASK GENMASK(1, 1)
+
+/* CV1800B_FIFO_RESET (0x030) */
+#define FIFO_RX_RESET_MASK GENMASK(0, 0)
+#define FIFO_TX_RESET_MASK GENMASK(16, 16)
+
+/* CV1800B_I2S_ENABLE (0x018) */
+#define I2S_ENABLE_MASK GENMASK(0, 0)
+
+/* CV1800B_BLK_CFG (0x014) */
+#define BLK_AUTO_DISABLE_WITH_CH_EN_MASK GENMASK(4, 4)
+#define BLK_RX_BLK_CLK_FORCE_EN_MASK GENMASK(8, 8)
+#define BLK_RX_FIFO_DMA_CLK_FORCE_EN_MASK GENMASK(9, 9)
+#define BLK_TX_BLK_CLK_FORCE_EN_MASK GENMASK(16, 16)
+#define BLK_TX_FIFO_DMA_CLK_FORCE_EN_MASK GENMASK(17, 17)
+
+/* CV1800B_FRAME_SETTING (0x004) */
+#define FRAME_LENGTH_MASK GENMASK(8, 0)
+#define FS_ACTIVE_LENGTH_MASK GENMASK(23, 16)
+
+/* CV1800B_I2S_INT_EN (0x020) */
+#define INT_I2S_INT_EN_MASK GENMASK(8, 8)
+
+/* CV1800B_SLOT_SETTING2 (0x00C) */
+#define SLOT_EN_MASK GENMASK(15, 0)
+
+/* CV1800B_LRCK_MASTER (0x02C) */
+#define LRCK_MASTER_ENABLE_MASK GENMASK(0, 0)
+
+/* CV1800B_DATA_FORMAT (0x010) */
+#define DF_WORD_LENGTH_MASK GENMASK(2, 1)
+#define DF_TX_SOURCE_LEFT_ALIGN_MASK GENMASK(6, 6)
+
+/* CV1800B_FIFO_THRESHOLD (0x028) */
+#define FIFO_RX_THRESHOLD_MASK GENMASK(4, 0)
+#define FIFO_TX_THRESHOLD_MASK GENMASK(20, 16)
+#define FIFO_TX_HIGH_THRESHOLD_MASK GENMASK(28, 24)
+
+/* CV1800B_SLOT_SETTING1 (0x008) */
+#define SLOT_NUM_MASK GENMASK(3, 0)
+#define SLOT_SIZE_MASK GENMASK(13, 8)
+#define DATA_SIZE_MASK GENMASK(20, 16)
+#define FB_OFFSET_MASK GENMASK(28, 24)
+
+enum cv1800b_tdm_word_length {
+ CV1800B_WORD_LENGTH_8_BIT = 0,
+ CV1800B_WORD_LENGTH_16_BIT = 1,
+ CV1800B_WORD_LENGTH_32_BIT = 2,
+};
+
+struct cv1800b_i2s {
+ void __iomem *base;
+ struct clk *clk;
+ struct clk *sysclk;
+ struct device *dev;
+ struct snd_dmaengine_dai_dma_data playback_dma;
+ struct snd_dmaengine_dai_dma_data capture_dma;
+ u32 mclk_rate;
+ bool bclk_ratio_fixed;
+ u32 bclk_ratio;
+
+};
+
+static void cv1800b_setup_dma_struct(struct cv1800b_i2s *i2s,
+ phys_addr_t phys_base)
+{
+ i2s->playback_dma.addr = phys_base + CV1800B_TX_WR_PORT;
+ i2s->playback_dma.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ i2s->playback_dma.fifo_size = TX_FIFO_SIZE;
+ i2s->playback_dma.maxburst = TX_MAX_BURST;
+
+ i2s->capture_dma.addr = phys_base + CV1800B_RX_RD_PORT;
+ i2s->capture_dma.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ i2s->capture_dma.fifo_size = RX_FIFO_SIZE;
+ i2s->capture_dma.maxburst = RX_MAX_BURST;
+}
+
+static const struct snd_dmaengine_pcm_config cv1800b_i2s_pcm_config = {
+ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+};
+
+static void cv1800b_reset_fifo(struct cv1800b_i2s *i2s)
+{
+ u32 val;
+
+ val = readl(i2s->base + CV1800B_FIFO_RESET);
+ val = u32_replace_bits(val, 1, FIFO_RX_RESET_MASK);
+ val = u32_replace_bits(val, 1, FIFO_TX_RESET_MASK);
+ writel(val, i2s->base + CV1800B_FIFO_RESET);
+
+ usleep_range(10, 20);
+
+ val = readl(i2s->base + CV1800B_FIFO_RESET);
+ val = u32_replace_bits(val, 0, FIFO_RX_RESET_MASK);
+ val = u32_replace_bits(val, 0, FIFO_TX_RESET_MASK);
+ writel(val, i2s->base + CV1800B_FIFO_RESET);
+}
+
+static void cv1800b_reset_i2s(struct cv1800b_i2s *i2s)
+{
+ u32 val;
+
+ val = readl(i2s->base + CV1800B_I2S_RESET);
+ val = u32_replace_bits(val, 1, RST_I2S_RESET_RX_MASK);
+ val = u32_replace_bits(val, 1, RST_I2S_RESET_TX_MASK);
+ writel(val, i2s->base + CV1800B_I2S_RESET);
+
+ usleep_range(10, 20);
+
+ val = readl(i2s->base + CV1800B_I2S_RESET);
+ val = u32_replace_bits(val, 0, RST_I2S_RESET_RX_MASK);
+ val = u32_replace_bits(val, 0, RST_I2S_RESET_TX_MASK);
+ writel(val, i2s->base + CV1800B_I2S_RESET);
+}
+
+static void cv1800b_set_mclk_div(struct cv1800b_i2s *i2s, u32 mclk_div)
+{
+ u32 val;
+
+ val = readl(i2s->base + CV1800B_CLK_CTRL1);
+ val = u32_replace_bits(val, mclk_div, CLK_MCLK_DIV_MASK);
+ writel(val, i2s->base + CV1800B_CLK_CTRL1);
+ dev_dbg(i2s->dev, "mclk_div is set to %u\n", mclk_div);
+}
+
+static void cv1800b_set_tx_mode(struct cv1800b_i2s *i2s, bool is_tx)
+{
+ u32 val;
+
+ val = readl(i2s->base + CV1800B_BLK_MODE_SETTING);
+ val = u32_replace_bits(val, is_tx, BLK_TX_MODE_MASK);
+ writel(val, i2s->base + CV1800B_BLK_MODE_SETTING);
+ dev_dbg(i2s->dev, "tx_mode is set to %u\n", is_tx);
+}
+
+static int cv1800b_set_bclk_div(struct cv1800b_i2s *i2s, u32 bclk_div)
+{
+ u32 val;
+
+ if (bclk_div == 0 || bclk_div > 0xFFFF)
+ return -EINVAL;
+
+ val = readl(i2s->base + CV1800B_CLK_CTRL1);
+ val = u32_replace_bits(val, bclk_div, CLK_BCLK_DIV_MASK);
+ writel(val, i2s->base + CV1800B_CLK_CTRL1);
+ dev_dbg(i2s->dev, "bclk_div is set to %u\n", bclk_div);
+ return 0;
+}
+
+/* set memory width of audio data , reg word_length */
+static int cv1800b_set_word_length(struct cv1800b_i2s *i2s,
+ unsigned int physical_width)
+{
+ u8 word_length_val;
+ u32 val;
+
+ switch (physical_width) {
+ case 8:
+ word_length_val = CV1800B_WORD_LENGTH_8_BIT;
+ break;
+ case 16:
+ word_length_val = CV1800B_WORD_LENGTH_16_BIT;
+ break;
+ case 32:
+ word_length_val = CV1800B_WORD_LENGTH_32_BIT;
+ break;
+ default:
+ dev_dbg(i2s->dev, "can't set word_length field\n");
+ return -EINVAL;
+ }
+
+ val = readl(i2s->base + CV1800B_DATA_FORMAT);
+ val = u32_replace_bits(val, word_length_val, DF_WORD_LENGTH_MASK);
+ writel(val, i2s->base + CV1800B_DATA_FORMAT);
+ return 0;
+}
+
+static void cv1800b_enable_clocks(struct cv1800b_i2s *i2s, bool enabled)
+{
+ u32 val;
+
+ val = readl(i2s->base + CV1800B_CLK_CTRL0);
+ val = u32_replace_bits(val, enabled, CLK_AUD_EN_MASK);
+ writel(val, i2s->base + CV1800B_CLK_CTRL0);
+}
+
+static int cv1800b_set_slot_settings(struct cv1800b_i2s *i2s, u32 slots,
+ u32 physical_width, u32 data_size)
+{
+ u32 slot_num;
+ u32 slot_size;
+ u32 frame_length;
+ u32 frame_active_length;
+ u32 val;
+
+ if (!slots || !physical_width || !data_size) {
+ dev_err(i2s->dev, "frame or slot settings are not valid\n");
+ return -EINVAL;
+ }
+ if (slots > 16 || physical_width > 64 || data_size > 32) {
+ dev_err(i2s->dev, "frame or slot settings are not valid\n");
+ return -EINVAL;
+ }
+
+ slot_num = slots - 1;
+ slot_size = physical_width - 1;
+ frame_length = (physical_width * slots) - 1;
+ frame_active_length = physical_width - 1;
+
+ if (frame_length > 511 || frame_active_length > 255) {
+ dev_err(i2s->dev, "frame or slot settings are not valid\n");
+ return -EINVAL;
+ }
+
+ val = readl(i2s->base + CV1800B_SLOT_SETTING1);
+ val = u32_replace_bits(val, slot_size, SLOT_SIZE_MASK);
+ val = u32_replace_bits(val, data_size - 1, DATA_SIZE_MASK);
+ val = u32_replace_bits(val, slot_num, SLOT_NUM_MASK);
+ writel(val, i2s->base + CV1800B_SLOT_SETTING1);
+
+ val = readl(i2s->base + CV1800B_FRAME_SETTING);
+ val = u32_replace_bits(val, frame_length, FRAME_LENGTH_MASK);
+ val = u32_replace_bits(val, frame_active_length, FS_ACTIVE_LENGTH_MASK);
+ writel(val, i2s->base + CV1800B_FRAME_SETTING);
+
+ dev_dbg(i2s->dev, "slot settings num: %u width: %u\n", slots, physical_width);
+ return 0;
+}
+
+/*
+ * calculate mclk_div.
+ * if requested value is bigger than optimal
+ * leave mclk_div as 1. cff clock is capable
+ * to handle it
+ */
+static int cv1800b_calc_mclk_div(unsigned int target_mclk, u32 *mclk_div)
+{
+ *mclk_div = 1;
+
+ if (target_mclk == 0)
+ return -EINVAL;
+
+ /* optimal parent frequency is close to CV1800B_DEF_FREQ */
+ if (target_mclk < CV1800B_DEF_FREQ) {
+ *mclk_div = DIV_ROUND_CLOSEST(CV1800B_DEF_FREQ, target_mclk);
+ if (!*mclk_div || *mclk_div > 0xFFFF)
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * set CCF clock and divider for this clock
+ * mclk_clock = ccf_clock / mclk_div
+ */
+static int cv1800b_i2s_set_rate_for_mclk(struct cv1800b_i2s *i2s,
+ unsigned int target_mclk)
+{
+ u32 mclk_div = 1;
+ u64 tmp;
+ int ret;
+ unsigned long clk_rate;
+ unsigned long actual;
+
+ ret = cv1800b_calc_mclk_div(target_mclk, &mclk_div);
+ if (ret) {
+ dev_dbg(i2s->dev, "can't calc mclk_div for freq %u\n",
+ target_mclk);
+ return ret;
+ }
+
+ tmp = (u64)target_mclk * mclk_div;
+ if (tmp > ULONG_MAX) {
+ dev_err(i2s->dev, "clk_rate overflow: freq=%u div=%u\n",
+ target_mclk, mclk_div);
+ return -ERANGE;
+ }
+
+ clk_rate = (unsigned long)tmp;
+
+ cv1800b_enable_clocks(i2s, false);
+
+ ret = clk_set_rate(i2s->sysclk, clk_rate);
+ if (ret)
+ return ret;
+
+ actual = clk_get_rate(i2s->sysclk);
+ if (clk_rate != actual) {
+ dev_err_ratelimited(i2s->dev,
+ "clk_set_rate failed %lu, actual is %lu\n",
+ clk_rate, actual);
+ }
+
+ cv1800b_set_mclk_div(i2s, mclk_div);
+ cv1800b_enable_clocks(i2s, true);
+
+ return 0;
+}
+
+static int cv1800b_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cv1800b_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ unsigned int rate = params_rate(params);
+ unsigned int channels = params_channels(params);
+ unsigned int physical_width = params_physical_width(params);
+ int data_width = params_width(params);
+ bool tx_mode = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 1 : 0;
+ int ret;
+ u32 bclk_div;
+ u32 bclk_ratio;
+ u32 mclk_rate;
+ u32 tmp;
+
+ if (data_width < 0)
+ return data_width;
+
+ if (!channels || !rate || !physical_width)
+ return -EINVAL;
+
+ ret = cv1800b_set_slot_settings(i2s, channels, physical_width, data_width);
+ if (ret)
+ return ret;
+
+ if (i2s->mclk_rate) {
+ mclk_rate = i2s->mclk_rate;
+ } else {
+ dev_dbg(i2s->dev, "mclk is not set by machine driver\n");
+ ret = cv1800b_i2s_set_rate_for_mclk(i2s,
+ rate * CV1800B_DEF_MCLK_FS_RATIO);
+ if (ret)
+ return ret;
+ mclk_rate = rate * CV1800B_DEF_MCLK_FS_RATIO;
+ }
+
+ bclk_ratio = (i2s->bclk_ratio_fixed) ? i2s->bclk_ratio :
+ (physical_width * channels);
+
+ if (check_mul_overflow(rate, bclk_ratio, &tmp))
+ return -EOVERFLOW;
+
+ if (!tmp)
+ return -EINVAL;
+ if (mclk_rate % tmp)
+ dev_warn(i2s->dev, "mclk rate is not aligned to bclk or rate\n");
+
+ bclk_div = DIV_ROUND_CLOSEST(mclk_rate, tmp);
+
+ ret = cv1800b_set_bclk_div(i2s, bclk_div);
+ if (ret)
+ return ret;
+
+ ret = cv1800b_set_word_length(i2s, physical_width);
+ if (ret)
+ return ret;
+
+ cv1800b_set_tx_mode(i2s, tx_mode);
+
+ cv1800b_reset_fifo(i2s);
+ cv1800b_reset_i2s(i2s);
+ return 0;
+}
+
+static int cv1800b_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct cv1800b_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ u32 val;
+
+ val = readl(i2s->base + CV1800B_I2S_ENABLE);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ val = u32_replace_bits(val, 1, I2S_ENABLE_MASK);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ val = u32_replace_bits(val, 0, I2S_ENABLE_MASK);
+ break;
+ default:
+ return -EINVAL;
+ }
+ writel(val, i2s->base + CV1800B_I2S_ENABLE);
+ return 0;
+}
+
+static int cv1800b_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct cv1800b_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
+
+ dev_dbg(i2s->dev, "%s: dai=%s substream=%d\n", __func__, dai->name,
+ substream->stream);
+ /**
+ * Ensure DMA is stopped before DAI
+ * shutdown (prevents DW AXI DMAC stop/busy on next open).
+ */
+ dai_link->trigger_stop = SND_SOC_TRIGGER_ORDER_LDC;
+ return 0;
+}
+
+static int cv1800b_i2s_dai_probe(struct snd_soc_dai *dai)
+{
+ struct cv1800b_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+ if (!i2s) {
+ dev_err(dai->dev, "no drvdata in DAI probe\n");
+ return -ENODEV;
+ }
+
+ snd_soc_dai_init_dma_data(dai, &i2s->playback_dma, &i2s->capture_dma);
+ return 0;
+}
+
+static int cv1800b_i2s_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct cv1800b_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ u32 val;
+ u32 master;
+
+ /* only i2s format is supported */
+ if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_I2S)
+ return -EINVAL;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ dev_dbg(i2s->dev, "set to master mode\n");
+ master = 1;
+ break;
+
+ case SND_SOC_DAIFMT_CBC_CFC:
+ dev_dbg(i2s->dev, "set to slave mode\n");
+ master = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val = readl(i2s->base + CV1800B_BLK_MODE_SETTING);
+ val = u32_replace_bits(val, master, BLK_MASTER_MODE_MASK);
+ writel(val, i2s->base + CV1800B_BLK_MODE_SETTING);
+ return 0;
+}
+
+static int cv1800b_i2s_dai_set_bclk_ratio(struct snd_soc_dai *dai,
+ unsigned int ratio)
+{
+ struct cv1800b_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+ if (ratio == 0)
+ return -EINVAL;
+ i2s->bclk_ratio = ratio;
+ i2s->bclk_ratio_fixed = true;
+ return 0;
+}
+
+static int cv1800b_i2s_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct cv1800b_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ int ret;
+ u32 val;
+ bool output_enable = (dir == SND_SOC_CLOCK_OUT) ? true : false;
+
+ dev_dbg(i2s->dev, "%s called with %u\n", __func__, freq);
+ ret = cv1800b_i2s_set_rate_for_mclk(i2s, freq);
+ if (ret)
+ return ret;
+
+ val = readl(i2s->base + CV1800B_CLK_CTRL0);
+ val = u32_replace_bits(val, output_enable, CLK_MCLK_OUT_EN_MASK);
+ writel(val, i2s->base + CV1800B_CLK_CTRL0);
+
+ i2s->mclk_rate = freq;
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cv1800b_i2s_dai_ops = {
+ .probe = cv1800b_i2s_dai_probe,
+ .startup = cv1800b_i2s_startup,
+ .hw_params = cv1800b_i2s_hw_params,
+ .trigger = cv1800b_i2s_trigger,
+ .set_fmt = cv1800b_i2s_dai_set_fmt,
+ .set_bclk_ratio = cv1800b_i2s_dai_set_bclk_ratio,
+ .set_sysclk = cv1800b_i2s_dai_set_sysclk,
+};
+
+static const struct snd_soc_dai_driver cv1800b_i2s_dai_template = {
+ .name = "cv1800b-i2s",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &cv1800b_i2s_dai_ops,
+};
+
+static const struct snd_soc_component_driver cv1800b_i2s_component = {
+ .name = "cv1800b-i2s",
+};
+
+static void cv1800b_i2s_hw_disable(struct cv1800b_i2s *i2s)
+{
+ u32 val;
+
+ val = readl(i2s->base + CV1800B_I2S_ENABLE);
+ val = u32_replace_bits(val, 0, I2S_ENABLE_MASK);
+ writel(val, i2s->base + CV1800B_I2S_ENABLE);
+
+ val = readl(i2s->base + CV1800B_CLK_CTRL0);
+ val = u32_replace_bits(val, 0, CLK_AUD_EN_MASK);
+ val = u32_replace_bits(val, 0, CLK_MCLK_OUT_EN_MASK);
+ writel(val, i2s->base + CV1800B_CLK_CTRL0);
+
+ val = readl(i2s->base + CV1800B_I2S_RESET);
+ val = u32_replace_bits(val, 1, RST_I2S_RESET_RX_MASK);
+ val = u32_replace_bits(val, 1, RST_I2S_RESET_TX_MASK);
+ writel(val, i2s->base + CV1800B_I2S_RESET);
+
+ val = readl(i2s->base + CV1800B_FIFO_RESET);
+ val = u32_replace_bits(val, 1, FIFO_RX_RESET_MASK);
+ val = u32_replace_bits(val, 1, FIFO_TX_RESET_MASK);
+ writel(val, i2s->base + CV1800B_FIFO_RESET);
+}
+
+static void cv1800b_i2s_setup_tdm(struct cv1800b_i2s *i2s)
+{
+ u32 val;
+
+ val = readl(i2s->base + CV1800B_BLK_MODE_SETTING);
+ val = u32_replace_bits(val, 1, BLK_DMA_MODE_MASK);
+ writel(val, i2s->base + CV1800B_BLK_MODE_SETTING);
+
+ val = readl(i2s->base + CV1800B_CLK_CTRL0);
+ val = u32_replace_bits(val, 0, CLK_AUD_CLK_SEL_MASK);
+ val = u32_replace_bits(val, 0, CLK_MCLK_OUT_EN_MASK);
+ val = u32_replace_bits(val, 0, CLK_AUD_EN_MASK);
+ writel(val, i2s->base + CV1800B_CLK_CTRL0);
+
+ val = readl(i2s->base + CV1800B_FIFO_THRESHOLD);
+ val = u32_replace_bits(val, 4, FIFO_RX_THRESHOLD_MASK);
+ val = u32_replace_bits(val, 4, FIFO_TX_THRESHOLD_MASK);
+ val = u32_replace_bits(val, 4, FIFO_TX_HIGH_THRESHOLD_MASK);
+ writel(val, i2s->base + CV1800B_FIFO_THRESHOLD);
+
+ val = readl(i2s->base + CV1800B_I2S_ENABLE);
+ val = u32_replace_bits(val, 0, I2S_ENABLE_MASK);
+ writel(val, i2s->base + CV1800B_I2S_ENABLE);
+}
+
+static int cv1800b_i2s_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct cv1800b_i2s *i2s;
+ struct resource *res;
+ void __iomem *regs;
+ struct snd_soc_dai_driver *dai;
+ int ret;
+
+ i2s = devm_kzalloc(dev, sizeof(*i2s), GFP_KERNEL);
+ if (!i2s)
+ return -ENOMEM;
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+ i2s->dev = &pdev->dev;
+ i2s->base = regs;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+ cv1800b_setup_dma_struct(i2s, res->start);
+
+ i2s->clk = devm_clk_get_enabled(dev, "i2s");
+ if (IS_ERR(i2s->clk))
+ return dev_err_probe(dev, PTR_ERR(i2s->clk),
+ "failed to get+enable i2s\n");
+ i2s->sysclk = devm_clk_get_enabled(dev, "mclk");
+ if (IS_ERR(i2s->sysclk))
+ return dev_err_probe(dev, PTR_ERR(i2s->sysclk),
+ "failed to get+enable mclk\n");
+
+ platform_set_drvdata(pdev, i2s);
+ cv1800b_i2s_setup_tdm(i2s);
+
+ dai = devm_kmemdup(dev, &cv1800b_i2s_dai_template, sizeof(*dai),
+ GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ ret = devm_snd_soc_register_component(dev, &cv1800b_i2s_component, dai,
+ 1);
+ if (ret)
+ return ret;
+
+ ret = devm_snd_dmaengine_pcm_register(dev, &cv1800b_i2s_pcm_config, 0);
+ if (ret) {
+ dev_err(dev, "dmaengine_pcm_register failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void cv1800b_i2s_remove(struct platform_device *pdev)
+{
+ struct cv1800b_i2s *i2s = platform_get_drvdata(pdev);
+
+ if (!i2s)
+ return;
+ cv1800b_i2s_hw_disable(i2s);
+}
+
+static const struct of_device_id cv1800b_i2s_of_match[] = {
+ { .compatible = "sophgo,cv1800b-i2s" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, cv1800b_i2s_of_match);
+
+static struct platform_driver cv1800b_i2s_driver = {
+ .probe = cv1800b_i2s_probe,
+ .remove = cv1800b_i2s_remove,
+ .driver = {
+ .name = "cv1800b-i2s",
+ .of_match_table = cv1800b_i2s_of_match,
+ },
+};
+module_platform_driver(cv1800b_i2s_driver);
+
+MODULE_DESCRIPTION("Sophgo cv1800b I2S/TDM driver");
+MODULE_AUTHOR("Anton D. Stavinsky <stavinsky@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c
index 1e755a716c63..65de03ca3ad2 100644
--- a/sound/soc/sunxi/sun4i-spdif.c
+++ b/sound/soc/sunxi/sun4i-spdif.c
@@ -684,6 +684,10 @@ static int sun4i_spdif_probe(struct platform_device *pdev)
host->regmap = devm_regmap_init_mmio(&pdev->dev, base,
&sun4i_spdif_regmap_config);
+ if (IS_ERR(host->regmap)) {
+ dev_err(&pdev->dev, "failed to initialise regmap.\n");
+ return PTR_ERR(host->regmap);
+ }
/* Clocks */
host->apb_clk = devm_clk_get(&pdev->dev, "apb");
diff --git a/sound/soc/sunxi/sun50i-dmic.c b/sound/soc/sunxi/sun50i-dmic.c
index bab1e29c9988..eddfebe16616 100644
--- a/sound/soc/sunxi/sun50i-dmic.c
+++ b/sound/soc/sunxi/sun50i-dmic.c
@@ -358,6 +358,9 @@ static int sun50i_dmic_probe(struct platform_device *pdev)
host->regmap = devm_regmap_init_mmio(&pdev->dev, base,
&sun50i_dmic_regmap_config);
+ if (IS_ERR(host->regmap))
+ return dev_err_probe(&pdev->dev, PTR_ERR(host->regmap),
+ "failed to initialise regmap\n");
/* Clocks */
host->bus_clk = devm_clk_get(&pdev->dev, "bus");
diff --git a/sound/soc/tegra/tegra186_asrc.c b/sound/soc/tegra/tegra186_asrc.c
index 2c0220e14a57..d2a5ec7c54cc 100644
--- a/sound/soc/tegra/tegra186_asrc.c
+++ b/sound/soc/tegra/tegra186_asrc.c
@@ -950,6 +950,7 @@ static const struct regmap_config tegra186_asrc_regmap_config = {
.volatile_reg = tegra186_asrc_volatile_reg,
.reg_defaults = tegra186_asrc_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra186_asrc_reg_defaults),
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
diff --git a/sound/soc/tegra/tegra186_dspk.c b/sound/soc/tegra/tegra186_dspk.c
index a762150db802..8816e4967331 100644
--- a/sound/soc/tegra/tegra186_dspk.c
+++ b/sound/soc/tegra/tegra186_dspk.c
@@ -467,6 +467,7 @@ static const struct regmap_config tegra186_dspk_regmap = {
.volatile_reg = tegra186_dspk_volatile_reg,
.reg_defaults = tegra186_dspk_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra186_dspk_reg_defaults),
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
diff --git a/sound/soc/tegra/tegra210_admaif.c b/sound/soc/tegra/tegra210_admaif.c
index f9f6040c4e34..0976779d29f2 100644
--- a/sound/soc/tegra/tegra210_admaif.c
+++ b/sound/soc/tegra/tegra210_admaif.c
@@ -241,6 +241,7 @@ static const struct regmap_config tegra210_admaif_regmap_config = {
.volatile_reg = tegra_admaif_volatile_reg,
.reg_defaults = tegra210_admaif_reg_defaults,
.num_reg_defaults = TEGRA210_ADMAIF_CHANNEL_COUNT * 6 + 1,
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
@@ -254,6 +255,7 @@ static const struct regmap_config tegra186_admaif_regmap_config = {
.volatile_reg = tegra_admaif_volatile_reg,
.reg_defaults = tegra186_admaif_reg_defaults,
.num_reg_defaults = TEGRA186_ADMAIF_CHANNEL_COUNT * 6 + 1,
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
@@ -267,6 +269,7 @@ static const struct regmap_config tegra264_admaif_regmap_config = {
.volatile_reg = tegra_admaif_volatile_reg,
.reg_defaults = tegra264_admaif_reg_defaults,
.num_reg_defaults = TEGRA264_ADMAIF_CHANNEL_COUNT * 6 + 1,
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
diff --git a/sound/soc/tegra/tegra210_adx.c b/sound/soc/tegra/tegra210_adx.c
index 6c9a410085bc..95875c75ddf8 100644
--- a/sound/soc/tegra/tegra210_adx.c
+++ b/sound/soc/tegra/tegra210_adx.c
@@ -625,6 +625,7 @@ static const struct regmap_config tegra210_adx_regmap_config = {
.volatile_reg = tegra210_adx_volatile_reg,
.reg_defaults = tegra210_adx_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra210_adx_reg_defaults),
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
@@ -638,6 +639,7 @@ static const struct regmap_config tegra264_adx_regmap_config = {
.volatile_reg = tegra264_adx_volatile_reg,
.reg_defaults = tegra264_adx_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra264_adx_reg_defaults),
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
diff --git a/sound/soc/tegra/tegra210_ahub.c b/sound/soc/tegra/tegra210_ahub.c
index e795907a3963..43a45f785d5b 100644
--- a/sound/soc/tegra/tegra210_ahub.c
+++ b/sound/soc/tegra/tegra210_ahub.c
@@ -2049,6 +2049,61 @@ static const struct snd_soc_component_driver tegra264_ahub_component = {
.num_dapm_routes = ARRAY_SIZE(tegra264_ahub_routes),
};
+static bool tegra210_ahub_wr_reg(struct device *dev, unsigned int reg)
+{
+ int part;
+
+ if (reg % TEGRA210_XBAR_RX_STRIDE)
+ return false;
+
+ for (part = 0; part < TEGRA210_XBAR_UPDATE_MAX_REG; part++) {
+ switch (reg & ~(part * TEGRA210_XBAR_PART1_RX)) {
+ case TEGRA210_AXBAR_PART_0_ADMAIF_RX1_0 ... TEGRA210_AXBAR_PART_0_ADMAIF_RX10_0:
+ case TEGRA210_AXBAR_PART_0_I2S1_RX1_0 ... TEGRA210_AXBAR_PART_0_I2S5_RX1_0:
+ case TEGRA210_AXBAR_PART_0_SFC1_RX1_0 ... TEGRA210_AXBAR_PART_0_SFC4_RX1_0:
+ case TEGRA210_AXBAR_PART_0_MIXER1_RX1_0 ... TEGRA210_AXBAR_PART_0_MIXER1_RX10_0:
+ case TEGRA210_AXBAR_PART_0_SPDIF1_RX1_0 ... TEGRA210_AXBAR_PART_0_SPDIF1_RX2_0:
+ case TEGRA210_AXBAR_PART_0_AFC1_RX1_0 ... TEGRA210_AXBAR_PART_0_AFC6_RX1_0:
+ case TEGRA210_AXBAR_PART_0_OPE1_RX1_0 ... TEGRA210_AXBAR_PART_0_OPE2_RX1_0:
+ case TEGRA210_AXBAR_PART_0_SPKPROT1_RX1_0:
+ case TEGRA210_AXBAR_PART_0_MVC1_RX1_0 ... TEGRA210_AXBAR_PART_0_MVC2_RX1_0:
+ case TEGRA210_AXBAR_PART_0_AMX1_RX1_0 ... TEGRA210_AXBAR_PART_0_ADX2_RX1_0:
+ return true;
+ default:
+ break;
+ }
+ }
+
+ return false;
+}
+
+static bool tegra186_ahub_wr_reg(struct device *dev, unsigned int reg)
+{
+ int part;
+
+ if (reg % TEGRA210_XBAR_RX_STRIDE)
+ return false;
+
+ for (part = 0; part < TEGRA186_XBAR_UPDATE_MAX_REG; part++) {
+ switch (reg & ~(part * TEGRA210_XBAR_PART1_RX)) {
+ case TEGRA210_AXBAR_PART_0_ADMAIF_RX1_0 ... TEGRA186_AXBAR_PART_0_I2S6_RX1_0:
+ case TEGRA210_AXBAR_PART_0_SFC1_RX1_0 ... TEGRA210_AXBAR_PART_0_SFC4_RX1_0:
+ case TEGRA210_AXBAR_PART_0_MIXER1_RX1_0 ... TEGRA210_AXBAR_PART_0_MIXER1_RX10_0:
+ case TEGRA186_AXBAR_PART_0_DSPK1_RX1_0 ... TEGRA186_AXBAR_PART_0_DSPK2_RX1_0:
+ case TEGRA210_AXBAR_PART_0_AFC1_RX1_0 ... TEGRA210_AXBAR_PART_0_AFC6_RX1_0:
+ case TEGRA210_AXBAR_PART_0_OPE1_RX1_0:
+ case TEGRA186_AXBAR_PART_0_MVC1_RX1_0 ... TEGRA186_AXBAR_PART_0_MVC2_RX1_0:
+ case TEGRA186_AXBAR_PART_0_AMX1_RX1_0 ... TEGRA186_AXBAR_PART_0_AMX3_RX4_0:
+ case TEGRA210_AXBAR_PART_0_ADX1_RX1_0 ... TEGRA186_AXBAR_PART_0_ASRC1_RX7_0:
+ return true;
+ default:
+ break;
+ }
+ }
+
+ return false;
+}
+
static bool tegra264_ahub_wr_reg(struct device *dev, unsigned int reg)
{
int part;
@@ -2076,7 +2131,9 @@ static const struct regmap_config tegra210_ahub_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
+ .writeable_reg = tegra210_ahub_wr_reg,
.max_register = TEGRA210_MAX_REGISTER_ADDR,
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
@@ -2084,7 +2141,9 @@ static const struct regmap_config tegra186_ahub_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
+ .writeable_reg = tegra186_ahub_wr_reg,
.max_register = TEGRA186_MAX_REGISTER_ADDR,
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
@@ -2094,6 +2153,7 @@ static const struct regmap_config tegra264_ahub_regmap_config = {
.reg_stride = 4,
.writeable_reg = tegra264_ahub_wr_reg,
.max_register = TEGRA264_MAX_REGISTER_ADDR,
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
diff --git a/sound/soc/tegra/tegra210_ahub.h b/sound/soc/tegra/tegra210_ahub.h
index f355b2cfd19b..acbe640dd3b5 100644
--- a/sound/soc/tegra/tegra210_ahub.h
+++ b/sound/soc/tegra/tegra210_ahub.h
@@ -68,6 +68,36 @@
#define TEGRA210_MAX_REGISTER_ADDR (TEGRA210_XBAR_PART2_RX + \
(TEGRA210_XBAR_RX_STRIDE * (TEGRA210_XBAR_AUDIO_RX_COUNT - 1)))
+/* AXBAR register offsets */
+#define TEGRA186_AXBAR_PART_0_AMX1_RX1_0 0x120
+#define TEGRA186_AXBAR_PART_0_AMX3_RX4_0 0x14c
+#define TEGRA186_AXBAR_PART_0_ASRC1_RX7_0 0x1a8
+#define TEGRA186_AXBAR_PART_0_DSPK1_RX1_0 0xc0
+#define TEGRA186_AXBAR_PART_0_DSPK2_RX1_0 0xc4
+#define TEGRA186_AXBAR_PART_0_I2S6_RX1_0 0x54
+#define TEGRA186_AXBAR_PART_0_MVC1_RX1_0 0x110
+#define TEGRA186_AXBAR_PART_0_MVC2_RX1_0 0x114
+#define TEGRA210_AXBAR_PART_0_ADMAIF_RX10_0 0x24
+#define TEGRA210_AXBAR_PART_0_ADMAIF_RX1_0 0x0
+#define TEGRA210_AXBAR_PART_0_ADX1_RX1_0 0x160
+#define TEGRA210_AXBAR_PART_0_ADX2_RX1_0 0x164
+#define TEGRA210_AXBAR_PART_0_AFC1_RX1_0 0xd0
+#define TEGRA210_AXBAR_PART_0_AFC6_RX1_0 0xe4
+#define TEGRA210_AXBAR_PART_0_AMX1_RX1_0 0x140
+#define TEGRA210_AXBAR_PART_0_I2S1_RX1_0 0x40
+#define TEGRA210_AXBAR_PART_0_I2S5_RX1_0 0x50
+#define TEGRA210_AXBAR_PART_0_MIXER1_RX10_0 0xa4
+#define TEGRA210_AXBAR_PART_0_MIXER1_RX1_0 0x80
+#define TEGRA210_AXBAR_PART_0_MVC1_RX1_0 0x120
+#define TEGRA210_AXBAR_PART_0_MVC2_RX1_0 0x124
+#define TEGRA210_AXBAR_PART_0_OPE1_RX1_0 0x100
+#define TEGRA210_AXBAR_PART_0_OPE2_RX1_0 0x104
+#define TEGRA210_AXBAR_PART_0_SFC1_RX1_0 0x60
+#define TEGRA210_AXBAR_PART_0_SFC4_RX1_0 0x6c
+#define TEGRA210_AXBAR_PART_0_SPDIF1_RX1_0 0xc0
+#define TEGRA210_AXBAR_PART_0_SPDIF1_RX2_0 0xc4
+#define TEGRA210_AXBAR_PART_0_SPKPROT1_RX1_0 0x110
+
#define MUX_REG(id) (TEGRA210_XBAR_RX_STRIDE * (id))
#define MUX_VALUE(npart, nbit) (1 + (nbit) + (npart) * 32)
diff --git a/sound/soc/tegra/tegra210_amx.c b/sound/soc/tegra/tegra210_amx.c
index c94f8c84e04f..bfda82505298 100644
--- a/sound/soc/tegra/tegra210_amx.c
+++ b/sound/soc/tegra/tegra210_amx.c
@@ -654,6 +654,7 @@ static const struct regmap_config tegra210_amx_regmap_config = {
.volatile_reg = tegra210_amx_volatile_reg,
.reg_defaults = tegra210_amx_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra210_amx_reg_defaults),
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
@@ -667,6 +668,7 @@ static const struct regmap_config tegra194_amx_regmap_config = {
.volatile_reg = tegra210_amx_volatile_reg,
.reg_defaults = tegra210_amx_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra210_amx_reg_defaults),
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
@@ -680,6 +682,7 @@ static const struct regmap_config tegra264_amx_regmap_config = {
.volatile_reg = tegra264_amx_volatile_reg,
.reg_defaults = tegra264_amx_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra264_amx_reg_defaults),
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
diff --git a/sound/soc/tegra/tegra210_dmic.c b/sound/soc/tegra/tegra210_dmic.c
index 66fff53aeaa6..93def7ac4fde 100644
--- a/sound/soc/tegra/tegra210_dmic.c
+++ b/sound/soc/tegra/tegra210_dmic.c
@@ -483,6 +483,7 @@ static const struct regmap_config tegra210_dmic_regmap_config = {
.volatile_reg = tegra210_dmic_volatile_reg,
.reg_defaults = tegra210_dmic_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra210_dmic_reg_defaults),
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
diff --git a/sound/soc/tegra/tegra210_i2s.c b/sound/soc/tegra/tegra210_i2s.c
index b91e0e6cd7fe..d8e02f0a3025 100644
--- a/sound/soc/tegra/tegra210_i2s.c
+++ b/sound/soc/tegra/tegra210_i2s.c
@@ -997,6 +997,7 @@ static const struct regmap_config tegra210_regmap_conf = {
.volatile_reg = tegra210_i2s_volatile_reg,
.reg_defaults = tegra210_i2s_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra210_i2s_reg_defaults),
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
@@ -1044,6 +1045,7 @@ static const struct regmap_config tegra264_regmap_conf = {
.volatile_reg = tegra264_i2s_volatile_reg,
.reg_defaults = tegra264_i2s_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra264_i2s_reg_defaults),
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
diff --git a/sound/soc/tegra/tegra210_mbdrc.c b/sound/soc/tegra/tegra210_mbdrc.c
index 09fe3c5cf540..6a268dbb7197 100644
--- a/sound/soc/tegra/tegra210_mbdrc.c
+++ b/sound/soc/tegra/tegra210_mbdrc.c
@@ -763,6 +763,7 @@ static const struct regmap_config tegra210_mbdrc_regmap_cfg = {
.precious_reg = tegra210_mbdrc_precious_reg,
.reg_defaults = tegra210_mbdrc_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra210_mbdrc_reg_defaults),
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
diff --git a/sound/soc/tegra/tegra210_mixer.c b/sound/soc/tegra/tegra210_mixer.c
index ff8e9f2d7abf..6d3a2b76fd61 100644
--- a/sound/soc/tegra/tegra210_mixer.c
+++ b/sound/soc/tegra/tegra210_mixer.c
@@ -608,6 +608,7 @@ static const struct regmap_config tegra210_mixer_regmap_config = {
.precious_reg = tegra210_mixer_precious_reg,
.reg_defaults = tegra210_mixer_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra210_mixer_reg_defaults),
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
diff --git a/sound/soc/tegra/tegra210_mvc.c b/sound/soc/tegra/tegra210_mvc.c
index 779d4c199da9..6cdc5e1f5507 100644
--- a/sound/soc/tegra/tegra210_mvc.c
+++ b/sound/soc/tegra/tegra210_mvc.c
@@ -699,6 +699,7 @@ static const struct regmap_config tegra210_mvc_regmap_config = {
.volatile_reg = tegra210_mvc_volatile_reg,
.reg_defaults = tegra210_mvc_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra210_mvc_reg_defaults),
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
diff --git a/sound/soc/tegra/tegra210_ope.c b/sound/soc/tegra/tegra210_ope.c
index 27db70af2746..a440888dcdbd 100644
--- a/sound/soc/tegra/tegra210_ope.c
+++ b/sound/soc/tegra/tegra210_ope.c
@@ -297,6 +297,7 @@ static const struct regmap_config tegra210_ope_regmap_config = {
.volatile_reg = tegra210_ope_volatile_reg,
.reg_defaults = tegra210_ope_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra210_ope_reg_defaults),
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
diff --git a/sound/soc/tegra/tegra210_peq.c b/sound/soc/tegra/tegra210_peq.c
index 9a05e6913276..2f72e9d541dc 100644
--- a/sound/soc/tegra/tegra210_peq.c
+++ b/sound/soc/tegra/tegra210_peq.c
@@ -306,6 +306,7 @@ static const struct regmap_config tegra210_peq_regmap_config = {
.precious_reg = tegra210_peq_precious_reg,
.reg_defaults = tegra210_peq_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra210_peq_reg_defaults),
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
diff --git a/sound/soc/tegra/tegra210_sfc.c b/sound/soc/tegra/tegra210_sfc.c
index d6341968bebe..b298bf0421b1 100644
--- a/sound/soc/tegra/tegra210_sfc.c
+++ b/sound/soc/tegra/tegra210_sfc.c
@@ -3569,6 +3569,7 @@ static const struct regmap_config tegra210_sfc_regmap_config = {
.precious_reg = tegra210_sfc_precious_reg,
.reg_defaults = tegra210_sfc_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra210_sfc_reg_defaults),
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c
index 621a9d5f9377..2d260fbc9b83 100644
--- a/sound/soc/ti/davinci-mcasp.c
+++ b/sound/soc/ti/davinci-mcasp.c
@@ -25,6 +25,7 @@
#include <linux/math64.h>
#include <linux/bitmap.h>
#include <linux/gpio/driver.h>
+#include <linux/property.h>
#include <sound/asoundef.h>
#include <sound/core.h>
@@ -70,6 +71,7 @@ struct davinci_mcasp_context {
struct davinci_mcasp_ruledata {
struct davinci_mcasp *mcasp;
int serializers;
+ int stream;
};
struct davinci_mcasp {
@@ -87,21 +89,27 @@ struct davinci_mcasp {
bool missing_audio_param;
/* McASP specific data */
- int tdm_slots;
+ int tdm_slots_tx;
+ int tdm_slots_rx;
u32 tdm_mask[2];
- int slot_width;
+ int slot_width_tx;
+ int slot_width_rx;
u8 op_mode;
u8 dismod;
u8 num_serializer;
u8 *serial_dir;
u8 version;
- u8 bclk_div;
+ u8 bclk_div_tx;
+ u8 bclk_div_rx;
int streams;
u32 irq_request[2];
- int sysclk_freq;
+ unsigned int sysclk_freq_tx;
+ unsigned int sysclk_freq_rx;
bool bclk_master;
- u32 auxclk_fs_ratio;
+ bool async_mode;
+ u32 auxclk_fs_ratio_tx;
+ u32 auxclk_fs_ratio_rx;
unsigned long pdir; /* Pin direction bitfield */
@@ -179,10 +187,16 @@ static void mcasp_set_ctl_reg(struct davinci_mcasp *mcasp, u32 ctl_reg, u32 val)
static bool mcasp_is_synchronous(struct davinci_mcasp *mcasp)
{
- u32 rxfmctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG);
u32 aclkxctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG);
- return !(aclkxctl & TX_ASYNC) && rxfmctl & AFSRE;
+ return !(aclkxctl & TX_ASYNC);
+}
+
+static bool mcasp_is_frame_producer(struct davinci_mcasp *mcasp)
+{
+ u32 rxfmctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG);
+
+ return rxfmctl & AFSRE;
}
static inline void mcasp_set_clk_pdir(struct davinci_mcasp *mcasp, bool enable)
@@ -197,6 +211,27 @@ static inline void mcasp_set_clk_pdir(struct davinci_mcasp *mcasp, bool enable)
}
}
+static inline void mcasp_set_clk_pdir_stream(struct davinci_mcasp *mcasp,
+ int stream, bool enable)
+{
+ u32 bit, bit_end;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ bit = PIN_BIT_ACLKX;
+ bit_end = PIN_BIT_AFSX + 1;
+ } else {
+ bit = PIN_BIT_ACLKR;
+ bit_end = PIN_BIT_AFSR + 1;
+ }
+
+ for_each_set_bit_from(bit, &mcasp->pdir, bit_end) {
+ if (enable)
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit));
+ else
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit));
+ }
+}
+
static inline void mcasp_set_axr_pdir(struct davinci_mcasp *mcasp, bool enable)
{
u32 bit;
@@ -209,6 +244,36 @@ static inline void mcasp_set_axr_pdir(struct davinci_mcasp *mcasp, bool enable)
}
}
+static inline int mcasp_get_tdm_slots(struct davinci_mcasp *mcasp, int stream)
+{
+ return (stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ mcasp->tdm_slots_tx : mcasp->tdm_slots_rx;
+}
+
+static inline int mcasp_get_slot_width(struct davinci_mcasp *mcasp, int stream)
+{
+ return (stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ mcasp->slot_width_tx : mcasp->slot_width_rx;
+}
+
+static inline unsigned int mcasp_get_sysclk_freq(struct davinci_mcasp *mcasp, int stream)
+{
+ return (stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ mcasp->sysclk_freq_tx : mcasp->sysclk_freq_rx;
+}
+
+static inline unsigned int mcasp_get_bclk_div(struct davinci_mcasp *mcasp, int stream)
+{
+ return (stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ mcasp->bclk_div_tx : mcasp->bclk_div_rx;
+}
+
+static inline unsigned int mcasp_get_auxclk_fs_ratio(struct davinci_mcasp *mcasp, int stream)
+{
+ return (stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ mcasp->auxclk_fs_ratio_tx : mcasp->auxclk_fs_ratio_rx;
+}
+
static void mcasp_start_rx(struct davinci_mcasp *mcasp)
{
if (mcasp->rxnumevt) { /* enable FIFO */
@@ -224,13 +289,17 @@ static void mcasp_start_rx(struct davinci_mcasp *mcasp)
/*
* When ASYNC == 0 the transmit and receive sections operate
* synchronously from the transmit clock and frame sync. We need to make
- * sure that the TX signlas are enabled when starting reception.
+ * sure that the TX signals are enabled when starting reception,
+ * when the McASP is the producer.
*/
- if (mcasp_is_synchronous(mcasp)) {
+ if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp)) {
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST);
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST);
- mcasp_set_clk_pdir(mcasp, true);
}
+ if (mcasp_is_synchronous(mcasp))
+ mcasp_set_clk_pdir(mcasp, true);
+ else
+ mcasp_set_clk_pdir_stream(mcasp, SNDRV_PCM_STREAM_CAPTURE, true);
/* Activate serializer(s) */
mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF);
@@ -239,7 +308,7 @@ static void mcasp_start_rx(struct davinci_mcasp *mcasp)
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST);
/* Release Frame Sync generator */
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXFSRST);
- if (mcasp_is_synchronous(mcasp))
+ if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp))
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
/* enable receive IRQs */
@@ -261,7 +330,10 @@ static void mcasp_start_tx(struct davinci_mcasp *mcasp)
/* Start clocks */
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST);
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST);
- mcasp_set_clk_pdir(mcasp, true);
+ if (mcasp_is_synchronous(mcasp))
+ mcasp_set_clk_pdir(mcasp, true);
+ else
+ mcasp_set_clk_pdir_stream(mcasp, SNDRV_PCM_STREAM_PLAYBACK, true);
/* Activate serializer(s) */
mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
@@ -304,11 +376,19 @@ static void mcasp_stop_rx(struct davinci_mcasp *mcasp)
/*
* In synchronous mode stop the TX clocks if no other stream is
* running
+ * Otherwise in async mode only stop RX clocks
*/
- if (mcasp_is_synchronous(mcasp) && !mcasp->streams) {
+ if (mcasp_is_synchronous(mcasp) && !mcasp->streams)
mcasp_set_clk_pdir(mcasp, false);
+ else if (!mcasp_is_synchronous(mcasp))
+ mcasp_set_clk_pdir_stream(mcasp, SNDRV_PCM_STREAM_CAPTURE, false);
+ /*
+ * When McASP is the producer and operating in synchronous mode,
+ * stop the transmit clocks if no other stream is running. As
+ * tx & rx operate synchronously from the transmit clock.
+ */
+ if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) && !mcasp->streams)
mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, 0);
- }
mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, 0);
mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF);
@@ -331,11 +411,14 @@ static void mcasp_stop_tx(struct davinci_mcasp *mcasp)
/*
* In synchronous mode keep TX clocks running if the capture stream is
* still running.
+ * Otherwise in async mode only stop TX clocks
*/
- if (mcasp_is_synchronous(mcasp) && mcasp->streams)
+ if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) && mcasp->streams)
val = TXHCLKRST | TXCLKRST | TXFSRST;
- else
+ if (mcasp_is_synchronous(mcasp) && !mcasp->streams)
mcasp_set_clk_pdir(mcasp, false);
+ else if (!mcasp_is_synchronous(mcasp))
+ mcasp_set_clk_pdir_stream(mcasp, SNDRV_PCM_STREAM_PLAYBACK, false);
mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, val);
@@ -619,13 +702,39 @@ static int __davinci_mcasp_set_clkdiv(struct davinci_mcasp *mcasp, int div_id,
AHCLKRDIV(div - 1), AHCLKRDIV_MASK);
break;
+ case MCASP_CLKDIV_AUXCLK_TXONLY: /* MCLK divider for TX only */
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG,
+ AHCLKXDIV(div - 1), AHCLKXDIV_MASK);
+ break;
+
+ case MCASP_CLKDIV_AUXCLK_RXONLY: /* MCLK divider for RX only */
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG,
+ AHCLKRDIV(div - 1), AHCLKRDIV_MASK);
+ break;
+
case MCASP_CLKDIV_BCLK: /* BCLK divider */
mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG,
ACLKXDIV(div - 1), ACLKXDIV_MASK);
mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG,
ACLKRDIV(div - 1), ACLKRDIV_MASK);
+ if (explicit) {
+ mcasp->bclk_div_tx = div;
+ mcasp->bclk_div_rx = div;
+ }
+ break;
+
+ case MCASP_CLKDIV_BCLK_TXONLY: /* BCLK divider for TX only */
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG,
+ ACLKXDIV(div - 1), ACLKXDIV_MASK);
if (explicit)
- mcasp->bclk_div = div;
+ mcasp->bclk_div_tx = div;
+ break;
+
+ case MCASP_CLKDIV_BCLK_RXONLY: /* BCLK divider for RX only */
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG,
+ ACLKRDIV(div - 1), ACLKRDIV_MASK);
+ if (explicit)
+ mcasp->bclk_div_rx = div;
break;
case MCASP_CLKDIV_BCLK_FS_RATIO:
@@ -639,11 +748,33 @@ static int __davinci_mcasp_set_clkdiv(struct davinci_mcasp *mcasp, int div_id,
* tdm_slot width by dividing the ratio by the
* number of configured tdm slots.
*/
- mcasp->slot_width = div / mcasp->tdm_slots;
- if (div % mcasp->tdm_slots)
+ mcasp->slot_width_tx = div / mcasp->tdm_slots_tx;
+ if (div % mcasp->tdm_slots_tx)
+ dev_warn(mcasp->dev,
+ "%s(): BCLK/LRCLK %d is not divisible by %d tx tdm slots",
+ __func__, div, mcasp->tdm_slots_tx);
+
+ mcasp->slot_width_rx = div / mcasp->tdm_slots_rx;
+ if (div % mcasp->tdm_slots_rx)
dev_warn(mcasp->dev,
- "%s(): BCLK/LRCLK %d is not divisible by %d tdm slots",
- __func__, div, mcasp->tdm_slots);
+ "%s(): BCLK/LRCLK %d is not divisible by %d rx tdm slots",
+ __func__, div, mcasp->tdm_slots_rx);
+ break;
+
+ case MCASP_CLKDIV_BCLK_FS_RATIO_TXONLY:
+ mcasp->slot_width_tx = div / mcasp->tdm_slots_tx;
+ if (div % mcasp->tdm_slots_tx)
+ dev_warn(mcasp->dev,
+ "%s(): BCLK/LRCLK %d is not divisible by %d tx tdm slots",
+ __func__, div, mcasp->tdm_slots_tx);
+ break;
+
+ case MCASP_CLKDIV_BCLK_FS_RATIO_RXONLY:
+ mcasp->slot_width_rx = div / mcasp->tdm_slots_rx;
+ if (div % mcasp->tdm_slots_rx)
+ dev_warn(mcasp->dev,
+ "%s(): BCLK/LRCLK %d is not divisible by %d rx tdm slots",
+ __func__, div, mcasp->tdm_slots_rx);
break;
default:
@@ -677,6 +808,20 @@ static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id,
mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG,
AHCLKRE);
clear_bit(PIN_BIT_AHCLKX, &mcasp->pdir);
+ mcasp->sysclk_freq_tx = freq;
+ mcasp->sysclk_freq_rx = freq;
+ break;
+ case MCASP_CLK_HCLK_AHCLK_TXONLY:
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG,
+ AHCLKXE);
+ clear_bit(PIN_BIT_AHCLKX, &mcasp->pdir);
+ mcasp->sysclk_freq_tx = freq;
+ break;
+ case MCASP_CLK_HCLK_AHCLK_RXONLY:
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG,
+ AHCLKRE);
+ clear_bit(PIN_BIT_AHCLKR, &mcasp->pdir);
+ mcasp->sysclk_freq_rx = freq;
break;
case MCASP_CLK_HCLK_AUXCLK:
mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG,
@@ -684,22 +829,56 @@ static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id,
mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG,
AHCLKRE);
set_bit(PIN_BIT_AHCLKX, &mcasp->pdir);
+ mcasp->sysclk_freq_tx = freq;
+ mcasp->sysclk_freq_rx = freq;
+ break;
+ case MCASP_CLK_HCLK_AUXCLK_TXONLY:
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG,
+ AHCLKXE);
+ set_bit(PIN_BIT_AHCLKX, &mcasp->pdir);
+ mcasp->sysclk_freq_tx = freq;
+ break;
+ case MCASP_CLK_HCLK_AUXCLK_RXONLY:
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG,
+ AHCLKRE);
+ set_bit(PIN_BIT_AHCLKR, &mcasp->pdir);
+ mcasp->sysclk_freq_rx = freq;
break;
default:
dev_err(mcasp->dev, "Invalid clk id: %d\n", clk_id);
goto out;
}
} else {
- /* Select AUXCLK as HCLK */
- mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE);
- mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE);
- set_bit(PIN_BIT_AHCLKX, &mcasp->pdir);
+ /* McASP is clock master, select AUXCLK as HCLK */
+ switch (clk_id) {
+ case MCASP_CLK_HCLK_AUXCLK_TXONLY:
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG,
+ AHCLKXE);
+ set_bit(PIN_BIT_AHCLKX, &mcasp->pdir);
+ mcasp->sysclk_freq_tx = freq;
+ break;
+ case MCASP_CLK_HCLK_AUXCLK_RXONLY:
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG,
+ AHCLKRE);
+ set_bit(PIN_BIT_AHCLKR, &mcasp->pdir);
+ mcasp->sysclk_freq_rx = freq;
+ break;
+ default:
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG,
+ AHCLKXE);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG,
+ AHCLKRE);
+ set_bit(PIN_BIT_AHCLKX, &mcasp->pdir);
+ set_bit(PIN_BIT_AHCLKR, &mcasp->pdir);
+ mcasp->sysclk_freq_tx = freq;
+ mcasp->sysclk_freq_rx = freq;
+ break;
+ }
}
/*
* When AHCLK X/R is selected to be output it means that the HCLK is
* the same clock - coming via AUXCLK.
*/
- mcasp->sysclk_freq = freq;
out:
pm_runtime_put(mcasp->dev);
return 0;
@@ -711,9 +890,11 @@ static int davinci_mcasp_ch_constraint(struct davinci_mcasp *mcasp, int stream,
{
struct snd_pcm_hw_constraint_list *cl = &mcasp->chconstr[stream];
unsigned int *list = (unsigned int *) cl->list;
- int slots = mcasp->tdm_slots;
+ int slots;
int i, count = 0;
+ slots = mcasp_get_tdm_slots(mcasp, stream);
+
if (mcasp->tdm_mask[stream])
slots = hweight32(mcasp->tdm_mask[stream]);
@@ -778,27 +959,42 @@ static int davinci_mcasp_set_tdm_slot(struct snd_soc_dai *dai,
return -EINVAL;
}
- mcasp->tdm_slots = slots;
+ if (mcasp->async_mode) {
+ if (tx_mask) {
+ mcasp->tdm_slots_tx = slots;
+ mcasp->slot_width_tx = slot_width;
+ }
+ if (rx_mask) {
+ mcasp->tdm_slots_rx = slots;
+ mcasp->slot_width_rx = slot_width;
+ }
+ } else {
+ mcasp->tdm_slots_tx = slots;
+ mcasp->tdm_slots_rx = slots;
+ mcasp->slot_width_tx = slot_width;
+ mcasp->slot_width_rx = slot_width;
+ }
+
mcasp->tdm_mask[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask;
mcasp->tdm_mask[SNDRV_PCM_STREAM_CAPTURE] = rx_mask;
- mcasp->slot_width = slot_width;
return davinci_mcasp_set_ch_constraints(mcasp);
}
static int davinci_config_channel_size(struct davinci_mcasp *mcasp,
- int sample_width)
+ int sample_width, int stream)
{
u32 fmt;
u32 tx_rotate, rx_rotate, slot_width;
u32 mask = (1ULL << sample_width) - 1;
- if (mcasp->slot_width)
- slot_width = mcasp->slot_width;
- else if (mcasp->max_format_width)
- slot_width = mcasp->max_format_width;
- else
- slot_width = sample_width;
+ slot_width = mcasp_get_slot_width(mcasp, stream);
+ if (!slot_width) {
+ if (mcasp->max_format_width)
+ slot_width = mcasp->max_format_width;
+ else
+ slot_width = sample_width;
+ }
/*
* TX rotation:
* right aligned formats: rotate w/ slot_width
@@ -821,17 +1017,23 @@ static int davinci_config_channel_size(struct davinci_mcasp *mcasp,
fmt = (slot_width >> 1) - 1;
if (mcasp->op_mode != DAVINCI_MCASP_DIT_MODE) {
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXSSZ(fmt),
- RXSSZ(0x0F));
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXSSZ(fmt),
- TXSSZ(0x0F));
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXROT(tx_rotate),
- TXROT(7));
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXROT(rx_rotate),
- RXROT(7));
- mcasp_set_reg(mcasp, DAVINCI_MCASP_RXMASK_REG, mask);
+ if (!mcasp->async_mode || stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXSSZ(fmt),
+ TXSSZ(0x0F));
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXROT(tx_rotate),
+ TXROT(7));
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXMASK_REG, mask);
+ }
+ if (!mcasp->async_mode || stream == SNDRV_PCM_STREAM_CAPTURE) {
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXSSZ(fmt),
+ RXSSZ(0x0F));
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXROT(rx_rotate),
+ RXROT(7));
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXMASK_REG, mask);
+ }
} else {
/*
+ * DIT mode only use TX serializers
* according to the TRM it should be TXROT=0, this one works:
* 16 bit to 23-8 (TXROT=6, rotate 24 bits)
* 24 bit to 23-0 (TXROT=0, rotate 0 bits)
@@ -844,10 +1046,9 @@ static int davinci_config_channel_size(struct davinci_mcasp *mcasp,
TXROT(7));
mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXSSZ(15),
TXSSZ(0x0F));
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXMASK_REG, mask);
}
- mcasp_set_reg(mcasp, DAVINCI_MCASP_TXMASK_REG, mask);
-
return 0;
}
@@ -858,11 +1059,13 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream,
int i;
u8 tx_ser = 0;
u8 rx_ser = 0;
- u8 slots = mcasp->tdm_slots;
+ int slots;
u8 max_active_serializers, max_rx_serializers, max_tx_serializers;
int active_serializers, numevt;
u32 reg;
+ slots = mcasp_get_tdm_slots(mcasp, stream);
+
/* In DIT mode we only allow maximum of one serializers for now */
if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE)
max_active_serializers = 1;
@@ -990,7 +1193,7 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream,
u32 mask = 0;
u32 busel = 0;
- total_slots = mcasp->tdm_slots;
+ total_slots = mcasp_get_tdm_slots(mcasp, stream);
/*
* If more than one serializer is needed, then use them with
@@ -1021,7 +1224,10 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream,
mask |= (1 << i);
}
- mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC);
+ if (mcasp->async_mode)
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC);
+ else
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC);
if (!mcasp->dat_port)
busel = TXSEL;
@@ -1041,7 +1247,8 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream,
* not running already we need to configure the TX slots in
* order to have correct FSX on the bus
*/
- if (mcasp_is_synchronous(mcasp) && !mcasp->channels)
+ if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) &&
+ !mcasp->channels)
mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG,
FSXMOD(total_slots), FSXMOD(0x1FF));
}
@@ -1119,16 +1326,33 @@ static int mcasp_dit_hw_param(struct davinci_mcasp *mcasp,
static int davinci_mcasp_calc_clk_div(struct davinci_mcasp *mcasp,
unsigned int sysclk_freq,
- unsigned int bclk_freq, bool set)
+ unsigned int bclk_freq,
+ int stream,
+ bool set)
{
- u32 reg = mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG);
int div = sysclk_freq / bclk_freq;
int rem = sysclk_freq % bclk_freq;
int error_ppm;
int aux_div = 1;
+ int bclk_div_id, auxclk_div_id;
+ bool auxclk_enabled;
+
+ if (mcasp->async_mode && stream == SNDRV_PCM_STREAM_CAPTURE) {
+ auxclk_enabled = mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG) & AHCLKRE;
+ bclk_div_id = MCASP_CLKDIV_BCLK_RXONLY;
+ auxclk_div_id = MCASP_CLKDIV_AUXCLK_RXONLY;
+ } else if (mcasp->async_mode && stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ auxclk_enabled = mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG) & AHCLKXE;
+ bclk_div_id = MCASP_CLKDIV_BCLK_TXONLY;
+ auxclk_div_id = MCASP_CLKDIV_AUXCLK_TXONLY;
+ } else {
+ auxclk_enabled = mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG) & AHCLKXE;
+ bclk_div_id = MCASP_CLKDIV_BCLK;
+ auxclk_div_id = MCASP_CLKDIV_AUXCLK;
+ }
if (div > (ACLKXDIV_MASK + 1)) {
- if (reg & AHCLKXE) {
+ if (auxclk_enabled) {
aux_div = div / (ACLKXDIV_MASK + 1);
if (div % (ACLKXDIV_MASK + 1))
aux_div++;
@@ -1158,10 +1382,10 @@ static int davinci_mcasp_calc_clk_div(struct davinci_mcasp *mcasp,
dev_info(mcasp->dev, "Sample-rate is off by %d PPM\n",
error_ppm);
- __davinci_mcasp_set_clkdiv(mcasp, MCASP_CLKDIV_BCLK, div, 0);
- if (reg & AHCLKXE)
- __davinci_mcasp_set_clkdiv(mcasp, MCASP_CLKDIV_AUXCLK,
- aux_div, 0);
+ __davinci_mcasp_set_clkdiv(mcasp, bclk_div_id, div, false);
+ if (auxclk_enabled)
+ __davinci_mcasp_set_clkdiv(mcasp, auxclk_div_id,
+ aux_div, false);
}
return error_ppm;
@@ -1212,6 +1436,7 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
int channels = params_channels(params);
int period_size = params_period_size(params);
int ret;
+ unsigned int sysclk_freq = mcasp_get_sysclk_freq(mcasp, substream->stream);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_U8:
@@ -1252,22 +1477,26 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
* If mcasp is BCLK master, and a BCLK divider was not provided by
* the machine driver, we need to calculate the ratio.
*/
- if (mcasp->bclk_master && mcasp->bclk_div == 0 && mcasp->sysclk_freq) {
- int slots = mcasp->tdm_slots;
+ if (mcasp->bclk_master && mcasp_get_bclk_div(mcasp, substream->stream) == 0 &&
+ sysclk_freq) {
+ int slots, slot_width;
int rate = params_rate(params);
int sbits = params_width(params);
unsigned int bclk_target;
- if (mcasp->slot_width)
- sbits = mcasp->slot_width;
+ slots = mcasp_get_tdm_slots(mcasp, substream->stream);
+
+ slot_width = mcasp_get_slot_width(mcasp, substream->stream);
+ if (slot_width)
+ sbits = slot_width;
if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE)
bclk_target = rate * sbits * slots;
else
bclk_target = rate * 128;
- davinci_mcasp_calc_clk_div(mcasp, mcasp->sysclk_freq,
- bclk_target, true);
+ davinci_mcasp_calc_clk_div(mcasp, sysclk_freq,
+ bclk_target, substream->stream, true);
}
ret = mcasp_common_hw_param(mcasp, substream->stream,
@@ -1284,9 +1513,10 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
if (ret)
return ret;
- davinci_config_channel_size(mcasp, word_length);
+ davinci_config_channel_size(mcasp, word_length, substream->stream);
- if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) {
+ /* Channel constraints are disabled for async mode */
+ if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE && !mcasp->async_mode) {
mcasp->channels = channels;
if (!mcasp->max_format_width)
mcasp->max_format_width = word_length;
@@ -1330,7 +1560,7 @@ static int davinci_mcasp_hw_rule_slot_width(struct snd_pcm_hw_params *params,
snd_pcm_format_t i;
snd_mask_none(&nfmt);
- slot_width = rd->mcasp->slot_width;
+ slot_width = mcasp_get_slot_width(rd->mcasp, rd->stream);
pcm_for_each_format(i) {
if (snd_mask_test_format(fmt, i)) {
@@ -1380,12 +1610,15 @@ static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params,
struct snd_interval *ri =
hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
int sbits = params_width(params);
- int slots = rd->mcasp->tdm_slots;
+ int slots, slot_width;
struct snd_interval range;
int i;
- if (rd->mcasp->slot_width)
- sbits = rd->mcasp->slot_width;
+ slots = mcasp_get_tdm_slots(rd->mcasp, rd->stream);
+
+ slot_width = mcasp_get_slot_width(rd->mcasp, rd->stream);
+ if (slot_width)
+ sbits = slot_width;
snd_interval_any(&range);
range.empty = 1;
@@ -1395,16 +1628,17 @@ static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params,
uint bclk_freq = sbits * slots *
davinci_mcasp_dai_rates[i];
unsigned int sysclk_freq;
+ unsigned int ratio;
int ppm;
- if (rd->mcasp->auxclk_fs_ratio)
- sysclk_freq = davinci_mcasp_dai_rates[i] *
- rd->mcasp->auxclk_fs_ratio;
+ ratio = mcasp_get_auxclk_fs_ratio(rd->mcasp, rd->stream);
+ if (ratio)
+ sysclk_freq = davinci_mcasp_dai_rates[i] * ratio;
else
- sysclk_freq = rd->mcasp->sysclk_freq;
+ sysclk_freq = mcasp_get_sysclk_freq(rd->mcasp, rd->stream);
ppm = davinci_mcasp_calc_clk_div(rd->mcasp, sysclk_freq,
- bclk_freq, false);
+ bclk_freq, rd->stream, false);
if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) {
if (range.empty) {
range.min = davinci_mcasp_dai_rates[i];
@@ -1430,30 +1664,34 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params,
struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
struct snd_mask nfmt;
int rate = params_rate(params);
- int slots = rd->mcasp->tdm_slots;
+ int slots;
int count = 0;
snd_pcm_format_t i;
+ slots = mcasp_get_tdm_slots(rd->mcasp, rd->stream);
+
snd_mask_none(&nfmt);
pcm_for_each_format(i) {
if (snd_mask_test_format(fmt, i)) {
uint sbits = snd_pcm_format_width(i);
unsigned int sysclk_freq;
- int ppm;
+ unsigned int ratio;
+ int ppm, slot_width;
- if (rd->mcasp->auxclk_fs_ratio)
- sysclk_freq = rate *
- rd->mcasp->auxclk_fs_ratio;
+ ratio = mcasp_get_auxclk_fs_ratio(rd->mcasp, rd->stream);
+ if (ratio)
+ sysclk_freq = rate * ratio;
else
- sysclk_freq = rd->mcasp->sysclk_freq;
+ sysclk_freq = mcasp_get_sysclk_freq(rd->mcasp, rd->stream);
- if (rd->mcasp->slot_width)
- sbits = rd->mcasp->slot_width;
+ slot_width = mcasp_get_slot_width(rd->mcasp, rd->stream);
+ if (slot_width)
+ sbits = slot_width;
ppm = davinci_mcasp_calc_clk_div(rd->mcasp, sysclk_freq,
sbits * slots * rate,
- false);
+ rd->stream, false);
if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) {
snd_mask_set_format(&nfmt, i);
count++;
@@ -1490,7 +1728,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
&mcasp->ruledata[substream->stream];
u32 max_channels = 0;
int i, dir, ret;
- int tdm_slots = mcasp->tdm_slots;
+ int tdm_slots;
u8 *numevt;
/* Do not allow more then one stream per direction */
@@ -1499,6 +1737,8 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
mcasp->substreams[substream->stream] = substream;
+ tdm_slots = mcasp_get_tdm_slots(mcasp, substream->stream);
+
if (mcasp->tdm_mask[substream->stream])
tdm_slots = hweight32(mcasp->tdm_mask[substream->stream]);
@@ -1520,6 +1760,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
}
ruledata->serializers = max_channels;
ruledata->mcasp = mcasp;
+ ruledata->stream = substream->stream;
max_channels *= tdm_slots;
/*
* If the already active stream has less channels than the calculated
@@ -1527,9 +1768,13 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
* is in use we need to use that as a constraint for the second stream.
* Otherwise (first stream or less allowed channels or more than one
* serializer in use) we use the calculated constraint.
+ *
+ * However, in async mode, TX and RX have independent clocks and can
+ * use different configurations, so don't apply the constraint.
*/
if (mcasp->channels && mcasp->channels < max_channels &&
- ruledata->serializers == 1)
+ ruledata->serializers == 1 &&
+ !mcasp->async_mode)
max_channels = mcasp->channels;
/*
* But we can always allow channels upto the amount of
@@ -1546,10 +1791,10 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
0, SNDRV_PCM_HW_PARAM_CHANNELS,
&mcasp->chconstr[substream->stream]);
- if (mcasp->max_format_width) {
+ if (mcasp->max_format_width && !mcasp->async_mode) {
/*
* Only allow formats which require same amount of bits on the
- * bus as the currently running stream
+ * bus as the currently running stream to ensure sync mode
*/
ret = snd_pcm_hw_rule_add(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_FORMAT,
@@ -1558,8 +1803,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
SNDRV_PCM_HW_PARAM_FORMAT, -1);
if (ret)
return ret;
- }
- else if (mcasp->slot_width) {
+ } else if (mcasp_get_slot_width(mcasp, substream->stream)) {
/* Only allow formats require <= slot_width bits on the bus */
ret = snd_pcm_hw_rule_add(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_FORMAT,
@@ -1574,7 +1818,8 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
* If we rely on implicit BCLK divider setting we should
* set constraints based on what we can provide.
*/
- if (mcasp->bclk_master && mcasp->bclk_div == 0 && mcasp->sysclk_freq) {
+ if (mcasp->bclk_master && mcasp_get_bclk_div(mcasp, substream->stream) == 0 &&
+ mcasp_get_sysclk_freq(mcasp, substream->stream)) {
ret = snd_pcm_hw_rule_add(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
davinci_mcasp_hw_rule_rate,
@@ -1751,8 +1996,6 @@ static struct snd_soc_dai_driver davinci_mcasp_dai[] = {
.formats = DAVINCI_MCASP_PCM_FMTS,
},
.ops = &davinci_mcasp_dai_ops,
-
- .symmetric_rate = 1,
},
{
.name = "davinci-mcasp.1",
@@ -1875,11 +2118,7 @@ err1:
static bool davinci_mcasp_have_gpiochip(struct davinci_mcasp *mcasp)
{
-#ifdef CONFIG_OF_GPIO
- return of_property_read_bool(mcasp->dev->of_node, "gpio-controller");
-#else
- return false;
-#endif
+ return device_property_present(mcasp->dev, "gpio-controller");
}
static int davinci_mcasp_get_config(struct davinci_mcasp *mcasp,
@@ -1914,18 +2153,33 @@ static int davinci_mcasp_get_config(struct davinci_mcasp *mcasp,
goto out;
}
+ /* Parse TX-specific TDM slot and use it as default for RX */
if (of_property_read_u32(np, "tdm-slots", &val) == 0) {
if (val < 2 || val > 32) {
- dev_err(&pdev->dev, "tdm-slots must be in rage [2-32]\n");
+ dev_err(&pdev->dev, "tdm-slots must be in range [2-32]\n");
return -EINVAL;
}
- pdata->tdm_slots = val;
+ pdata->tdm_slots_tx = val;
+ pdata->tdm_slots_rx = val;
} else if (pdata->op_mode == DAVINCI_MCASP_IIS_MODE) {
mcasp->missing_audio_param = true;
goto out;
}
+ /* Parse RX-specific TDM slot count if provided */
+ if (of_property_read_u32(np, "tdm-slots-rx", &val) == 0) {
+ if (val < 2 || val > 32) {
+ dev_err(&pdev->dev, "tdm-slots-rx must be in range [2-32]\n");
+ return -EINVAL;
+ }
+
+ pdata->tdm_slots_rx = val;
+ }
+
+ if (pdata->op_mode != DAVINCI_MCASP_DIT_MODE)
+ mcasp->async_mode = of_property_read_bool(np, "ti,async-mode");
+
of_serial_dir32 = of_get_property(np, "serial-dir", &val);
val /= sizeof(u32);
if (of_serial_dir32) {
@@ -1951,8 +2205,15 @@ static int davinci_mcasp_get_config(struct davinci_mcasp *mcasp,
if (of_property_read_u32(np, "rx-num-evt", &val) == 0)
pdata->rxnumevt = val;
- if (of_property_read_u32(np, "auxclk-fs-ratio", &val) == 0)
- mcasp->auxclk_fs_ratio = val;
+ /* Parse TX-specific auxclk/fs ratio and use it as default for RX */
+ if (of_property_read_u32(np, "auxclk-fs-ratio", &val) == 0) {
+ mcasp->auxclk_fs_ratio_tx = val;
+ mcasp->auxclk_fs_ratio_rx = val;
+ }
+
+ /* Parse RX-specific auxclk/fs ratio if provided */
+ if (of_property_read_u32(np, "auxclk-fs-ratio-rx", &val) == 0)
+ mcasp->auxclk_fs_ratio_rx = val;
if (of_property_read_u32(np, "dismod", &val) == 0) {
if (val == 0 || val == 2 || val == 3) {
@@ -1981,19 +2242,51 @@ out:
mcasp->op_mode = pdata->op_mode;
/* sanity check for tdm slots parameter */
if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) {
- if (pdata->tdm_slots < 2) {
- dev_warn(&pdev->dev, "invalid tdm slots: %d\n",
- pdata->tdm_slots);
- mcasp->tdm_slots = 2;
- } else if (pdata->tdm_slots > 32) {
- dev_warn(&pdev->dev, "invalid tdm slots: %d\n",
- pdata->tdm_slots);
- mcasp->tdm_slots = 32;
+ if (pdata->tdm_slots_tx < 2) {
+ dev_warn(&pdev->dev, "invalid tdm tx slots: %d\n",
+ pdata->tdm_slots_tx);
+ mcasp->tdm_slots_tx = 2;
+ } else if (pdata->tdm_slots_tx > 32) {
+ dev_warn(&pdev->dev, "invalid tdm tx slots: %d\n",
+ pdata->tdm_slots_tx);
+ mcasp->tdm_slots_tx = 32;
} else {
- mcasp->tdm_slots = pdata->tdm_slots;
+ mcasp->tdm_slots_tx = pdata->tdm_slots_tx;
+ }
+
+ if (pdata->tdm_slots_rx < 2) {
+ dev_warn(&pdev->dev, "invalid tdm rx slots: %d\n",
+ pdata->tdm_slots_rx);
+ mcasp->tdm_slots_rx = 2;
+ } else if (pdata->tdm_slots_rx > 32) {
+ dev_warn(&pdev->dev, "invalid tdm rx slots: %d\n",
+ pdata->tdm_slots_rx);
+ mcasp->tdm_slots_rx = 32;
+ } else {
+ mcasp->tdm_slots_rx = pdata->tdm_slots_rx;
}
} else {
- mcasp->tdm_slots = 32;
+ mcasp->tdm_slots_tx = 32;
+ mcasp->tdm_slots_rx = 32;
+ }
+
+ /* Different TX/RX slot counts require async mode */
+ if (pdata->op_mode != DAVINCI_MCASP_DIT_MODE &&
+ mcasp->tdm_slots_tx != mcasp->tdm_slots_rx && !mcasp->async_mode) {
+ dev_err(&pdev->dev,
+ "Different TX (%d) and RX (%d) TDM slots require ti,async-mode\n",
+ mcasp->tdm_slots_tx, mcasp->tdm_slots_rx);
+ return -EINVAL;
+ }
+
+ /* Different TX/RX auxclk-fs-ratio require async mode */
+ if (pdata->op_mode != DAVINCI_MCASP_DIT_MODE &&
+ mcasp->auxclk_fs_ratio_tx && mcasp->auxclk_fs_ratio_rx &&
+ mcasp->auxclk_fs_ratio_tx != mcasp->auxclk_fs_ratio_rx && !mcasp->async_mode) {
+ dev_err(&pdev->dev,
+ "Different TX (%d) and RX (%d) auxclk-fs-ratio require ti,async-mode\n",
+ mcasp->auxclk_fs_ratio_tx, mcasp->auxclk_fs_ratio_rx);
+ return -EINVAL;
}
mcasp->num_serializer = pdata->num_serializer;
diff --git a/sound/soc/ti/davinci-mcasp.h b/sound/soc/ti/davinci-mcasp.h
index 5de2b8a31061..83b3c67f4a2b 100644
--- a/sound/soc/ti/davinci-mcasp.h
+++ b/sound/soc/ti/davinci-mcasp.h
@@ -298,10 +298,20 @@
/* Source of High-frequency transmit/receive clock */
#define MCASP_CLK_HCLK_AHCLK 0 /* AHCLKX/R */
#define MCASP_CLK_HCLK_AUXCLK 1 /* Internal functional clock */
+#define MCASP_CLK_HCLK_AHCLK_TXONLY 2 /* AHCLKX for TX only */
+#define MCASP_CLK_HCLK_AHCLK_RXONLY 3 /* AHCLKR for RX only */
+#define MCASP_CLK_HCLK_AUXCLK_TXONLY 4 /* AUXCLK for TX only */
+#define MCASP_CLK_HCLK_AUXCLK_RXONLY 5 /* AUXCLK for RX only */
/* clock divider IDs */
#define MCASP_CLKDIV_AUXCLK 0 /* HCLK divider from AUXCLK */
#define MCASP_CLKDIV_BCLK 1 /* BCLK divider from HCLK */
#define MCASP_CLKDIV_BCLK_FS_RATIO 2 /* to set BCLK FS ration */
+#define MCASP_CLKDIV_AUXCLK_TXONLY 3 /* AUXCLK divider for TX only */
+#define MCASP_CLKDIV_AUXCLK_RXONLY 4 /* AUXCLK divider for RX only */
+#define MCASP_CLKDIV_BCLK_TXONLY 5 /* BCLK divider for TX only */
+#define MCASP_CLKDIV_BCLK_RXONLY 6 /* BCLK divider for RX only */
+#define MCASP_CLKDIV_BCLK_FS_RATIO_TXONLY 7 /* BCLK/FS ratio for TX only */
+#define MCASP_CLKDIV_BCLK_FS_RATIO_RXONLY 8 /* BCLK/FS ratio for RX only*/
#endif /* DAVINCI_MCASP_H */
diff --git a/sound/usb/fcp.c b/sound/usb/fcp.c
index 11e9a96b46ff..1f4595d1e217 100644
--- a/sound/usb/fcp.c
+++ b/sound/usb/fcp.c
@@ -182,10 +182,6 @@ static int fcp_usb(struct usb_mixer_interface *mixer, u32 opcode,
{
struct fcp_data *private = mixer->private_data;
struct usb_device *dev = mixer->chip->dev;
- struct fcp_usb_packet *req __free(kfree) = NULL;
- struct fcp_usb_packet *resp __free(kfree) = NULL;
- size_t req_buf_size = struct_size(req, data, req_size);
- size_t resp_buf_size = struct_size(resp, data, resp_size);
int retries = 0;
const int max_retries = 5;
int err;
@@ -193,10 +189,14 @@ static int fcp_usb(struct usb_mixer_interface *mixer, u32 opcode,
if (!mixer->urb)
return -ENODEV;
+ struct fcp_usb_packet *req __free(kfree) = NULL;
+ size_t req_buf_size = struct_size(req, data, req_size);
req = kmalloc(req_buf_size, GFP_KERNEL);
if (!req)
return -ENOMEM;
+ struct fcp_usb_packet *resp __free(kfree) = NULL;
+ size_t resp_buf_size = struct_size(resp, data, resp_size);
resp = kmalloc(resp_buf_size, GFP_KERNEL);
if (!resp)
return -ENOMEM;
@@ -300,16 +300,17 @@ retry:
static int fcp_reinit(struct usb_mixer_interface *mixer)
{
struct fcp_data *private = mixer->private_data;
- void *step0_resp __free(kfree) = NULL;
- void *step2_resp __free(kfree) = NULL;
if (mixer->urb)
return 0;
- step0_resp = kmalloc(private->step0_resp_size, GFP_KERNEL);
+ void *step0_resp __free(kfree) =
+ kmalloc(private->step0_resp_size, GFP_KERNEL);
if (!step0_resp)
return -ENOMEM;
- step2_resp = kmalloc(private->step2_resp_size, GFP_KERNEL);
+
+ void *step2_resp __free(kfree) =
+ kmalloc(private->step2_resp_size, GFP_KERNEL);
if (!step2_resp)
return -ENOMEM;
@@ -464,7 +465,6 @@ static int fcp_ioctl_init(struct usb_mixer_interface *mixer,
struct fcp_init init;
struct usb_device *dev = mixer->chip->dev;
struct fcp_data *private = mixer->private_data;
- void *resp __free(kfree) = NULL;
void *step2_resp;
int err, buf_size;
@@ -485,7 +485,8 @@ static int fcp_ioctl_init(struct usb_mixer_interface *mixer,
/* Allocate response buffer */
buf_size = init.step0_resp_size + init.step2_resp_size;
- resp = kmalloc(buf_size, GFP_KERNEL);
+ void *resp __free(kfree) =
+ kmalloc(buf_size, GFP_KERNEL);
if (!resp)
return -ENOMEM;
@@ -619,7 +620,6 @@ static int fcp_ioctl_set_meter_map(struct usb_mixer_interface *mixer,
{
struct fcp_meter_map map;
struct fcp_data *private = mixer->private_data;
- s16 *tmp_map __free(kfree) = NULL;
int err;
if (copy_from_user(&map, arg, sizeof(map)))
@@ -641,7 +641,8 @@ static int fcp_ioctl_set_meter_map(struct usb_mixer_interface *mixer,
return -EINVAL;
/* Allocate and copy the map data */
- tmp_map = memdup_array_user(arg->map, map.map_size, sizeof(s16));
+ s16 *tmp_map __free(kfree) =
+ memdup_array_user(arg->map, map.map_size, sizeof(s16));
if (IS_ERR(tmp_map))
return PTR_ERR(tmp_map);
@@ -651,17 +652,16 @@ static int fcp_ioctl_set_meter_map(struct usb_mixer_interface *mixer,
/* If the control doesn't exist, create it */
if (!private->meter_ctl) {
- s16 *new_map __free(kfree) = NULL;
- __le32 *meter_levels __free(kfree) = NULL;
-
/* Allocate buffer for the map */
- new_map = kmalloc_array(map.map_size, sizeof(s16), GFP_KERNEL);
+ s16 *new_map __free(kfree) =
+ kmalloc_array(map.map_size, sizeof(s16), GFP_KERNEL);
if (!new_map)
return -ENOMEM;
/* Allocate buffer for reading meter levels */
- meter_levels = kmalloc_array(map.meter_slots, sizeof(__le32),
- GFP_KERNEL);
+ __le32 *meter_levels __free(kfree) =
+ kmalloc_array(map.meter_slots, sizeof(__le32),
+ GFP_KERNEL);
if (!meter_levels)
return -ENOMEM;
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index cf4ccf467cb6..ec65e8a71919 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -4412,6 +4412,9 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
err = snd_create_std_mono_table(mixer, ebox44_table);
break;
+ case USB_ID(0x1235, 0x8010): /* Focusrite Forte */
+ err = snd_forte_controls_create(mixer);
+ break;
case USB_ID(0x1235, 0x8012): /* Focusrite Scarlett 6i6 */
case USB_ID(0x1235, 0x8002): /* Focusrite Scarlett 8i6 */
case USB_ID(0x1235, 0x8004): /* Focusrite Scarlett 18i6 */
diff --git a/sound/usb/mixer_s1810c.c b/sound/usb/mixer_s1810c.c
index 6e09e074c0e7..b4ce89afb25b 100644
--- a/sound/usb/mixer_s1810c.c
+++ b/sound/usb/mixer_s1810c.c
@@ -25,12 +25,6 @@
#include "helper.h"
#include "mixer_s1810c.h"
-#define SC1810C_CMD_REQ 160
-#define SC1810C_CMD_REQTYPE \
- (USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT)
-#define SC1810C_CMD_F1 0x50617269
-#define SC1810C_CMD_F2 0x14
-
/*
* DISCLAIMER: These are just guesses based on the
* dumps I got.
@@ -42,9 +36,8 @@
* * b selects an input channel (see below).
* * c selects an output channel pair (see below).
* * d selects left (0) or right (1) of that pair.
- * * e 0-> disconnect, 0x01000000-> connect,
- * 0x0109-> used for stereo-linking channels,
- * e is also used for setting volume levels
+ * * e level : see MIXER_LEVEL_* defines below.
+ * Also used for setting volume levels
* in which case b is also set so I guess
* this way it is possible to set the volume
* level from the specified input to the
@@ -75,52 +68,98 @@
* For output (0x65):
* * b is the output channel (see above).
* * c is zero.
- * * e I guess the same as with mixer except 0x0109
- * which I didn't see in my dumps.
+ * * e I guess the same as with mixer
+ *
+ */
+/** struct s1810c_ctl_packet - basic vendor request
+ * @selector: device/mixer/output
+ * @b: request-dependant field b
+ * @tag: fixed value identifying type of request
+ * @len: sizeof this struct - 8 (excludes first 2 fields)
+ * i.e. for basic struct s1810c_ctl_packet: len is 5*4=0x14
+ * @c: request-dependant field c
+ * @d: request-dependant field d
+ * @e: request-dependant field e
*
- * The two fixed fields have the same values for
- * mixer and output but a different set for device.
+ * See longer description above. This could be combined
+ * (as a union?) with the longer struct s1810c_state_packet
*/
struct s1810c_ctl_packet {
- u32 a;
- u32 b;
- u32 fixed1;
- u32 fixed2;
- u32 c;
- u32 d;
- u32 e;
+ __le32 selector;
+ __le32 b;
+ __le32 tag;
+ __le32 len;
+ __le32 c;
+ __le32 d;
+ __le32 e;
};
+/** selectors for CMD request
+ */
+#define SC1810C_SEL_DEVICE 0
+#define SC1810C_SEL_MIXER 0x64
+#define SC1810C_SEL_OUTPUT 0x65
+
+
+/** control ids */
#define SC1810C_CTL_LINE_SW 0
#define SC1810C_CTL_MUTE_SW 1
#define SC1824C_CTL_MONO_SW 2
#define SC1810C_CTL_AB_SW 3
#define SC1810C_CTL_48V_SW 4
+/* USB Control (vendor) requests
+ */
+#define SC1810C_CMD_REQ 160
+#define SC1810C_CMD_REQTYPE \
+ (USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT)
+#define SC1810C_CMD_TAG 0x50617269
+#define SC1810C_CMD_LEN 0x14
+
#define SC1810C_SET_STATE_REQ 161
#define SC1810C_SET_STATE_REQTYPE SC1810C_CMD_REQTYPE
-#define SC1810C_SET_STATE_F1 0x64656D73
-#define SC1810C_SET_STATE_F2 0xF4
+#define SC1810C_SET_STATE_TAG 0x64656D73
+#define SC1810C_SET_STATE_LEN 0xF4
#define SC1810C_GET_STATE_REQ 162
#define SC1810C_GET_STATE_REQTYPE \
(USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN)
-#define SC1810C_GET_STATE_F1 SC1810C_SET_STATE_F1
-#define SC1810C_GET_STATE_F2 SC1810C_SET_STATE_F2
-
-#define SC1810C_STATE_F1_IDX 2
-#define SC1810C_STATE_F2_IDX 3
+#define SC1810C_GET_STATE_TAG SC1810C_SET_STATE_TAG
+#define SC1810C_GET_STATE_LEN SC1810C_SET_STATE_LEN
+
+/** Mixer levels normally range from 0 (off) to 0x0100 0000 (0 dB).
+ * raw_level = 2^24 * 10^(db_level / 20), thus
+ * -3dB = 0xb53bf0 (technically, half-power -3.01...dB would be 0xb504f3)
+ * -96dB = 0x109
+ * -99dB = 0xBC
+ * PC software sliders cover -96 to +10dB (0x0329 8b08),
+ * but the value 0 (-inf dB) can be used when e.g. Mixer Bypass is enabled.
+ * Unclear what the hardware's maximum value is.
+ *
+ * Note, when a channel is panned to two channels (stereo),
+ * the mixer level is set to slider value (by default -96dB) minus 3dB,
+ * which explains the -99dB value seen in USB captures.
+ */
+#define MIXER_LEVEL_MUTE 0
+#define MIXER_LEVEL_N99DB 0xbc
+#define MIXER_LEVEL_N3DB 0xb53bf0
+#define MIXER_LEVEL_0DB 0x1000000
-/*
+/**
* This packet includes mixer volumes and
* various other fields, it's an extended
* version of ctl_packet, with a and b
- * being zero and different f1/f2.
+ * being zero and different tag/length.
*/
struct s1810c_state_packet {
- u32 fields[63];
+ __le32 fields[63];
};
+/** indices into s1810c_state_packet.fields[]
+ */
+#define SC1810C_STATE_TAG_IDX 2
+#define SC1810C_STATE_LEN_IDX 3
+
#define SC1810C_STATE_48V_SW 58
#define SC1810C_STATE_LINE_SW 59
#define SC1810C_STATE_MUTE_SW 60
@@ -134,20 +173,20 @@ struct s1810_mixer_state {
};
static int
-snd_s1810c_send_ctl_packet(struct usb_device *dev, u32 a,
+snd_s1810c_send_ctl_packet(struct usb_device *dev, u32 sel,
u32 b, u32 c, u32 d, u32 e)
{
struct s1810c_ctl_packet pkt = { 0 };
int ret = 0;
- pkt.fixed1 = SC1810C_CMD_F1;
- pkt.fixed2 = SC1810C_CMD_F2;
+ pkt.tag = __cpu_to_le32(SC1810C_CMD_TAG);
+ pkt.len = __cpu_to_le32(SC1810C_CMD_LEN);
- pkt.a = a;
- pkt.b = b;
- pkt.c = c;
- pkt.d = d;
- pkt.e = e;
+ pkt.selector = __cpu_to_le32(sel);
+ pkt.b = __cpu_to_le32(b);
+ pkt.c = __cpu_to_le32(c);
+ pkt.d = __cpu_to_le32(d);
+ pkt.e = __cpu_to_le32(e);
ret = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
SC1810C_CMD_REQ,
@@ -176,8 +215,8 @@ snd_sc1810c_get_status_field(struct usb_device *dev,
struct s1810c_state_packet pkt_in = { { 0 } };
int ret = 0;
- pkt_out.fields[SC1810C_STATE_F1_IDX] = SC1810C_SET_STATE_F1;
- pkt_out.fields[SC1810C_STATE_F2_IDX] = SC1810C_SET_STATE_F2;
+ pkt_out.fields[SC1810C_STATE_TAG_IDX] = __cpu_to_le32(SC1810C_SET_STATE_TAG);
+ pkt_out.fields[SC1810C_STATE_LEN_IDX] = __cpu_to_le32(SC1810C_SET_STATE_LEN);
ret = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
SC1810C_SET_STATE_REQ,
SC1810C_SET_STATE_REQTYPE,
@@ -197,7 +236,7 @@ snd_sc1810c_get_status_field(struct usb_device *dev,
return ret;
}
- (*field) = pkt_in.fields[field_idx];
+ (*field) = __le32_to_cpu(pkt_in.fields[field_idx]);
(*seqnum)++;
return 0;
}
@@ -216,8 +255,8 @@ static int snd_s1810c_init_mixer_maps(struct snd_usb_audio *chip)
switch (chip->usb_id) {
case USB_ID(0x194f, 0x010c): /* 1810c */
/* Set initial volume levels ? */
- a = 0x64;
- e = 0xbc;
+ a = SC1810C_SEL_MIXER;
+ e = MIXER_LEVEL_N99DB;
for (n = 0; n < 2; n++) {
off = n * 18;
for (b = off; b < 18 + off; b++) {
@@ -234,22 +273,21 @@ static int snd_s1810c_init_mixer_maps(struct snd_usb_audio *chip)
* I noticed on UC that DAW channels have different
* initial volumes, so this makes sense.
*/
- e = 0xb53bf0;
+ e = MIXER_LEVEL_N3DB;
}
/* Connect analog outputs ? */
- a = 0x65;
- e = 0x01000000;
+ a = SC1810C_SEL_OUTPUT;
for (b = 1; b < 3; b++) {
- snd_s1810c_send_ctl_packet(dev, a, b, 0, 0, e);
- snd_s1810c_send_ctl_packet(dev, a, b, 0, 1, e);
+ snd_s1810c_send_ctl_packet(dev, a, b, 0, 0, MIXER_LEVEL_0DB);
+ snd_s1810c_send_ctl_packet(dev, a, b, 0, 1, MIXER_LEVEL_0DB);
}
- snd_s1810c_send_ctl_packet(dev, a, 0, 0, 0, e);
- snd_s1810c_send_ctl_packet(dev, a, 0, 0, 1, e);
+ snd_s1810c_send_ctl_packet(dev, a, 0, 0, 0, MIXER_LEVEL_0DB);
+ snd_s1810c_send_ctl_packet(dev, a, 0, 0, 1, MIXER_LEVEL_0DB);
/* Set initial volume levels for S/PDIF mappings ? */
- a = 0x64;
- e = 0xbc;
+ a = SC1810C_SEL_MIXER;
+ e = MIXER_LEVEL_N99DB;
c = 3;
for (n = 0; n < 2; n++) {
off = n * 18;
@@ -257,26 +295,23 @@ static int snd_s1810c_init_mixer_maps(struct snd_usb_audio *chip)
snd_s1810c_send_ctl_packet(dev, a, b, c, 0, e);
snd_s1810c_send_ctl_packet(dev, a, b, c, 1, e);
}
- e = 0xb53bf0;
+ e = MIXER_LEVEL_N3DB;
}
/* Connect S/PDIF output ? */
- a = 0x65;
- e = 0x01000000;
- snd_s1810c_send_ctl_packet(dev, a, 3, 0, 0, e);
- snd_s1810c_send_ctl_packet(dev, a, 3, 0, 1, e);
+ a = SC1810C_SEL_OUTPUT;
+ snd_s1810c_send_ctl_packet(dev, a, 3, 0, 0, MIXER_LEVEL_0DB);
+ snd_s1810c_send_ctl_packet(dev, a, 3, 0, 1, MIXER_LEVEL_0DB);
/* Connect all outputs (again) ? */
- a = 0x65;
- e = 0x01000000;
+ a = SC1810C_SEL_OUTPUT;
for (b = 0; b < 4; b++) {
- snd_s1810c_send_ctl_packet(dev, a, b, 0, 0, e);
- snd_s1810c_send_ctl_packet(dev, a, b, 0, 1, e);
+ snd_s1810c_send_ctl_packet(dev, a, b, 0, 0, MIXER_LEVEL_0DB);
+ snd_s1810c_send_ctl_packet(dev, a, b, 0, 1, MIXER_LEVEL_0DB);
}
/* Basic routing to get sound out of the device */
- a = 0x64;
- e = 0x01000000;
+ a = SC1810C_SEL_MIXER;
for (c = 0; c < 4; c++) {
for (b = 0; b < 36; b++) {
if ((c == 0 && b == 18) || /* DAW1/2 -> Main */
@@ -284,23 +319,29 @@ static int snd_s1810c_init_mixer_maps(struct snd_usb_audio *chip)
(c == 2 && b == 22) || /* DAW4/5 -> Line5/6 */
(c == 3 && b == 24)) { /* DAW5/6 -> S/PDIF */
/* Left */
- snd_s1810c_send_ctl_packet(dev, a, b, c, 0, e);
- snd_s1810c_send_ctl_packet(dev, a, b, c, 1, 0);
+ snd_s1810c_send_ctl_packet(dev,
+ a, b, c, 0, MIXER_LEVEL_0DB);
+ snd_s1810c_send_ctl_packet(dev,
+ a, b, c, 1, MIXER_LEVEL_MUTE);
b++;
/* Right */
- snd_s1810c_send_ctl_packet(dev, a, b, c, 0, 0);
- snd_s1810c_send_ctl_packet(dev, a, b, c, 1, e);
+ snd_s1810c_send_ctl_packet(dev,
+ a, b, c, 0, MIXER_LEVEL_MUTE);
+ snd_s1810c_send_ctl_packet(dev,
+ a, b, c, 1, MIXER_LEVEL_0DB);
} else {
/* Leave the rest disconnected */
- snd_s1810c_send_ctl_packet(dev, a, b, c, 0, 0);
- snd_s1810c_send_ctl_packet(dev, a, b, c, 1, 0);
+ snd_s1810c_send_ctl_packet(dev,
+ a, b, c, 0, MIXER_LEVEL_MUTE);
+ snd_s1810c_send_ctl_packet(dev,
+ a, b, c, 1, MIXER_LEVEL_MUTE);
}
}
}
/* Set initial volume levels for S/PDIF (again) ? */
- a = 0x64;
- e = 0xbc;
+ a = SC1810C_SEL_MIXER;
+ e = MIXER_LEVEL_N99DB;
c = 3;
for (n = 0; n < 2; n++) {
off = n * 18;
@@ -308,29 +349,26 @@ static int snd_s1810c_init_mixer_maps(struct snd_usb_audio *chip)
snd_s1810c_send_ctl_packet(dev, a, b, c, 0, e);
snd_s1810c_send_ctl_packet(dev, a, b, c, 1, e);
}
- e = 0xb53bf0;
+ e = MIXER_LEVEL_N3DB;
}
/* Connect S/PDIF outputs (again) ? */
- a = 0x65;
- e = 0x01000000;
- snd_s1810c_send_ctl_packet(dev, a, 3, 0, 0, e);
- snd_s1810c_send_ctl_packet(dev, a, 3, 0, 1, e);
+ a = SC1810C_SEL_OUTPUT;
+ snd_s1810c_send_ctl_packet(dev, a, 3, 0, 0, MIXER_LEVEL_0DB);
+ snd_s1810c_send_ctl_packet(dev, a, 3, 0, 1, MIXER_LEVEL_0DB);
/* Again ? */
- snd_s1810c_send_ctl_packet(dev, a, 3, 0, 0, e);
- snd_s1810c_send_ctl_packet(dev, a, 3, 0, 1, e);
+ snd_s1810c_send_ctl_packet(dev, a, 3, 0, 0, MIXER_LEVEL_0DB);
+ snd_s1810c_send_ctl_packet(dev, a, 3, 0, 1, MIXER_LEVEL_0DB);
break;
case USB_ID(0x194f, 0x010d): /* 1824c */
/* Set all output faders to unity gain */
- a = 0x65;
+ a = SC1810C_SEL_OUTPUT;
c = 0x00;
- e = 0x01000000;
-
for (b = 0; b < 9; b++) {
- snd_s1810c_send_ctl_packet(dev, a, b, c, 0, e);
- snd_s1810c_send_ctl_packet(dev, a, b, c, 1, e);
+ snd_s1810c_send_ctl_packet(dev, a, b, c, 0, MIXER_LEVEL_0DB);
+ snd_s1810c_send_ctl_packet(dev, a, b, c, 1, MIXER_LEVEL_0DB);
}
/* Set
@@ -345,7 +383,7 @@ static int snd_s1810c_init_mixer_maps(struct snd_usb_audio *chip)
* Daw 17 -> ADAT out 7, (left) Daw 18 -> ADAT out 8 (right)
* Everything else muted
*/
- a = 0x64;
+ a = SC1810C_SEL_MIXER;
/* The first Daw channel is channel 18 */
left = 18;
@@ -354,14 +392,20 @@ static int snd_s1810c_init_mixer_maps(struct snd_usb_audio *chip)
for (b = 0; b < 36; b++) {
if (b == left) {
- snd_s1810c_send_ctl_packet(dev, a, b, c, 0, 0x01000000);
- snd_s1810c_send_ctl_packet(dev, a, b, c, 1, 0x00);
+ snd_s1810c_send_ctl_packet(dev,
+ a, b, c, 0, MIXER_LEVEL_0DB);
+ snd_s1810c_send_ctl_packet(dev,
+ a, b, c, 1, MIXER_LEVEL_MUTE);
} else if (b == right) {
- snd_s1810c_send_ctl_packet(dev, a, b, c, 0, 0x00);
- snd_s1810c_send_ctl_packet(dev, a, b, c, 1, 0x01000000);
+ snd_s1810c_send_ctl_packet(dev,
+ a, b, c, 0, MIXER_LEVEL_MUTE);
+ snd_s1810c_send_ctl_packet(dev,
+ a, b, c, 1, MIXER_LEVEL_0DB);
} else {
- snd_s1810c_send_ctl_packet(dev, a, b, c, 0, 0x00);
- snd_s1810c_send_ctl_packet(dev, a, b, c, 1, 0x00);
+ snd_s1810c_send_ctl_packet(dev,
+ a, b, c, 0, MIXER_LEVEL_MUTE);
+ snd_s1810c_send_ctl_packet(dev,
+ a, b, c, 1, MIXER_LEVEL_MUTE);
}
}
left += 2;
diff --git a/sound/usb/mixer_scarlett.c b/sound/usb/mixer_scarlett.c
index 8babfa3f7c45..fa11f25288b7 100644
--- a/sound/usb/mixer_scarlett.c
+++ b/sound/usb/mixer_scarlett.c
@@ -135,7 +135,7 @@
/* some gui mixers can't handle negative ctl values */
#define SND_SCARLETT_LEVEL_BIAS 128
#define SND_SCARLETT_MATRIX_IN_MAX 18
-#define SND_SCARLETT_CONTROLS_MAX 10
+#define SND_SCARLETT_CONTROLS_MAX 14
#define SND_SCARLETT_OFFSETS_MAX 5
enum {
@@ -143,6 +143,12 @@ enum {
SCARLETT_SWITCH_IMPEDANCE,
SCARLETT_SWITCH_PAD,
SCARLETT_SWITCH_GAIN,
+ FORTE_INPUT_SOURCE, /* mic/line/instrument selection */
+ FORTE_INPUT_HPF, /* high pass filter */
+ FORTE_INPUT_PHANTOM, /* 48V phantom power */
+ FORTE_INPUT_PHASE, /* phase invert */
+ FORTE_INPUT_PAD, /* pad */
+ FORTE_INPUT_GAIN, /* preamp gain 0-42 (~0-75dB for mic) */
};
enum {
@@ -172,6 +178,8 @@ struct scarlett_device_info {
int input_len;
int output_len;
+ bool has_output_source_routing;
+
struct scarlett_mixer_elem_enum_info opt_master;
struct scarlett_mixer_elem_enum_info opt_matrix;
@@ -229,6 +237,239 @@ static const struct scarlett_mixer_elem_enum_info opt_sync = {
}
};
+/* Forte-specific input control options */
+static const struct scarlett_mixer_elem_enum_info opt_forte_source = {
+ .start = 0,
+ .len = 3,
+ .offsets = {},
+ .names = (char const * const []){
+ "Mic", "Line", "Inst"
+ }
+};
+
+/*
+ * Forte-specific USB control functions
+ * Forte input controls use bRequest=0x03 (UAC2_CS_MEM) instead of 0x01
+ * wValue = (control_code << 8) | channel
+ * wIndex = interface | (0x3c << 8) like Scarlett meter/matrix controls
+ */
+static int forte_set_ctl_value(struct usb_mixer_elem_info *elem, int value)
+{
+ struct snd_usb_audio *chip = elem->head.mixer->chip;
+ unsigned char buf[2];
+ int wValue = elem->control; /* Just control code, NO shift, NO channel */
+ int idx = snd_usb_ctrl_intf(elem->head.mixer->hostif) | (elem->head.id << 8);
+ int err;
+
+ /* Wiki format: "2 bytes, chan 0,1 and value"
+ * Data order: [channel, value] - channel FIRST per wiki
+ */
+ buf[0] = elem->idx_off; /* Channel: 0 or 1 */
+ buf[1] = value & 0xff; /* Value: 0-2 for source, 0-1 for switches */
+
+ err = snd_usb_lock_shutdown(chip);
+ if (err < 0)
+ return -EIO;
+
+ err = snd_usb_ctl_msg(chip->dev,
+ usb_sndctrlpipe(chip->dev, 0),
+ UAC2_CS_MEM, /* bRequest = 0x03 */
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
+ wValue, idx, buf, 2);
+
+ snd_usb_unlock_shutdown(chip);
+
+ if (err < 0) {
+ usb_audio_err(chip, "forte_set FAILED: req=0x03 wVal=0x%04x wIdx=0x%04x buf=[%02x,%02x] err=%d\n",
+ wValue, idx, buf[0], buf[1], err);
+ return err;
+ }
+ return 0;
+}
+
+static int forte_get_ctl_value(struct usb_mixer_elem_info *elem, int *value)
+{
+ /* Device may not support reading input controls.
+ * Return cached value or default to avoid blocking module load.
+ */
+ if (elem->cached)
+ *value = elem->cache_val[0];
+ else
+ *value = 0; /* Default: first option */
+
+ return 0;
+}
+
+/*
+ * Forte Input Gain control functions
+ * Gain range is 0-42 (0x00-0x2a) which maps to approximately:
+ * - Mic: 0 to +75dB (~1.8dB per step)
+ * - Instrument: +14 to +68dB
+ * - Line: -12 to +42dB
+ * We use a TLV scale of 0 to 7500 centidB (0 to 75dB) in ~179 cB steps
+ */
+#define FORTE_INPUT_GAIN_MAX 42
+
+static int forte_input_gain_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = FORTE_INPUT_GAIN_MAX;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+
+static int forte_input_gain_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ int err, val;
+
+ /* Use Forte-specific USB command with UAC2_CS_MEM */
+ err = forte_get_ctl_value(elem, &val);
+ if (err < 0)
+ return err;
+
+ ucontrol->value.integer.value[0] = clamp(val, 0, FORTE_INPUT_GAIN_MAX);
+ return 0;
+}
+
+static int forte_input_gain_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ int err, oval, val;
+
+ /* Read current value */
+ err = forte_get_ctl_value(elem, &oval);
+ if (err < 0)
+ return err;
+
+ val = clamp((int)ucontrol->value.integer.value[0], 0, FORTE_INPUT_GAIN_MAX);
+ if (oval != val) {
+ /* Use Forte-specific USB command */
+ err = forte_set_ctl_value(elem, val);
+ if (err < 0)
+ return err;
+ elem->cached |= 1;
+ elem->cache_val[0] = val;
+ return 1;
+ }
+ return 0;
+}
+
+static int forte_input_gain_resume(struct usb_mixer_elem_list *list)
+{
+ struct usb_mixer_elem_info *elem = mixer_elem_list_to_info(list);
+
+ if (elem->cached)
+ forte_set_ctl_value(elem, *elem->cache_val);
+ return 0;
+}
+
+/*
+ * Forte-specific enum control functions (for Source selection)
+ * Uses bRequest=0x03 (UAC2_CS_MEM) instead of standard 0x01
+ */
+static int forte_ctl_enum_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct scarlett_mixer_elem_enum_info *opt = elem->private_data;
+ int err, val;
+
+ err = forte_get_ctl_value(elem, &val);
+ if (err < 0)
+ return err;
+
+ val = clamp(val - opt->start, 0, opt->len - 1);
+ ucontrol->value.enumerated.item[0] = val;
+ return 0;
+}
+
+static int forte_ctl_enum_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct scarlett_mixer_elem_enum_info *opt = elem->private_data;
+ int err, oval, val;
+
+ err = forte_get_ctl_value(elem, &oval);
+ if (err < 0)
+ return err;
+
+ val = ucontrol->value.integer.value[0] + opt->start;
+ if (val != oval) {
+ err = forte_set_ctl_value(elem, val);
+ if (err < 0)
+ return err;
+ elem->cached |= 1;
+ elem->cache_val[0] = val;
+ return 1;
+ }
+ return 0;
+}
+
+static int forte_ctl_enum_resume(struct usb_mixer_elem_list *list)
+{
+ struct usb_mixer_elem_info *elem = mixer_elem_list_to_info(list);
+
+ if (elem->cached)
+ forte_set_ctl_value(elem, *elem->cache_val);
+ return 0;
+}
+
+/*
+ * Forte-specific switch control functions (for HPF, 48V, Phase, Pad)
+ * Uses bRequest=0x03 (UAC2_CS_MEM) instead of standard 0x01
+ */
+static int forte_ctl_switch_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ int err, val;
+
+ err = forte_get_ctl_value(elem, &val);
+ if (err < 0)
+ return err;
+
+ ucontrol->value.integer.value[0] = val ? 1 : 0;
+ return 0;
+}
+
+static int forte_ctl_switch_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ int err, oval, val;
+
+ err = forte_get_ctl_value(elem, &oval);
+ if (err < 0)
+ return err;
+
+ val = ucontrol->value.integer.value[0] ? 1 : 0;
+ if (val != oval) {
+ err = forte_set_ctl_value(elem, val);
+ if (err < 0)
+ return err;
+ elem->cached |= 1;
+ elem->cache_val[0] = val;
+ return 1;
+ }
+ return 0;
+}
+
+static int forte_ctl_switch_resume(struct usb_mixer_elem_list *list)
+{
+ struct usb_mixer_elem_info *elem = mixer_elem_list_to_info(list);
+
+ if (elem->cached)
+ forte_set_ctl_value(elem, *elem->cache_val);
+ return 0;
+}
+
static int scarlett_ctl_switch_info(struct snd_kcontrol *kctl,
struct snd_ctl_elem_info *uinfo)
{
@@ -534,6 +775,37 @@ static const struct snd_kcontrol_new usb_scarlett_ctl_sync = {
.get = scarlett_ctl_meter_get,
};
+/* Forte-specific control structures - use bRequest=0x03 instead of 0x01 */
+static const struct snd_kcontrol_new usb_forte_ctl_enum = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = scarlett_ctl_enum_info,
+ .get = forte_ctl_enum_get,
+ .put = forte_ctl_enum_put,
+};
+
+static const struct snd_kcontrol_new usb_forte_ctl_switch = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = snd_ctl_boolean_mono_info,
+ .get = forte_ctl_switch_get,
+ .put = forte_ctl_switch_put,
+};
+
+/* Forte input gain: 0-42 maps to approximately 0-75dB (~179 cB per step) */
+static const DECLARE_TLV_DB_SCALE(db_scale_forte_input_gain, 0, 179, 0);
+
+static const struct snd_kcontrol_new usb_forte_ctl_input_gain = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .name = "",
+ .info = forte_input_gain_info,
+ .get = forte_input_gain_get,
+ .put = forte_input_gain_put,
+ .tlv = { .p = db_scale_forte_input_gain }
+};
+
static int add_new_ctl(struct usb_mixer_interface *mixer,
const struct snd_kcontrol_new *ncontrol,
usb_mixer_elem_resume_func_t resume,
@@ -608,37 +880,83 @@ static int add_output_ctls(struct usb_mixer_interface *mixer,
if (err < 0)
return err;
- /* Add L channel source playback enumeration */
- snprintf(mx, sizeof(mx), "Master %dL (%s) Source Playback Enum",
- index + 1, name);
- err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum,
- scarlett_ctl_enum_resume, 0x33, 0x00,
- 2*index, USB_MIXER_S16, 1, mx, &info->opt_master,
- &elem);
- if (err < 0)
- return err;
+ /* Add L/R source routing if device supports it */
+ if (info->has_output_source_routing) {
+ /* Add L channel source playback enumeration */
+ snprintf(mx, sizeof(mx), "Master %dL (%s) Source Playback Enum",
+ index + 1, name);
+ err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum,
+ scarlett_ctl_enum_resume, 0x33, 0x00,
+ 2*index, USB_MIXER_S16, 1, mx, &info->opt_master,
+ &elem);
+ if (err < 0)
+ return err;
- /* Add R channel source playback enumeration */
- snprintf(mx, sizeof(mx), "Master %dR (%s) Source Playback Enum",
- index + 1, name);
- err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum,
- scarlett_ctl_enum_resume, 0x33, 0x00,
- 2*index+1, USB_MIXER_S16, 1, mx, &info->opt_master,
- &elem);
- if (err < 0)
- return err;
+ /* Add R channel source playback enumeration */
+ snprintf(mx, sizeof(mx), "Master %dR (%s) Source Playback Enum",
+ index + 1, name);
+ err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum,
+ scarlett_ctl_enum_resume, 0x33, 0x00,
+ 2*index+1, USB_MIXER_S16, 1, mx, &info->opt_master,
+ &elem);
+ if (err < 0)
+ return err;
+ }
return 0;
}
/********************** device-specific config *************************/
+static const struct scarlett_device_info forte_info = {
+ .matrix_in = 6,
+ .matrix_out = 4,
+ .input_len = 2,
+ .output_len = 4,
+ .has_output_source_routing = false,
+
+ .opt_master = {
+ .start = -1,
+ .len = 13,
+ .offsets = {0, 4, 6, 6, 6},
+ .names = NULL
+ },
+
+ .opt_matrix = {
+ .start = -1,
+ .len = 7,
+ .offsets = {0, 4, 6, 6, 6},
+ .names = NULL
+ },
+
+ .num_controls = 14,
+ .controls = {
+ { .num = 0, .type = SCARLETT_OUTPUTS, .name = "Line Out" },
+ { .num = 1, .type = SCARLETT_OUTPUTS, .name = "Headphone" },
+ /* Input 1 controls */
+ { .num = 1, .type = FORTE_INPUT_GAIN, .name = NULL},
+ { .num = 1, .type = FORTE_INPUT_SOURCE, .name = NULL},
+ { .num = 1, .type = FORTE_INPUT_HPF, .name = NULL},
+ { .num = 1, .type = FORTE_INPUT_PHANTOM, .name = NULL},
+ { .num = 1, .type = FORTE_INPUT_PHASE, .name = NULL},
+ { .num = 1, .type = FORTE_INPUT_PAD, .name = NULL},
+ /* Input 2 controls */
+ { .num = 2, .type = FORTE_INPUT_GAIN, .name = NULL},
+ { .num = 2, .type = FORTE_INPUT_SOURCE, .name = NULL},
+ { .num = 2, .type = FORTE_INPUT_HPF, .name = NULL},
+ { .num = 2, .type = FORTE_INPUT_PHANTOM, .name = NULL},
+ { .num = 2, .type = FORTE_INPUT_PHASE, .name = NULL},
+ { .num = 2, .type = FORTE_INPUT_PAD, .name = NULL},
+ },
+};
+
/* untested... */
static const struct scarlett_device_info s6i6_info = {
.matrix_in = 18,
.matrix_out = 8,
.input_len = 6,
.output_len = 6,
+ .has_output_source_routing = true,
.opt_master = {
.start = -1,
@@ -681,6 +999,7 @@ static const struct scarlett_device_info s8i6_info = {
.matrix_out = 6,
.input_len = 8,
.output_len = 6,
+ .has_output_source_routing = true,
.opt_master = {
.start = -1,
@@ -720,6 +1039,7 @@ static const struct scarlett_device_info s18i6_info = {
.matrix_out = 6,
.input_len = 18,
.output_len = 6,
+ .has_output_source_routing = true,
.opt_master = {
.start = -1,
@@ -757,6 +1077,7 @@ static const struct scarlett_device_info s18i8_info = {
.matrix_out = 8,
.input_len = 18,
.output_len = 8,
+ .has_output_source_routing = true,
.opt_master = {
.start = -1,
@@ -799,6 +1120,7 @@ static const struct scarlett_device_info s18i20_info = {
.matrix_out = 8,
.input_len = 18,
.output_len = 20,
+ .has_output_source_routing = true,
.opt_master = {
.start = -1,
@@ -906,6 +1228,61 @@ static int scarlett_controls_create_generic(struct usb_mixer_interface *mixer,
if (err < 0)
return err;
break;
+ /* Forte input controls - use wIndex 0x3c per wiki docs */
+ case FORTE_INPUT_GAIN:
+ sprintf(mx, "Line In %d Gain Capture Volume", ctl->num);
+ err = add_new_ctl(mixer, &usb_forte_ctl_input_gain,
+ forte_input_gain_resume, 0x3c,
+ 0x06, ctl->num - 1, USB_MIXER_S16, 1, mx,
+ NULL, &elem);
+ if (err < 0)
+ return err;
+ break;
+ case FORTE_INPUT_SOURCE:
+ sprintf(mx, "Input %d Source", ctl->num);
+ err = add_new_ctl(mixer, &usb_forte_ctl_enum,
+ forte_ctl_enum_resume, 0x3c,
+ 0x07, ctl->num - 1, USB_MIXER_S16, 1, mx,
+ &opt_forte_source, &elem);
+ if (err < 0)
+ return err;
+ break;
+ case FORTE_INPUT_HPF:
+ sprintf(mx, "Input %d High Pass Filter Switch", ctl->num);
+ err = add_new_ctl(mixer, &usb_forte_ctl_switch,
+ forte_ctl_switch_resume, 0x3c,
+ 0x08, ctl->num - 1, USB_MIXER_S16, 1, mx,
+ NULL, &elem);
+ if (err < 0)
+ return err;
+ break;
+ case FORTE_INPUT_PHANTOM:
+ sprintf(mx, "Input %d 48V Phantom Power Switch", ctl->num);
+ err = add_new_ctl(mixer, &usb_forte_ctl_switch,
+ forte_ctl_switch_resume, 0x3c,
+ 0x09, ctl->num - 1, USB_MIXER_S16, 1, mx,
+ NULL, &elem);
+ if (err < 0)
+ return err;
+ break;
+ case FORTE_INPUT_PHASE:
+ sprintf(mx, "Input %d Phase Invert Switch", ctl->num);
+ err = add_new_ctl(mixer, &usb_forte_ctl_switch,
+ forte_ctl_switch_resume, 0x3c,
+ 0x0a, ctl->num - 1, USB_MIXER_S16, 1, mx,
+ NULL, &elem);
+ if (err < 0)
+ return err;
+ break;
+ case FORTE_INPUT_PAD:
+ sprintf(mx, "Input %d Pad Switch", ctl->num);
+ err = add_new_ctl(mixer, &usb_forte_ctl_switch,
+ forte_ctl_switch_resume, 0x3c,
+ 0x0b, ctl->num - 1, USB_MIXER_S16, 1, mx,
+ NULL, &elem);
+ if (err < 0)
+ return err;
+ break;
}
}
@@ -1014,3 +1391,64 @@ int snd_scarlett_controls_create(struct usb_mixer_interface *mixer)
return err;
}
+
+/*
+ * Create and initialize a mixer for the Focusrite(R) Forte
+ */
+int snd_forte_controls_create(struct usb_mixer_interface *mixer)
+{
+ int err, i, o;
+ char mx[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ const struct scarlett_device_info *info;
+ struct usb_mixer_elem_info *elem;
+
+ /* only use UAC_VERSION_2 */
+ if (!mixer->protocol)
+ return 0;
+
+ switch (mixer->chip->usb_id) {
+ case USB_ID(0x1235, 0x8010):
+ info = &forte_info;
+ break;
+ default: /* device not (yet) supported */
+ return -EINVAL;
+ }
+
+ /* generic function to create controls */
+ err = scarlett_controls_create_generic(mixer, info);
+ if (err < 0)
+ return err;
+
+ /* setup matrix controls */
+ for (i = 0; i < info->matrix_in; i++) {
+ snprintf(mx, sizeof(mx), "Matrix %02d Input Playback Route",
+ i+1);
+ err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum,
+ scarlett_ctl_enum_resume, 0x32,
+ 0x06, i, USB_MIXER_S16, 1, mx,
+ &info->opt_matrix, &elem);
+ if (err < 0)
+ return err;
+
+ for (o = 0; o < info->matrix_out; o++) {
+ sprintf(mx, "Matrix %02d Mix %c Playback Volume", i+1,
+ o+'A');
+ err = add_new_ctl(mixer, &usb_scarlett_ctl,
+ scarlett_ctl_resume, 0x3c, 0x01,
+ (i << 2) + (o & 0x03), USB_MIXER_S16,
+ 1, mx, NULL, &elem);
+ if (err < 0)
+ return err;
+
+ }
+ }
+
+ /* val_len == 1 and UAC2_CS_MEM */
+ err = add_new_ctl(mixer, &usb_scarlett_ctl_sync, NULL, 0x3c, 0x00, 2,
+ USB_MIXER_U8, 1, "Sample Clock Sync Status",
+ &opt_sync, &elem);
+ if (err < 0)
+ return err;
+
+ return err;
+}
diff --git a/sound/usb/mixer_scarlett.h b/sound/usb/mixer_scarlett.h
index bbf063b79370..c44fe7d72ee3 100644
--- a/sound/usb/mixer_scarlett.h
+++ b/sound/usb/mixer_scarlett.h
@@ -3,5 +3,6 @@
#define __USB_MIXER_SCARLETT_H
int snd_scarlett_controls_create(struct usb_mixer_interface *mixer);
+int snd_forte_controls_create(struct usb_mixer_interface *mixer);
#endif /* __USB_MIXER_SCARLETT_H */
diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index bef8c9e544dd..88b7e42d159e 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -2377,18 +2377,18 @@ static int scarlett2_usb(
{
struct scarlett2_data *private = mixer->private_data;
struct usb_device *dev = mixer->chip->dev;
- struct scarlett2_usb_packet *req __free(kfree) = NULL;
- struct scarlett2_usb_packet *resp __free(kfree) = NULL;
- size_t req_buf_size = struct_size(req, data, req_size);
- size_t resp_buf_size = struct_size(resp, data, resp_size);
int retries = 0;
const int max_retries = 5;
int err;
+ struct scarlett2_usb_packet *req __free(kfree) = NULL;
+ size_t req_buf_size = struct_size(req, data, req_size);
req = kmalloc(req_buf_size, GFP_KERNEL);
if (!req)
return -ENOMEM;
+ struct scarlett2_usb_packet *resp __free(kfree) = NULL;
+ size_t resp_buf_size = struct_size(resp, data, resp_size);
resp = kmalloc(resp_buf_size, GFP_KERNEL);
if (!resp)
return -ENOMEM;
@@ -3919,9 +3919,9 @@ static int scarlett2_input_select_ctl_info(
struct scarlett2_data *private = mixer->private_data;
int inputs = private->info->gain_input_count;
int i, err;
- char **values __free(kfree) = NULL;
+ char **values __free(kfree) =
+ kcalloc(inputs, sizeof(char *), GFP_KERNEL);
- values = kcalloc(inputs, sizeof(char *), GFP_KERNEL);
if (!values)
return -ENOMEM;
@@ -9083,8 +9083,6 @@ static long scarlett2_hwdep_read(struct snd_hwdep *hw,
__le32 len;
} __packed req;
- u8 *resp __free(kfree) = NULL;
-
/* Flash segment must first be selected */
if (private->flash_write_state != SCARLETT2_FLASH_WRITE_STATE_SELECTED)
return -EINVAL;
@@ -9122,7 +9120,8 @@ static long scarlett2_hwdep_read(struct snd_hwdep *hw,
req.offset = cpu_to_le32(*offset);
req.len = cpu_to_le32(count);
- resp = kzalloc(count, GFP_KERNEL);
+ u8 *resp __free(kfree) =
+ kzalloc(count, GFP_KERNEL);
if (!resp)
return -ENOMEM;
@@ -9267,7 +9266,6 @@ static ssize_t scarlett2_devmap_read(
loff_t pos)
{
struct usb_mixer_interface *mixer = entry->private_data;
- u8 *resp_buf __free(kfree) = NULL;
const size_t block_size = SCARLETT2_DEVMAP_BLOCK_SIZE;
size_t copied = 0;
@@ -9277,7 +9275,8 @@ static ssize_t scarlett2_devmap_read(
if (pos + count > entry->size)
count = entry->size - pos;
- resp_buf = kmalloc(block_size, GFP_KERNEL);
+ u8 *resp_buf __free(kfree) =
+ kmalloc(block_size, GFP_KERNEL);
if (!resp_buf)
return -ENOMEM;
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 2d9f28558874..86c329632e39 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -555,7 +555,6 @@ int snd_usb_create_quirk(struct snd_usb_audio *chip,
static int snd_usb_extigy_boot_quirk(struct usb_device *dev, struct usb_interface *intf)
{
struct usb_host_config *config = dev->actconfig;
- struct usb_device_descriptor *new_device_descriptor __free(kfree) = NULL;
int err;
if (le16_to_cpu(get_cfg_desc(config)->wTotalLength) == EXTIGY_FIRMWARE_SIZE_OLD ||
@@ -566,8 +565,8 @@ static int snd_usb_extigy_boot_quirk(struct usb_device *dev, struct usb_interfac
0x10, 0x43, 0x0001, 0x000a, NULL, 0);
if (err < 0)
dev_dbg(&dev->dev, "error sending boot message: %d\n", err);
-
- new_device_descriptor = kmalloc(sizeof(*new_device_descriptor), GFP_KERNEL);
+ struct usb_device_descriptor *new_device_descriptor __free(kfree) =
+ kmalloc(sizeof(*new_device_descriptor), GFP_KERNEL);
if (!new_device_descriptor)
return -ENOMEM;
err = usb_get_descriptor(dev, USB_DT_DEVICE, 0,
@@ -910,7 +909,6 @@ static void mbox2_setup_48_24_magic(struct usb_device *dev)
static int snd_usb_mbox2_boot_quirk(struct usb_device *dev)
{
struct usb_host_config *config = dev->actconfig;
- struct usb_device_descriptor *new_device_descriptor __free(kfree) = NULL;
int err;
u8 bootresponse[0x12];
int fwsize;
@@ -945,7 +943,8 @@ static int snd_usb_mbox2_boot_quirk(struct usb_device *dev)
dev_dbg(&dev->dev, "device initialised!\n");
- new_device_descriptor = kmalloc(sizeof(*new_device_descriptor), GFP_KERNEL);
+ struct usb_device_descriptor *new_device_descriptor __free(kfree) =
+ kmalloc(sizeof(*new_device_descriptor), GFP_KERNEL);
if (!new_device_descriptor)
return -ENOMEM;
@@ -1267,7 +1266,6 @@ static void mbox3_setup_defaults(struct usb_device *dev)
static int snd_usb_mbox3_boot_quirk(struct usb_device *dev)
{
struct usb_host_config *config = dev->actconfig;
- struct usb_device_descriptor *new_device_descriptor __free(kfree) = NULL;
int err;
int descriptor_size;
@@ -1280,7 +1278,8 @@ static int snd_usb_mbox3_boot_quirk(struct usb_device *dev)
dev_dbg(&dev->dev, "MBOX3: device initialised!\n");
- new_device_descriptor = kmalloc(sizeof(*new_device_descriptor), GFP_KERNEL);
+ struct usb_device_descriptor *new_device_descriptor __free(kfree) =
+ kmalloc(sizeof(*new_device_descriptor), GFP_KERNEL);
if (!new_device_descriptor)
return -ENOMEM;
@@ -2147,6 +2146,8 @@ struct usb_audio_quirk_flags_table {
static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
/* Device matches */
+ DEVICE_FLG(0x001f, 0x0b21, /* AB13X USB Audio */
+ QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY),
DEVICE_FLG(0x03f0, 0x654a, /* HP 320 FHD Webcam */
QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16),
DEVICE_FLG(0x041e, 0x3000, /* Creative SB Extigy */
@@ -2236,6 +2237,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
DEVICE_FLG(0x0644, 0x806c, /* Esoteric XD */
QUIRK_FLAG_ITF_USB_DSD_DAC | QUIRK_FLAG_CTL_MSG_DELAY |
QUIRK_FLAG_IFACE_DELAY | QUIRK_FLAG_FORCE_IFACE_RESET),
+ DEVICE_FLG(0x0661, 0x0883, /* iBasso DC04 Ultra */
+ QUIRK_FLAG_DSD_RAW),
DEVICE_FLG(0x06f8, 0xb000, /* Hercules DJ Console (Windows Edition) */
QUIRK_FLAG_IGNORE_CTL_ERROR),
DEVICE_FLG(0x06f8, 0xd002, /* Hercules DJ Console (Macintosh Edition) */
diff --git a/sound/usb/usx2y/us144mkii.c b/sound/usb/usx2y/us144mkii.c
index f6572a576c15..bc71968df8e2 100644
--- a/sound/usb/usx2y/us144mkii.c
+++ b/sound/usb/usx2y/us144mkii.c
@@ -412,7 +412,6 @@ static int tascam_probe(struct usb_interface *intf,
struct snd_card *card;
struct tascam_card *tascam;
int err;
- char *handshake_buf __free(kfree) = NULL;
if (dev->speed != USB_SPEED_HIGH)
dev_info(
@@ -439,7 +438,8 @@ static int tascam_probe(struct usb_interface *intf,
return -ENOENT;
}
- handshake_buf = kmalloc(1, GFP_KERNEL);
+ char *handshake_buf __free(kfree) =
+ kmalloc(1, GFP_KERNEL);
if (!handshake_buf)
return -ENOMEM;
diff --git a/sound/usb/usx2y/us144mkii_controls.c b/sound/usb/usx2y/us144mkii_controls.c
index 5d69441ef414..62055fb8e7ba 100644
--- a/sound/usb/usx2y/us144mkii_controls.c
+++ b/sound/usb/usx2y/us144mkii_controls.c
@@ -373,7 +373,6 @@ static int tascam_samplerate_get(struct snd_kcontrol *kcontrol,
{
struct tascam_card *tascam =
(struct tascam_card *)snd_kcontrol_chip(kcontrol);
- u8 *buf __free(kfree) = NULL;
int err;
u32 rate = 0;
@@ -384,7 +383,8 @@ static int tascam_samplerate_get(struct snd_kcontrol *kcontrol,
}
}
- buf = kmalloc(3, GFP_KERNEL);
+ u8 *buf __free(kfree) =
+ kmalloc(3, GFP_KERNEL);
if (!buf)
return -ENOMEM;
diff --git a/sound/usb/usx2y/us144mkii_pcm.c b/sound/usb/usx2y/us144mkii_pcm.c
index 0c84304d4624..03dfb1f38801 100644
--- a/sound/usb/usx2y/us144mkii_pcm.c
+++ b/sound/usb/usx2y/us144mkii_pcm.c
@@ -115,7 +115,6 @@ void process_capture_routing_us144mkii(struct tascam_card *tascam,
int us144mkii_configure_device_for_rate(struct tascam_card *tascam, int rate)
{
struct usb_device *dev = tascam->dev;
- u8 *rate_payload_buf __free(kfree) = NULL;
u16 rate_vendor_wValue;
int err = 0;
const u8 *current_payload_src;
@@ -148,7 +147,8 @@ int us144mkii_configure_device_for_rate(struct tascam_card *tascam, int rate)
return -EINVAL;
}
- rate_payload_buf = kmemdup(current_payload_src, 3, GFP_KERNEL);
+ u8 *rate_payload_buf __free(kfree) =
+ kmemdup(current_payload_src, 3, GFP_KERNEL);
if (!rate_payload_buf)
return -ENOMEM;
diff --git a/sound/x86/intel_hdmi_audio.c b/sound/x86/intel_hdmi_audio.c
index 8e2a007311d3..f5807fc73c54 100644
--- a/sound/x86/intel_hdmi_audio.c
+++ b/sound/x86/intel_hdmi_audio.c
@@ -1517,13 +1517,12 @@ static void had_audio_wq(struct work_struct *work)
container_of(work, struct snd_intelhad, hdmi_audio_wq);
struct intel_hdmi_lpe_audio_pdata *pdata = ctx->dev->platform_data;
struct intel_hdmi_lpe_audio_port_pdata *ppdata = &pdata->port[ctx->port];
- int ret;
- ret = pm_runtime_resume_and_get(ctx->dev);
- if (ret < 0)
+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(ctx->dev, pm);
+ if (PM_RUNTIME_ACQUIRE_ERR(&pm))
return;
- mutex_lock(&ctx->mutex);
+ guard(mutex)(&ctx->mutex);
if (ppdata->pipe < 0) {
dev_dbg(ctx->dev, "%s: Event: HAD_NOTIFY_HOT_UNPLUG : port = %d\n",
__func__, ctx->port);
@@ -1564,9 +1563,6 @@ static void had_audio_wq(struct work_struct *work)
/* Restart the stream if necessary */
had_process_mode_change(ctx);
}
-
- mutex_unlock(&ctx->mutex);
- pm_runtime_put_autosuspend(ctx->dev);
}
/*